Nel giugno 2026 i ricercatori hanno individuato una nuova evoluzione della famiglia di malware Mini Shai-Hulud, battezzata Miasma. A differenza del classico malware npm che prende di mira una manciata di pacchetti, questa campagna ha mostrato come la compromissione di un singolo elemento dell'infrastruttura di sviluppo possa propagarsi rapidamente su più ecosistemi: npm, GitHub Actions e persino i moduli Go.
Gli attaccanti non hanno preso di mira gli utenti finali. Hanno preso di mira i publisher, le pipeline CI/CD e i meccanismi di fiducia che permettono al software di passare dal laptop di uno sviluppatore alla produzione senza che nessuno lo ricontrolli lungo il percorso. Per nulla di tutto questo esiste una CVE, perché il codice interessato non ha mai avuto alcun difetto. Il punto di ingresso è stata una credenziale rubata, non una vulnerabilità.
Una cosa va segnalata subito: il 12 maggio 2026 il gruppo originariamente dietro questa famiglia di worm, TeamPCP, ha pubblicato su GitHub il codice sorgente completo di Shai-Hulud/Mini Shai-Hulud con licenza MIT. Questo cambia il modo in cui va letta questa storia. Le quattro ondate successive condividono lo stesso toolkit e le stesse tecniche, ma strumenti condivisi non significano più un operatore condiviso. Una volta che il framework è pubblico, qualsiasi attore tecnicamente competente può condurre una campagna identica a quelle di TeamPCP. Quanto segue va letto come una sovrapposizione di tattiche, tecniche e procedure (TTP), non come un'attribuzione confermata a un singolo gruppo.
Pacchetti interessati (npm e Go)
| Pacchetto | Versione compromessa |
|---|---|
hexo-deployer-wrangler | 1.0.4 |
hexo-shoka-swiper | 0.1.10 |
leo-auth | 4.0.6 |
leo-aws | 2.0.4 |
leo-cache | 1.0.2 |
leo-cdk-lib | 0.0.2 |
leo-cli | 3.0.3 |
leo-config | 1.1.1 |
leo-connector-elasticsearch | 2.0.6 |
leo-connector-mongo | 3.0.8 |
leo-connector-mysql | 3.0.3 |
leo-connector-oracle | 2.0.1 |
leo-connector-redshift | 3.0.6 |
leo-cron | 2.0.2 |
leo-logger | 1.0.8 |
leo-sdk | 6.0.19 |
leo-streams | 2.0.1 |
prism-silq | 1.0.1 |
rstreams-metrics | 2.0.2 |
rstreams-shard-util | 1.0.1 |
serverless-convention | 2.0.4 |
serverless-leo | 3.0.14 |
solo-nav | 1.0.1 |
github.com/verana-labs/verana-blockchain (Go) | v0.10.1-dev.20 |
Socket sta proseguendo l'indagine; i nuovi risultati vengono aggiunti alla sua pagina di tracciamento di Miasma.
Se il vostro albero delle dipendenze include uno di questi pacchetti nelle versioni indicate (o le ondate compromesse precedenti della stessa campagna, di inizio giugno), trattate l'ambiente come potenzialmente compromesso e seguite i passi di remediation immediata descritti più avanti.
Cronologia
24 giugno 2026
↓
Account del maintainer di LeoPlatform compromesso
↓
23 versioni malevole di pacchetti pubblicate (a pochi secondi di distanza)
↓
Il payload in binding.gyp viene eseguito all'installazione
↓
Credenziali dello sviluppatore e della CI/CD rubate
↓
Workflow GitHub Actions ("Run Copilot") distribuito
↓
Segreti rubati esfiltrati verso repository GitHub pubblici
↓
Modulo Go (Verana Blockchain) compromesso con una tecnica correlata
↓
I ricercatori di Socket, Wiz, Sonatype e altri identificano e rendono pubblica la campagna
È la quarta ondata della stessa famiglia di campagne in meno di un mese: è emersa per la prima volta contro il namespace npm di Red Hat il 1° giugno, poi con un'ondata di 57 pacchetti il 3 giugno, infine con questa ondata LeoPlatform/Go il 24 e 25 giugno. Come già detto, TeamPCP ha reso open source il framework del worm sottostante il 12 maggio, quindi «stessa famiglia di campagne» indica TTP condivise tra le ondate, non la prova confermata di un unico operatore dietro tutte e quattro.
Cosa è successo davvero
- A un maintainer dell'ecosistema npm LeoPlatform sono state rubate le credenziali dell'account o il token npm, molto probabilmente da un infostealer.
- L'attaccante ha usato quell'accesso rubato per pubblicare versioni trojanizzate di pacchetti che il maintainer già possedeva legittimamente. Non è stato necessario «hackerare» nulla nel senso tradizionale, perché l'attaccante disponeva già di un'identità di pubblicazione fidata.
- Chiunque abbia installato una di quelle versioni ha eseguito un payload nascosto che ha rubato a sua volta le sue credenziali.
- Alcune di quelle credenziali appartenevano ad ambienti GitHub Actions, cosa che ha permesso all'attaccante di piazzare un workflow malevolo e sottrarre i segreti della CI/CD direttamente dal runner.
- Separatamente, un modulo Go è stato compromesso con una tecnica correlata ma distinta, a dimostrazione che la campagna non è legata a un singolo linguaggio o package manager.
- Il tutto è stato scoperto perché diversi team di ricerca indipendenti stavano monitorando esattamente questo schema, già osservato nelle ondate precedenti della stessa campagna.
Analisi tecnica
Fase 1: maintainer compromesso
L'account npm czirker, associato a LeoPlatform, è stato compromesso, molto probabilmente tramite credenziali trapelate o un token sottratto da un infostealer già in esecuzione sulla macchina del maintainer.
Fase 2: pubblicazione dei pacchetti malevoli
Con il token rubato, l'attaccante ha pubblicato 23 versioni malevole di pacchetti negli ecosistemi LeoPlatform e RStreams in una finestra di sei secondi. Una velocità del genere indica uno strumento di pubblicazione automatizzato e scriptato, non una persona che carica file a mano.
Il dettaglio più concreto dell'intera campagna si trova in questa fase: l'attaccante non si è limitato a pubblicare versioni malevole, ha anche spostato il dist-tag latest di leo-sdk sulla 6.0.19 avvelenata, mentre la release pulita 7.1.21 restava disponibile solo sotto il tag awsv3, molto meno visibile. In pratica, qualsiasi ambiente che eseguisse npm install leo-sdk senza un pin semver esplicito veniva attivamente indirizzato verso il worm, nonostante una release pulita esistesse per tutto il tempo. La lezione pratica è brutale: fissare la versione, e non il tag, è stata la vera differenza tra un'installazione pulita e una compromissione.
Fase 3: esecuzione al momento dell'installazione
Qui sta la parte ingegnosa. La maggior parte dei pacchetti npm malevoli viene intercettata perché gli scanner controllano i campi preinstall e postinstall in package.json. Miasma li evita del tutto e aggiunge invece un file binding.gyp, la configurazione che Node.js usa per compilare i moduli nativi durante l'installazione. Poiché binding.gyp è una componente del tutto normale nella build degli add-on nativi, esegue codice durante npm install senza mai toccare i campi che la maggior parte degli strumenti sta osservando.
Contro questa tecnica esistevano due mitigazioni reali, e vale la pena essere precisi su entrambe. La prima esisteva già: npm install --ignore-scripts sopprime effettivamente l'hook implicito node-gyp rebuild attivato da un file binding.gyp, secondo la stessa documentazione di npm. Da sola non è una difesa completa, perché un pacchetto malevolo può comunque essere scaricato e scompattato, e una rebuild successiva senza quel flag può comunque attivarlo, ma blocca questo specifico percorso di esecuzione. La seconda mitigazione è arrivata con tre settimane di ritardo: npm v12, rilasciato a luglio 2026, chiude questo vettore per impostazione predefinita, trattando il node-gyp rebuild implicito attivato da binding.gyp esattamente come qualsiasi altro install script, bloccato a meno che il pacchetto non sia esplicitamente in allowlist. La tecnica «Phantom Gyp» di Miasma è stata, di fatto, uno sfruttamento diretto della falla esatta che npm v12 era stato progettato per chiudere.
Fase 4: raccolta dei segreti
Una volta in esecuzione, il payload raccoglie un ampio insieme di credenziali, tra cui:
- token npm e PyPI
- GitHub Personal Access Token e token OIDC di GitHub
- credenziali AWS, Azure e GCP
- chiavi SSH
- percorsi di configurazione e credenziali di Claude Code, Cursor e altri assistenti di coding basati su AI
- chiavi API di OpenAI
- token Slack e Twilio, file di autenticazione Docker, configurazioni Kubernetes, dati di Vault e cronologia della shell
Fase 5: abuso di GitHub
Il malware rilascia un workflow GitHub Actions (osservato con il nome «Run Copilot») che estrae i segreti direttamente dalla memoria del runner CI durante l'esecuzione. I dati rubati vengono cifrati e inviati a repository GitHub pubblici appena creati che fungono da dead drop; al momento in cui scriviamo, oltre 550 repository portano il marcatore di esfiltrazione della campagna. In un incidente correlato dello stesso giorno, un attaccante ha eseguito un force-push di un commit malevolo su una GitHub Action molto diffusa e ha reindirizzato i tag di versione verso di esso: da quel momento qualsiasi workflow eseguito contro quei tag ha eseguito il payload dell'attaccante direttamente all'interno del runner di GitHub Actions.
Fase 6: propagazione
Macchina dello sviluppatore compromessa
↓
Credenziali GitHub dello sviluppatore rubate
↓
Segreti della pipeline CI/CD raccolti dal runner
↓
Token npm rubato usato per pubblicare altri pacchetti malevoli
↓
Altri sviluppatori installano i pacchetti compromessi
↓
(si ripete)
Ogni compromissione riuscita diventa la base di lancio della successiva. È questo che rende Miasma una campagna auto-propagante e non un singolo incidente.
Perché questo attacco è diverso
| Vecchi attacchi alla supply chain | Miasma |
|---|---|
| Un singolo pacchetto | Un intero ecosistema |
| Furto di criptovalute | Furto di credenziali su larga scala |
| Un solo linguaggio | Più linguaggi (npm, ora anche Go) |
| Un solo maintainer | L'infrastruttura di sviluppo nel suo insieme |
| Malware statico | Auto-propagante, in evoluzione tra un'ondata e l'altra |
I precedenti incidenti di supply chain su npm erano per lo più colpi mordi e fuggi: compromettere un pacchetto, rubare chiavi di wallet o token di sessione, passare oltre. Miasma si comporta più come un worm con un modello di business. Tratta ogni infezione riuscita come carburante per l'ondata successiva, e dopo più di tre settimane e quattro ondate distinte sta dimostrabilmente ancora evolvendo.
Perché GitHub Actions è diventato un bersaglio
GitHub Actions occupa un punto di valore unico nella supply chain del software: detiene i token OIDC usati per la pubblicazione fidata e senza credenziali verso cloud provider e registry, conserva segreti CI a lunga durata e, per progettazione, esegue operazioni automatizzate e non presidiate. Compromettere un runner CI non espone solo il laptop di uno sviluppatore, espone ogni pipeline di deployment, ogni pubblicazione su registry e ogni credenziale cloud che quel workflow tocca. È un raggio d'impatto molto più ampio di qualsiasi ambiente locale di un singolo sviluppatore, ed è esattamente per questo che la campagna ha costruito strumenti dedicati per prenderlo di mira in modo specifico.
Perché l'espansione verso Go è importante
La maggior parte della copertura di questa campagna si è concentrata su npm, perché è lì che risiede la maggioranza dei pacchetti. Ma la compromissione del modulo Go è probabilmente il dettaglio più importante di tutta la storia, ed è quello che la maggior parte degli articoli salta a piè pari.
Il campione Go non usa binding.gyp. Non si appoggia affatto alla risoluzione dei moduli o ai meccanismi di build di Go. Invece, uno sviluppatore che semplicemente clona il repository o lo apre in un IDE fidato o in un assistente di coding AI può attivare il payload attraverso la sola configurazione del progetto.
Questo dice qualcosa di importante: agli attaccanti non interessano, in fondo, JavaScript, npm o i meccanismi di uno specifico package manager. Interessano gli sviluppatori, ovunque gli sviluppatori ripongano fiducia in strumenti automatizzati, che si tratti di un install script, di un passo di build o di un IDE che apre una cartella. Il linguaggio non conta. Le credenziali degli sviluppatori sì.
La lezione più ampia
Il vecchio modo di pensare:
Scansioniamo le dipendenze
↓
Applichiamo le patch alle CVE note
↓
Siamo al sicuro
La realtà di oggi:
Uno sviluppatore viene compromesso
↓
La CI/CD viene compromessa
↓
Un pacchetto legittimo viene pubblicato con codice malevolo
↓
Ogni consumatore a valle lo installa
↓
In nessuna fase è mai necessaria una CVE
La scansione delle dipendenze risponde alla domanda «questo codice ha un difetto noto?». Miasma mette in luce una domanda completamente diversa, a cui la maggior parte dei programmi di sicurezza non è affatto attrezzata per rispondere: «mi fido delle persone e dei sistemi automatizzati autorizzati a pubblicare codice nel mio ambiente, adesso, oggi?».
Opportunità di rilevamento
Più che chiedersi «come fermarlo», è utile chiedersi dove un difensore avrebbe realisticamente avuto la possibilità di notare che qualcosa non andava. È qui che la maggior parte della copertura su Miasma si ferma, quindi vale la pena approfondire.
Velocità di pubblicazione. 23 versioni di pacchetti pubblicate in una finestra di sei secondi non sono opera di un essere umano. Qualsiasi organizzazione che gestisca un proprio mirror del registry npm, o che monitori gli eventi del registry pubblico per i pacchetti da cui dipende, potrebbe segnalare automaticamente raffiche di pubblicazioni come questa. Un singolo aumento di versione è normale. Due dozzine in pochi secondi sono un segnale di per sé, indipendentemente dal contenuto effettivo del pacchetto.
Cambi di dist-tag senza un aumento di versione richiesto dagli utenti. Il dirottamento di leo-sdk ha funzionato proprio perché latest è stato spostato su un numero di versione inferiore e apparentemente più vecchio (6.0.19), mentre una release pulita più recente (7.1.21) si trovava sotto un tag diverso. Qualsiasi strumento che confronti nel tempo i dist-tag di un pacchetto, e non solo il suo elenco di versioni, avrebbe rilevato latest che puntava all'indietro nella sequenza delle versioni, un comportamento che non è quasi mai legittimo.
Artefatti di build che non corrispondono al sorgente. Il pacchetto leo-sdk compromesso è passato da un index.js di 10 KB nella versione pulita a un index.js di 5 MB in quella avvelenata, con un file binding.gyp che non esiste da nessuna parte nel repository GitHub effettivo del progetto. Confrontare ciò che viene pubblicato sul registry con ciò che è committato nel controllo di versione, pacchetto per pacchetto, avrebbe fatto emergere tutto questo immediatamente. La maggior parte delle organizzazioni non esegue mai questo confronto.
Build native inattese su pacchetti puramente JavaScript. leo-sdk è un SDK JavaScript senza alcuna necessità legittima di compilazione nativa. Un binding.gyp che compare in un pacchetto che non ne ha mai distribuito uno, o che non ha alcun motivo plausibile di averne bisogno, è di per sé un forte segnale di anomalia, ben prima che qualcuno esamini cosa fa effettivamente quel file.
Nuovi file di workflow che compaiono senza una pull request corrispondente. Un file di workflow chiamato «Run Copilot» che appare in .github/workflows/ senza una PR collegata, senza review e senza un autore che qualcuno riconosca è esattamente il tipo di modifica che la branch protection e le review obbligatorie esistono per intercettare, a patto che vengano davvero applicate in modo specifico ai file di workflow.
Traffico in uscita verso repository GitHub appena creati. Poiché l'esfiltrazione si basava su repository pubblici appena creati usati come dead drop, runner CI o macchine di sviluppatori che effettuano chiamate API in uscita per creare nuovi repository, o che eseguono push verso repository inesistenti un'ora prima, costituiscono uno schema di comportamento rilevabile e piuttosto insolito.
Uso di OIDC e token al di fuori del loro schema abituale. Un token OIDC di GitHub o un token di pubblicazione npm usato da un intervallo IP, un workflow o una finestra temporale da cui non è mai stato usato prima è un classico segnale di anomalia di identità, e non richiede alcuna conoscenza di questa specifica campagna per essere intercettato.
Nessuno di questi segnali richiede un database di CVE o una conoscenza preventiva di Miasma. Richiedono di osservare davvero come fiducia, comportamento di pubblicazione e identità si muovono nel vostro ambiente nel tempo, e non solo ciò che è elencato in un file manifest in un singolo istante.
Cosa dovrebbero fare le organizzazioni
Subito
- Ruotare le credenziali di qualsiasi ambiente che abbia installato un pacchetto interessato, ma solo dopo aver rimosso la versione malevola e confermato che non ci sono meccanismi di persistenza, non prima.
- Aggiornare o rimuovere le versioni dei pacchetti interessati in lockfile, cache, container e runner CI, non solo nel manifest di primo livello.
- Revocare e riemettere qualsiasi token GitHub, configurazione OIDC o token npm che potrebbe essere stato esposto.
- Fissare le GitHub Actions tramite hash del commit invece che con tag di versione mutabili.
Medio termine
- Introdurre allowlist di pacchetti per ciò che è consentito nelle build CI/CD e di produzione.
- Richiedere provenance e firma dei pacchetti dove disponibili.
- Mantenere un vero Software Bill of Materials (SBOM), non un'istantanea puntuale.
- Aggiungere una finestra di attesa prima che le versioni appena pubblicate di un pacchetto vengano considerate affidabili in una build.
Lungo termine
- Validazione continua di ciò che gira effettivamente nel vostro ambiente, non audit periodici.
- Monitoraggio dei repository e della CI/CD per modifiche inattese ai workflow o comportamenti di pubblicazione anomali.
- Analisi continua dei percorsi di attacco: non solo «questo pacchetto è vulnerabile?» ma «cosa potrebbe raggiungere qualcuno se questo pacchetto fosse malevolo?».
- Monitoraggio dell'identità degli sviluppatori, perché in questa campagna l'identità era la vera superficie di attacco.
Perché questo conta per Penetrify
Miasma non è stato un problema di npm. È stato un problema di identità. Un problema di CI/CD. Un problema di fiducia. Un problema di percorsi di attacco.
Gli attaccanti non hanno scoperto una vulnerabilità. Hanno scoperto un percorso dalla workstation di un singolo sviluppatore a migliaia di sistemi a valle, e quel percorso esisteva interamente all'interno di relazioni che la maggior parte dei programmi di sicurezza non monitora mai davvero: quali persone possono pubblicare codice, quali token possono avviare un deployment, quali workflow girano non presidiati con accesso reale alla produzione.
npm v12 chiude il vettore di esecuzione specifico su cui Miasma si è basato. Non fa nulla per il problema di identità che c'è sotto. Come ha detto Moshe Siman Tov Bustan di OX Security nella copertura del rilascio di npm v12: una volta che un attaccante controlla le credenziali di un maintainer legittimo, bloccare gli install script non serve, perché il codice malevolo viene distribuito come release fidata e firmata.
È l'intera tesi di questo articolo, espressa in modo più semplice di quanto avremmo saputo fare noi. La sicurezza moderna della supply chain del software deve andare oltre la scansione delle dipendenze e arrivare a comprendere in modo continuo come le relazioni di fiducia evolvono tra repository, pipeline di build, identità e ambienti di produzione, e non solo quali CVE compaiono in un file manifest.
Fonti
- The Hacker News: Miasma Malware Targets npm Packages and GitHub Actions in Supply Chain Attack
- Socket: Miasma Mini Shai-Hulud Hits LeoPlatform npm Packages and Go Ecosystem
- StepSecurity: Mass npm Supply Chain Attack: 20 Leo Platform Packages Compromised e Supply Chain Compromise: codfish/semantic-release-action
- Endor Labs: Shai-Hulud Strikes Leo Platform npm
- Corgea: Leo Platform npm packages compromised with Phantom Gyp Miasma toolkit
- Snyk: Node-gyp Supply Chain Compromise
- Datadog Security Labs: Shai-Hulud Goes Open Source
- The Register: Malware crew TeamPCP open-sources its Shai-Hulud worm on GitHub
- GitHub Changelog: Upcoming breaking changes for npm v12
- TechTimes: npm v12 Ships This Month, Blocking Install Scripts That Enabled Year of Supply Chain Attacks
