Threat Advisories

ChainDrop: l'attacco alla catena di approvvigionamento di npm tramite Keyv e Cacheable

Scritto da Integrity360 | 5 ago 2026, 13:26:00

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.

 

Panoramica delle vulnerabilità

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:

  • Credenziali cloud: chiavi di accesso AWS, valori dell’SSM Parameter Store (con WithDecryption: true), segreti di AWS Secrets Manager, credenziali di Azure e GCP
  • Segreti CI/CD: segreti di GitHub Actions, inclusa l’estrazione dei valori "isSecret":true dalla memoria dei runner self-hosted, token dei workflow di GitHub
  • Token degli sviluppatori: token di accesso personali di GitHub (prefissi ghp_, gho_, ghs_), token di pubblicazione npm (prefisso npm_), ogni token convalidato in tempo reale su registry.npmjs.org/-/whoami prima dell’esfiltrazione
  • Segreti di infrastruttura: segreti Kubernetes da tutti gli spazi dei nomi accessibili, token HashiCorp Vault e segreti KV, credenziali Terraform, chiavi SSH, stringhe di connessione ai database
  • Configurazione di IA e IDE: file di configurazione dell’API Claude, credenziali dei servizi relativi all’IA
  • Portafogli di criptovalute: file e chiavi dei portafogli locali
  • Credenziali di servizio: Stripe, Slack, Twilio e altre chiavi API di terze parti
  • Ambiente di processo completo: tutte le variabili d’ambiente del processo compromesso

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:

  • Repository GitHub pubblici creati con identità compromesse, recanti la descrizione «Shai-Hulud: Here We Go Again»
  • Il dominio npm-cache[.]com

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:

  • Verifica delle dipendenze: eseguire la scansione di tutti i file package-lock.json, yarn.lock, pnpm-lock.yaml e bun.lockb alla ricerca dei pacchetti e delle versioni interessati. Non fare affidamento sui tag attuali del registro: utilizzare esclusivamente le versioni risolte dai file di blocco.
  • Indicatori del filesystem: cercare sulle workstation di sviluppo, sui runner CI/CD e sui server di build la presenza di Math_Symbol.js, math_init.js e setup.mjs all’interno delle directory node_modules e delle directory temporanee corrispondenti a /tmp/bun-dl-*/.
  • Monitoraggio della rete: generare avvisi in caso di connessioni in uscita e query DNS verso npm-cache[.]com, pypi-get[.]com, js-mirror[.]com e gli endpoint RPC di Ethereum elencati negli IOC sopra riportati. Monitorare la presenza della stringa user-agent "Bun/1.3.13".
  • Verifica su GitHub: esaminare le organizzazioni alla ricerca di repository pubblici inattesi con la descrizione «Shai-Hulud: Here We Go Again» o di commit contenenti la stringa intimidatoria.
  • Monitoraggio degli script di preinstallazione: eseguire un audit dei file package.json in tutti i progetti alla ricerca di voci di script di preinstallazione inattese che facciano riferimento a setup.mjs.
  • Affidabilità dell’area di lavoro dell’IDE: verificare la presenza di file .claude/settings.json e configurazioni .vscode/ non autorizzati che potrebbero caricare payload esterni.

 

Misure di mitigazione e correzione

Azioni immediate

  • Identificare e rimuovere le versioni compromesse: eseguire un audit su tutti gli ambienti e rimuovere qualsiasi installazione delle versioni dannose del pacchetto. Eseguire l’aggiornamento a versioni pulite e verificate.
  • Rimuovere prima il watcher di revoca delle credenziali: non ruotare i token prima di aver rimosso il malware. Il payload installa un watcher che si attiva in caso di revoca del token. La rotazione prematura delle credenziali può eseguire un handler fornito dall’autore dell’attacco. Eliminare il malware dai sistemi interessati prima di procedere alla rotazione delle credenziali.
  • Partire dal presupposto che le credenziali siano state completamente compromesse: se è stata installata una versione compromessa, considerare rubate tutte le credenziali accessibili in quell’ambiente. Ciò include credenziali cloud, token npm, PAT di GitHub, chiavi SSH, segreti CI/CD, credenziali di database e chiavi API di terze parti.
  • Eseguire la rotazione di tutte le credenziali esposte: dopo aver rimosso il malware, effettuare la rotazione di: tutti i token di pubblicazione npm, i token di accesso personale (PAT) e i token OAuth di GitHub, le chiavi di accesso AWS IAM, le credenziali dei soggetti di servizio di Azure e GCP, i token degli account di servizio Kubernetes, i token HashiCorp Vault, le password dei database, le chiavi SSH e le chiavi API di terze parti (Stripe, Slack, Twilio, ecc.).
  • Revoca e riemissione dei token npm/GitHub: utilizza le console di amministrazione di npm e GitHub per revocare forzatamente tutti i token potenzialmente esposti. Genera nuovi token con gli ambiti minimi richiesti.

Ripristino del sistema

  • Ricostruire i sistemi interessati: considerare potenzialmente compromessa qualsiasi workstation di sviluppo, runner CI/CD o server di build su cui sia stato installato un pacchetto dannoso. Ricostruire il sistema a partire da un’immagine o uno snapshot di cui si conosce la pulizia.
  • Svuotare le cache locali: svuotare la cache di npm (npm cache clean --force), la cache di Yarn, l’archivio di pnpm e la cache di Bun per eliminare i pacchetti dannosi memorizzati nella cache.
  • Eliminare e ricreare gli ambienti virtuali: non limitarsi a eseguire il downgrade dei pacchetti interessati. Eliminare le directory `node_modules` e ricreare interamente gli ambienti virtuali Python.

Revisione del cloud e del codice sorgente

  • Eseguire l’audit degli ambienti cloud: esaminare AWS CloudTrail, i registri delle attività di Azure e i registri di audit di GCP alla ricerca di chiamate API non autorizzate effettuate nel periodo in cui è avvenuta l’installazione del pacchetto. Verificare la creazione inattesa di utenti o ruoli IAM, l’accesso a SSM Parameter Store con decrittografia e l’accesso a Secrets Manager.
  • Esaminare i log di audit di GitHub: esaminare i log di audit dell’organizzazione per individuare la creazione inattesa di repository, la generazione di token e le modifiche al flusso di lavoro.
  • Cerca i repository di esfiltrazione: cerca i repository pubblici creati con le identità della tua organizzazione con la descrizione "Shai-Hulud: Here We Go Again".

Rafforzamento della catena di fornitura

  • Fissare le dipendenze: utilizzare il pinning della versione esatta in tutti i file package.json e sottoporre i file di blocco a commit nel sistema di controllo delle versioni.
  • Eseguire l’aggiornamento a npm 12: npm 12 blocca per impostazione predefinita gli script del ciclo di vita delle dipendenze non approvati, impedendo l’esecuzione automatica degli script di preinstallazione. I client npm precedenti rimangono vulnerabili.
  • Applicare SLSA e OIDC: richiedere attestati di provenienza SLSA e verificarli in fase di compilazione. Tuttavia, si noti che questa campagna ha dimostrato che la provenienza da sola è insufficiente quando l’autore dell’attacco controlla la pipeline di compilazione: la provenienza deve essere combinata con la verifica dell’identità.
  • Abilitare l’elenco delle dipendenze consentite: limitare le fonti e le versioni dei pacchetti consentiti tramite strumenti come Socket, Semgrep Supply Chain o le politiche del proprio registro degli artefatti.
  • Implementare la verifica dell’integrità dei pacchetti: utilizzare le firme di audit di npm e verificare gli hash di integrità del registro, ove supportati.
  • Abilitare l’autenticazione a più fattori (MFA) su tutti gli account dei manutentori: richiedere l’autenticazione a più fattori supportata da dispositivo hardware per tutti gli account npm e GitHub con permessi di pubblicazione.
  • Bloccare i domini dannosi: aggiungere gli IOC sopra elencati alle liste di blocco del perimetro di rete e alle soluzioni di filtraggio DNS.

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.