Il 4 agosto 2026, un sofisticato attacco alla catena di approvvigionamento del software, denominato ChainDrop, ha colpito l’ecosistema npm attraverso la compromissione dell’account GitHub di un manutentore dei pacchetti open source Keyv e Cacheable, ampiamente utilizzati . Il worm autopropagante che ne è derivato si è diffuso in oltre 2.251 versioni di 452 pacchetti distinti, con circa 2 miliardi di download mensili, colpendo organizzazioni quali Deliveroo, Ornikar, OneReach, Picsart, Qlik e ServiceTitan.
Keyv è un semplice archivio chiave-valore per Node.js con supporto per più backend, mentre Cacheable è il framework di cache HTTP sottostante. Insieme, questi pacchetti costituiscono l’infrastruttura di caching fondamentale nell’ecosistema JavaScript. Entrambi i pacchetti sono gestiti dallo stesso sviluppatore, il cui account GitHub è stato compromesso dall’autore dell’attacco.
L’autore dell’attacco ha pubblicato la prima versione dannosa, keyv@6.0.0, alle 9:00 UTC circa del 4 agosto 2026. Il malware discende dalla famiglia “Mini” Shai-Hulud; l’attribuzione a TeamPCP rimane non confermata. Il worm si è propagato rapidamente raccogliendo token npm e GitHub da ambienti compromessi e utilizzandoli per pubblicare ulteriori versioni infette. Socket ha verificato 2251 versioni infette distribuite su 452 pacchetti.
Il payload ruba una vasta gamma di dati sensibili, tra cui credenziali cloud, segreti CI/CD, token degli sviluppatori, file di configurazione dell’IA e portafogli di criptovalute. Inoltre, inserisce hook in Claude Code e Visual Studio Code per garantire un’esecuzione persistente basata sull’IDE. Al momento della stesura di questo articolo, a questa campagna non sono stati assegnati identificatori CVE.
Accesso iniziale
L’autore della minaccia ha compromesso l’account GitHub del manutentore di Keyv, ottenendo l’accesso diretto in modalità push ai repository sorgente di Keyv, Cacheable, cache-manager, flat-cache, file-entry-cache e numerosi pacchetti correlati all’interno del monorepo del manutentore.
Pubblicazione dannosa
A partire dalle 9:00 UTC, l’autore dell’attacco ha introdotto payload di persistenza IDE nel repository di Keyv, per poi pubblicare keyv@6.0.0 e le successive versioni dannose. Poiché i pacchetti sono stati compilati e pubblicati tramite i legittimi flussi di lavoro di GitHub Actions, le versioni compromesse riportavano informazioni di provenienza SLSA valide, aggirando i controlli di verifica dell’integrità. Ciascun pacchetto compromesso conteneva una voce "preinstall": "node setup.mjs" nel proprio file package.json, causando l’esecuzione automatica del dropper prima che qualsiasi installazione npm fosse completata.
Propagazione del worm
La capacità di auto-diffusione del malware gli ha permesso di infettare pacchetti appartenenti ad altri manutentori i cui progetti dipendevano da un pacchetto precedentemente compromesso. Raccogliendo token di pubblicazione npm validi dai runner CI/CD e dalle workstation degli sviluppatori, il worm ha ripubblicato i pacchetti a valle con il payload dannoso iniettato, creando una catena a cascata di compromissioni. Questo meccanismo ha permesso all’attacco di sfuggire allo spazio dei nomi Keyv/Cacheable originale e di propagarsi oltre i confini organizzativi.
Pacchetti colpiti
I pacchetti principali compromessi includevano:
|
Nome del pacchetto |
Versioni compromesse |
Versione verificata come sicura / ripristinata |
|
keyv |
6.0.0 (e successive iterazioni dannose) |
5.6.0 |
|
flat-cache |
6.1.24 |
6.1.23 |
|
cache-voci-file |
11.1.6 |
11.1.5 (o versione stabile precedente) |
|
cacheable-request |
13.0.20 |
13.0.19 (o versione stabile precedente) |
|
cache-manager |
7.2.10 |
7.2.9 |
|
cacheable |
2.5.1 |
2.5.0 (o versione stabile precedente) |
|
@cacheable/utils |
2.5.1 |
2.5.0 (o versione stabile precedente) |
|
@cacheable/memory |
2.2.1 |
2.2.0 (o versione stabile precedente) |
|
@cacheable/node-cache |
3.1.2 |
3.1.1 (o versione stabile precedente) |
|
@cacheable/net |
2.1.1 |
2.1.0 (o versione stabile precedente) |
|
ecto |
5.0.1 |
5.0.0 (o versione stabile precedente) |
Il worm si è successivamente diffuso nei pacchetti di organizzazioni quali Ornikar (tra cui @ornikar/eslint-config-*, @ornikar/prettier-config, @ornikar/babel-preset-* e molti altri), Qlik (@nebula.js/nucleus), HubSync (@hubsync/web-sdk-react) e numerosi altri. Un elenco completo e costantemente aggiornato è gestito da Wiz Research sul proprio repository GitHub pubblico.
Importante: il registro npm ha subito rapidi cambiamenti durante l’incidente. Alle 17:40 IST del 4 agosto, le versioni precedenti non compromesse sono state ripristinate come versioni più recenti per almeno nove pacchetti fondamentali, tra cui keyv@5.6.0, flat-cache@6.1.23 e cache-manager@7.2.9. Le organizzazioni devono verificare l’esposizione utilizzando le versioni esatte dei pacchetti risolte nei file di blocco (lockfile), non i tag attuali del registro.
Analisi del malware
Panoramica del payload
Il malware si distribuisce secondo un'architettura a due fasi:
Fase 1 - setup.mjs (dropper): questo script Node.js leggero funge da payload iniziale. Verifica la presenza del runtime JavaScript Bun e, in caso di assenza, scarica Bun v1.3.13 dalle versioni ufficiali su GitHub. Successivamente, passa l’esecuzione al bundle principale dell’infostealer prima di eliminare la directory temporanea del runtime per ridurre le tracce forensi.
Fase 2 - Math_Symbol.js / math_init.js (infostealer): un bundle compilato di 727.680 byte, fortemente offuscato, eseguito tramite il runtime Bun. Si tratta del motore principale per la raccolta delle credenziali. Entrambi i nomi dei file (Math_Symbol.js e math_init.js) sono stati rilevati in circolazione e contengono funzionalità identiche con lo stesso hash SHA-1.
Obiettivi della raccolta delle credenziali
L’infostealer enumera e sottrae sistematicamente:
Meccanismi di persistenza
Il repository Keyv conservava hook separati per Claude Code (.claude/settings.json) e Visual Studio Code (.vscode/). Questi meccanismi di persistenza a livello di IDE possono eseguire il payload una volta che un utente si fida dell’area di lavoro o autorizza la configurazione del progetto, fornendo un percorso di esecuzione secondario indipendente dagli script del ciclo di vita di npm install.
Il malware installa inoltre un monitor per la revoca delle credenziali — una trappola che attiva un gestore locale fornito dall’autore dell’attacco quando i token vengono sostituiti. Chi deve intervenire deve rimuovere questo monitor prima di sostituire qualsiasi credenziale.
Comando e controllo
Il malware recupera i propri domini C2 da uno smart contract di Ethereum (StringListStore) utilizzando un eth_call anziché incorporarli nel payload. Questa risoluzione C2 basata su blockchain consente all’operatore di aggiornare l’infrastruttura senza modificare il malware. La cronologia on-chain mostra che il contratto era inizialmente configurato con tre domini prima di essere aggiornato per restituire solo npm-cache[.]com. Il proprietario del contratto è stato finanziato da un indirizzo precedentemente segnalato per attività fraudolente.
Esfiltrazione
I dati rubati vengono crittografati utilizzando uno schema ibrido AES-256 e RSA-4096, quindi esfiltrati attraverso due canali:
I commit iniziali nei repository di esfiltrazione contengono la stringa intimidatoria: «IfYouBlockThisAPIKeyItWillCrashTheLiveProductionServersOfAllThirdPartyClients.»
Indicatori di compromissione (IoC) noti
|
Categoria |
Indicatore |
Descrizione |
|
Dominio |
npm-cache[.]com |
Dominio C2 principale e di esfiltrazione dei dati; servito tramite Cloudflare |
|
Dominio |
registry[.]npmjs[.]org |
Endpoint di convalida dei token utilizzato in modo improprio per la verifica delle credenziali in tempo reale prima dell’esfiltrazione |
|
Dominio |
eth-mainnet[.]nodereal[.]io |
Endpoint RPC di Ethereum utilizzato per il recupero del dominio C2 basato su smart contract |
|
Dominio |
go[.]getblock[.]io |
Endpoint RPC di Ethereum utilizzato per il recupero del dominio C2 basato su smart contract |
|
Dominio |
eth[.]llamarpc[.]com |
Endpoint RPC di Ethereum utilizzato per il recupero del dominio C2 basato su smart contract |
|
Dominio |
pypi-get[.]com |
Infrastruttura aggiuntiva controllata dall'autore dell'attacco |
|
Dominio |
js-mirror[.]com |
Infrastruttura aggiuntiva controllata dagli autori dell'attacco |
|
IPv4 |
104[.]21[.]35[.]216 |
IP Cloudflare associato a npm-cache[.]com |
|
IPv4 |
172[.]67[.]167[.]200 |
IP Cloudflare associato a eth[.]llamarpc[.]com |
|
IPv4 |
185[.]44[.]207[.]215 |
IP associato a go[.]getblock[.]io ETH RPC |
|
IPv4 |
35[.]175[.]164[.]77 |
IP AWS associato a eth-mainnet[.]nodereal[.]io |
|
Nome file |
Math_Symbol.js |
Payload di un infostealer offuscato (727.680 byte); distribuito tramite script di preinstallazione del pacchetto npm |
|
Nome file |
math_init.js |
Variante del payload dell’infostealer con funzionalità identiche |
|
Nome file |
setup.mjs |
Dropper di fase 1; scarica il runtime di Bun e avvia l'esecuzione dell'infostealer |
|
Hash del file (SHA-1) |
35a672cf34b996b91f3e1c28cbf3a05a37e036e4 |
Math_Symbol.js / math_init.js: payload dell’infostealer |
|
Hash del file (SHA-1) |
686aa40d0fc22c8d569494543a0f891f359f2f99 |
setup.mjs situato nella directory .claude (hook Claude Code) |
|
Hash del file (SHA-1) |
f525d52ceb966516686b482d3dc0137028cc6a63 |
setup.mjs situato nella directory .vscode (hook VS Code) |
|
User-Agent |
Bun/1.3.13 |
Stringa user-agent HTTP utilizzata durante il download in fase di esecuzione di Bun e le comunicazioni C2 |
|
Percorso nel filesystem |
/tmp/bun-dl-*/ |
Directory temporanea utilizzata per il download del runtime di Bun e la fase di preparazione |
|
Percorso nel filesystem |
node_modules/keyv/Math_Symbol.js |
Posizione del payload dell'infostealer all'interno dell'albero del pacchetto installato |
|
Stringa |
Shai-Hulud: Ci risiamo |
Descrizione del repository GitHub utilizzato per i repository di esfiltrazione |
|
Stringa |
Se blocchi questa chiave API, i server di produzione live di tutti i client di terze parti andranno in crash |
Stringa intimidatoria nei commit iniziali di esfiltrazione |
Rilevamenti e ricerca delle minacce
I team di sicurezza dovrebbero implementare le seguenti misure di rilevamento:
Misure di mitigazione e correzione
Azioni immediate
Ripristino del sistema
Revisione del cloud e del codice sorgente
Rafforzamento della catena di fornitura
Se siete preoccupati per una qualsiasi delle minacce descritte in questo bollettino o avete bisogno di aiuto per determinare quali misure adottare per proteggervi dalle minacce più rilevanti che la vostra organizzazione deve affrontare, contattate il vostro account manager o, in alternativa, mettetevi in contatto con noi per scoprire come proteggere la vostra organizzazione.