Secondo le ricostruzioni dei ricercatori, intorno alle 09:00 UTC del 4 agosto un aggressore ha ottenuto il controllo dell’account GitHub di Jared Wray . Da lì ha inserito codice malevolo direttamente nel branch main del repository keyv e ha pubblicato nuove release per l’intera famiglia keyv e cacheable .
La prima ondata comprendeva 11 pacchetti appartenenti ai due namespace, tra cui keyv, cacheable-request, cache-manager, @cacheable/utils, flat-cache e file-entry-cache . Le analisi indipendenti di Aikido Security, StepSecurity, Socket e Chainguard hanno confermato la compromissione nelle prime ore dell’attacco .
Ogni pacchetto contaminato presentava uno schema ricorrente:
setup.mjs e Math_Symbol.js;package.json con l’hook "preinstall": "node setup.mjs" .L’hook preinstall viene eseguito automaticamente durante npm install, prima che l’installazione sia completata. In questo caso setup.mjs funzionava da dropper: scaricava da GitHub Releases una versione legittima del runtime JavaScript Bun e la utilizzava per avviare il secondo stadio, Math_Symbol.js, un file JavaScript fortemente offuscato di circa 710–728 KB .
Microsoft Threat Intelligence ha classificato il payload come una variante di Mini Shai-Hulud . Il malware cercava numerosi tipi di segreti presenti sulle workstation degli sviluppatori e negli ambienti di build :
Il rischio riguardava quindi non solo il computer dello sviluppatore, ma anche pipeline CI/CD, account cloud, repository e servizi di terze parti raggiungibili dall’ambiente infetto.
L’elemento più pericoloso dell’operazione era la capacità del worm di replicarsi. Dopo aver sottratto token di pubblicazione npm e GitHub PAT dall’ambiente compromesso, il malware li utilizzava per pubblicare versioni infette di altri pacchetti appartenenti a maintainer differenti .
In questo modo l’attacco ha superato rapidamente i namespace originali keyv e cacheable, raggiungendo pacchetti associati anche a organizzazioni come Deliveroo, Ornikar, OneReach, Picsart, Qlik e ServiceTitan .
La progressione osservata dai ricercatori mostra la velocità dell’espansione:
Il fatto che l’infezione viaggiasse attraverso credenziali legittime rendeva l’attacco particolarmente difficile da contenere: ogni nuovo ambiente infetto poteva diventare un ulteriore punto di propagazione.
I dati sottratti venivano inviati a un repository GitHub controllato dall’aggressore. Il worm poteva creare un repository dedicato all’esfiltrazione oppure utilizzare un repository già predisposto per raccogliere i segreti .
Il payload includeva inoltre più canali ridondanti di esfiltrazione . Questa scelta aumentava la resilienza dell’operazione: la rimozione o il blocco di un singolo canale non era necessariamente sufficiente a interrompere la fuga dei dati.
I ricercatori hanno invitato a considerare completamente compromesso qualsiasi sistema sul quale sia stato eseguito npm install con una versione infetta. Eliminare semplicemente i file malevoli non è sufficiente, perché le credenziali potrebbero essere già state copiate e utilizzate .
Individuare le versioni compromesse nei file package-lock.json, yarn.lock e pnpm-lock.yaml, includendo anche le dipendenze transitive .
Dopo aver identificato una versione pulita, fissarla esplicitamente o tornare a una release precedente verificata. Gli override di npm, Yarn o pnpm — per esempio la proprietà "overrides" nel package.json — possono impedire la reinstallazione accidentale delle versioni contaminate .
Una workstation o un runner CI/CD che abbia eseguito l’installazione di un pacchetto compromesso deve essere trattato come un ambiente esposto . Ove possibile, è preferibile ricrearlo da un’immagine pulita invece di affidarsi soltanto alla rimozione manuale dei file.
Devono essere revocati e sostituiti tutti i segreti potenzialmente accessibili dal sistema infetto , tra cui:
La rotazione dovrebbe iniziare dalle credenziali che consentono di pubblicare pacchetti o di accedere all’infrastruttura più sensibile.
Un dettaglio operativo è particolarmente importante: in alcuni casi il malware installava watcher nei workflow GitHub capaci di intercettare o riesporre un nuovo token subito dopo la sua creazione .
Per questo i ricercatori hanno raccomandato di individuare e rimuovere o disabilitare questi meccanismi di persistenza prima di procedere alla rotazione delle credenziali .
È necessario cancellare le cache npm, pnpm e Yarn sia dai computer degli sviluppatori sia dai runner CI/CD. Vanno considerate anche le cache di build Docker .
Gli artefatti e le immagini devono poi essere ricostruiti da zero, così da evitare che una dipendenza contaminata sopravviva in una cache o in un layer Docker .
Infine, i team dovrebbero verificare la presenza di repository creati di recente, workflow non autorizzati e commit sospetti . È inoltre opportuno cercare artefatti di persistenza come .claude/settings.json e .vscode/tasks.json, che il worm potrebbe aver creato .
L’attacco del 4 agosto mostra quanto rapidamente una compromissione apparentemente circoscritta possa trasformarsi in un’emergenza dell’intero ecosistema. È bastato l’accesso all’account di un singolo maintainer per infettare la sua famiglia di pacchetti e, attraverso i token sottratti, attraversare i confini dei namespace npm.
L’uso di un runtime Bun legittimo per eseguire il payload, la propagazione basata su token validi e la presenza di più canali di esfiltrazione hanno reso l’operazione più sofisticata rispetto a molte precedenti campagne contro la filiera del software .
Per sviluppatori e aziende, la lezione è concreta: fissare le versioni delle dipendenze, controllare anche le dipendenze transitive, limitare gli script preinstall e postinstall quando possibile, monitorare le attività anomale su GitHub e mantenere un piano di risposta già pronto per gli incidenti nella supply chain.