Den 4 augusti 2026 drabbades npm-ekosystemet av en sofistikerad attack mot programvarans leveranskedja, kallad ChainDrop, efter att GitHub-kontot tillhörande en underhållsansvarig för de populära open source-paketen Keyv och Cacheable hade komprometterats . Den resulterande självförökande masken spred sig till över 2 251 versioner av 452 unika paket med cirka 2 miljarder nedladdningar per månad, vilket drabbade organisationer som Deliveroo, Ornikar, OneReach, Picsart, Qlik och ServiceTitan.
Keyv är ett enkelt nyckel-värde-lager för Node.js med stöd för flera backends, medan Cacheable är dess underliggande HTTP-cache-ramverk. Tillsammans fungerar dessa paket som en grundläggande cachinginfrastruktur i hela JavaScript-ekosystemet. Båda paketen underhålls av samma utvecklare, vars GitHub-konto komprometterades av hotaktören.
Angriparen publicerade den första skadliga versionen, keyv@6.0.0, omkring kl. 09:00 UTC den 4 augusti 2026. Skadlig programvaran är en avkomma till ”Mini”-familjen Shai-Hulud, men tillskrivningen till TeamPCP är fortfarande obekräftad. Masken spred sig snabbt genom att samla in npm- och GitHub-token från komprometterade miljöer och använda dem för att publicera ytterligare förgiftade versioner. Socket verifierade 2 251 förgiftade versioner i 452 paket.
Nyttolasten stjäl ett omfattande urval av känslig data, inklusive inloggningsuppgifter för molntjänster, CI/CD-hemligheter, utvecklartoken, AI-konfigurationsfiler och kryptovalutaplånböcker. Den planterar även Claude Code- och Visual Studio Code-hooks för kontinuerlig IDE-baserad körning. Inga CVE-identifierare har tilldelats denna kampanj vid tidpunkten för skrivandet.
Initial åtkomst
Hotaktören komprometterade GitHub-kontot för Keyv-underhållaren, vilket gav direkt push-åtkomst till källkodsrepositorierna för Keyv, Cacheable, cache-manager, flat-cache, file-entry-cache och ett stort antal relaterade paket inom underhållarens monorepo.
Skadlig publicering
Från och med kl. 09:00 UTC införde angriparen IDE-persistenspayloads i Keyv-repositoriet och publicerade därefter keyv@6.0.0 samt efterföljande skadliga versioner. Eftersom paketen byggdes och publicerades via deras legitima GitHub Actions-arbetsflöden innehöll de komprometterade utgåvorna giltig SLSA-ursprungsinformation, vilket gjorde att integritetsverifieringskontrollerna kunde kringgås. Varje förgiftat paket innehöll en post ”preinstall”: ”node setup.mjs” i sin package.json, vilket ledde till att dropparen kördes automatiskt innan någon npm-installation hade slutförts.
Spridning av masken
Malwarens förmåga att sprida sig själv gjorde det möjligt för den att infektera paket som tillhörde andra underhållare vars projekt var beroende av ett tidigare komprometterat paket. Genom att samla in giltiga npm-publiceringstoken från CI/CD-körare och utvecklares arbetsstationer publicerade masken nedströms-paket på nytt med den skadliga nyttolasten injicerad, vilket skapade en kaskadkedja av komprometteringar. Denna mekanism gjorde det möjligt för attacken att undkomma det ursprungliga Keyv/Cacheable-namnområdet och sprida sig över organisationsgränser.
Drabbade paket
De viktigaste komprometterade paketen var:
|
Paketnamn |
Skadliga drabbade versioner |
Verifierat säkert / Återställd ren version |
|
keyv |
6.0.0 (och efterföljande skadliga versioner) |
5.6.0 |
|
flat-cache |
6.1.24 |
6.1.23 |
|
fil-post-cache |
11.1.6 |
11.1.5 (eller tidigare stabil version) |
|
cacheable-request |
13.0.20 |
13.0.19 (eller tidigare stabil version) |
|
cache-manager |
7.2.10 |
7.2.9 |
|
cacheable |
2.5.1 |
2.5.0 (eller tidigare stabil version) |
|
@cacheable/utils |
2.5.1 |
2.5.0 (eller tidigare stabil version) |
|
@cacheable/memory |
2.2.1 |
2.2.0 (eller tidigare stabil version) |
|
@cacheable/node-cache |
3.1.2 |
3.1.1 (eller tidigare stabil version) |
|
@cacheable/net |
2.1.1 |
2.1.0 (eller tidigare stabil version) |
|
ecto |
5.0.1 |
5.0.0 (eller tidigare stabil version) |
Wormen spred sig därefter till paket inom organisationer som Ornikar (bland annat @ornikar/eslint-config-*, @ornikar/prettier-config, @ornikar/babel-preset-* och många fler), Qlik (@nebula.js/nucleus), HubSync (@hubsync/web-sdk-react) och många andra. En fullständig, kontinuerligt uppdaterad lista underhålls av Wiz Research i deras offentliga GitHub-repository.
Viktigt: npm-registret förändrades snabbt under hela incidenten. Kl. 17:40 IST den 4 augusti återställdes tidigare rena versioner som de senaste för minst nio kärnpaket, däribland keyv@5.6.0, flat-cache@6.1.23 och cache-manager@7.2.9. Organisationer måste verifiera exponeringen med hjälp av de exakta paketversionerna som anges i lockfilerna, inte de aktuella registertaggarna.
Analys av skadlig kod
Översikt över nyttolasten
Skadlig programvara distribueras i en tvåstegsarkitektur:
Steg 1 – setup.mjs (Dropper): Detta lätta Node.js-skript fungerar som den initiala nyttolasten. Det kontrollerar om Bun JavaScript-runtime finns och, om så inte är fallet, laddar det ner Bun v1.3.13 från de officiella GitHub-utgåvorna. Därefter överlämnar det exekveringen till huvudpaketet för informationsstöld innan den tillfälliga runtime-katalogen raderas för att minska de forensiska spåren.
Steg 2 – Math_Symbol.js / math_init.js (infostealer): Ett kraftigt förvrängt, kompilerat paket på 727 680 byte som körs via Bun-runtime-miljön. Detta är den centrala motorn för insamling av inloggningsuppgifter. Båda filnamnen (Math_Symbol.js och math_init.js) har observerats i det vilda och innehåller identisk funktionalitet med samma SHA-1-hash.
Mål för insamling av inloggningsuppgifter
Infostealern inventerar och exfiltrerar systematiskt:
Persistensmekanismer
Keyv-repositoriet behöll separata kopplingar till Claude Code (.claude/settings.json) och Visual Studio Code (.vscode/). Dessa persistensmekanismer på IDE-nivå kan köra nyttolasten så snart en användare litar på arbetsytan eller godkänner projektkonfigurationen, vilket ger en sekundär körväg oberoende av npm install-livscykelskript.
Skadlig programvara installerar även en bevakare för återkallande av autentiseringsuppgifter – en fälla som utlöser en lokal hanterare som tillhandahålls av angriparen när token byts ut. De som hanterar incidenten måste ta bort denna bevakare innan de byter ut några autentiseringsuppgifter.
Kommando och kontroll
Skadlig programvaran hämtar sina C2-domäner från ett smart kontrakt på Ethereum (StringListStore) med hjälp av ett eth_call istället för att bädda in dem i nyttolasten. Denna blockkedjebaserade C2-upplösning gör det möjligt för operatören att uppdatera infrastrukturen utan att behöva modifiera den skadliga programvaran. Historiken på blockkedjan visar att kontraktet ursprungligen konfigurerades med tre domäner innan det uppdaterades för att endast returnera npm-cache[.]com. Kontraktets ägare finansierades av en adress som tidigare flaggats för bedräglig verksamhet.
Exfiltrering
Stulna data krypteras med ett hybridsystem bestående av AES-256 och RSA-4096 och exfiltreras sedan via två kanaler:
De första commit-uppdateringarna till utläckningsrepositorierna innehåller hottexposten: ”IfYouBlockThisAPIKeyItWillCrashTheLiveProductionServersOfAllThirdPartyClients.”
Kända indikatorer på intrång (IoC)
|
Kategori |
Indikator |
Beskrivning |
|
Domän |
npm-cache[.]com |
Primär C2- och dataexfiltreringsdomän; levereras via Cloudflare |
|
Domän |
registry[.]npmjs[.]org |
Endpunkt för tokenvalidering som missbrukas för verifiering av inloggningsuppgifter i realtid före exfiltrering |
|
Domän |
eth-mainnet[.]nodereal[.]io |
Ethereum RPC-ändpunkt som används för att hämta C2-domäner baserade på smarta kontrakt |
|
Domän |
go[.]getblock[.]io |
Ethereum RPC-ändpunkt som används för hämtning av C2-domäner baserat på smarta kontrakt |
|
Domän |
eth[.]llamarpc[.]com |
Ethereum RPC-ändpunkt som används för att hämta C2-domäner baserade på smarta kontrakt |
|
Domän |
pypi-get[.]com |
Ytterligare infrastruktur som kontrolleras av angriparen |
|
Domän |
js-mirror[.]com |
Ytterligare infrastruktur som kontrolleras av angriparen |
|
IPv4 |
104[.]21[.]35[.]216 |
Cloudflare-IP kopplad till npm-cache[.]com |
|
IPv4 |
172[.]67[.]167[.]200 |
Cloudflare-IP kopplad till eth[.]llamarpc[.]com |
|
IPv4 |
185[.]44[.]207[.]215 |
IP kopplad till go[.]getblock[.]io ETH RPC |
|
IPv4 |
35[.]175[.]164[.]77 |
AWS-IP kopplad till eth-mainnet[.]nodereal[.]io |
|
Filnamn |
Math_Symbol.js |
Förvrängd infostealer-payload (727 680 byte); distribuerad via preinstall-skript i npm-paket |
|
Filnamn |
math_init.js |
Variant av infostealer-payloaden med identisk funktionalitet |
|
Filnamn |
setup.mjs |
Stage-1-dropper; hämtar Bun-runtime och utlöser körning av infostealer |
|
Filhash (SHA-1) |
35a672cf34b996b91f3e1c28cbf3a05a37e036e4 |
Math_Symbol.js / math_init.js – infostealer-nyttolast |
|
Filhash (SHA-1) |
686aa40d0fc22c8d569494543a0f891f359f2f99 |
setup.mjs i katalogen .claude (Claude Code hook) |
|
Filhash (SHA-1) |
f525d52ceb966516686b482d3dc0137028cc6a63 |
setup.mjs finns i katalogen .vscode (VS Code-hook) |
|
Användaragent |
Bun/1.3.13 |
HTTP-user-agent-sträng som används vid nedladdning av Bun-runtime och C2-kommunikation |
|
Filsystemväg |
/tmp/bun-dl-*/ |
Tillfällig katalog som används för nedladdning och mellanlagring av Bun-runtime |
|
Filsystemets sökväg |
node_modules/keyv/Math_Symbol.js |
Platsen för infostealer-payloaden i det installerade paketträdet |
|
Sträng |
Shai-Hulud: Here We Go Again |
Beskrivning av GitHub-repositoriet som används för exfiltreringsrepositorier |
|
Sträng |
Om du blockerar den här API-nyckeln kommer det att krascha live-produktionsservrarna hos alla tredjepartsklienter |
Hotfull sträng i de första exfiltrerings-commitsen |
Detekteringar och hotjakt
Säkerhetsteam bör implementera följande detekteringsåtgärder:
Åtgärder och avhjälpande åtgärder
Omedelbara åtgärder
Systemåterställning
Granskning av moln- och källkodsmiljöer
Säkring av leveranskedjan
Om du är orolig för någon av de hot som beskrivs i detta meddelande eller behöver hjälp med att avgöra vilka åtgärder du bör vidta för att skydda dig mot de mest väsentliga hoten som din organisation står inför, vänligen kontakta din kundansvarige eller hör av dig till oss för att ta reda på hur du kan skydda din organisation.