Shai-Hulud je samošířící se červ, který infikuje npm balíčky: útok na dodavatelský řetězec, který každou oběť zapojí do dalšího šíření. Spouští se při instalaci balíčku, krade z počítače tokeny npm, tokeny GitHubu a cloudové klíče a pomocí tokenu npm publikuje infikované verze dalších balíčků oběti. Od první vlny v září 2025 se opakovaně vrací a v květnu 2026 jeho autoři zveřejnili zdrojový kód, takže za každou další vlnou může stát jiná skupina. Pokud některý z vašich buildů nainstaloval infikovanou verzi, považujte daný job za kompromitovaný a vyměňte každý tajný klíč, který mohl číst.
Největší škody vznikají v CI. Ve vlně z listopadu 2025 Wiz zjistil, že jen 23 % infekcí bylo na počítačích vývojářů, zbytek hlavně v pipeline CI/CD, nejčastěji v GitHub Actions. Build instaluje stovky balíčků, má tajné klíče potřebné k publikování a nasazení a spouští každý instalační skript, aniž by na to někdo dohlížel. Když je tedy ohlášena nová vlna, každý tým se ptá na totéž: nainstaloval některý z našich buildů infikovanou verzi?
Co se stalo
| Kdy | Vlna | Co bylo jinak |
|---|---|---|
| Září 2025 | Shai-Hulud | Skript postinstall spustil 3,6 MB velký bundle.js, pomocí TruffleHog hledal tajné klíče a znovu publikoval až 20 balíčků daného správce. Zasaženo bylo více než 500 balíčků, včetně @ctrl/tinycolor a 18 balíčků @crowdstrike. CISA 23. září zveřejnila výstrahu. |
| Listopad 2025 | Sha1-Hulud: The Second Coming | Přešel na preinstall, nainstaloval runtime Bun ke spuštění 10 MB payloadu, umístil zadní vrátka až do 100 balíčků na správce a registroval infikované počítače jako self-hosted runnery GitHubu. Zasaženy byly balíčky Zapier, ENS, PostHog a Postman; Wiz napočítal více než 25 000 repozitářů s uniklými tajnými klíči. |
| Prosinec 2025 | Test „3.0“ | Upravená verze v jediném balíčku, bez významného šíření. |
| Květen 2026 | TanStack | 84 škodlivých verzí 42 balíčků @tanstack. Útočník token npm nikdy neukradl: workflow pull_request_target, otrávená cache Actions a OIDC token přečtený z paměti runneru mu umožnily publikovat přes trusted publishing samotného TanStacku. Sledováno jako CVE-2026-45321, 27. května přidáno do katalogu KEV agentury CISA. |
| Květen 2026 | AntV | 639 verzí 323 balíčků. Na linuxových runnerech GitHub Actions payload četl tajné klíče z paměti procesu runneru a obešel tak maskování v logu. GitHub zneplatnil 61 274 tokenů npm s právem zápisu. |
| Červen 2026 | Miasma | 32 balíčků @redhat-cloud-services, publikovaných přes vložená workflow s platnou provenance. Každá infekce nesla jedinečně zašifrovaný payload, takže detekce podle hashů selhala. |
| Srpen 2026 | CHAINDROP | Populární balíčky pro cachování jako keyv, cacheable a cache-manager, celkem se stovkami zasažených balíčků. V repozitáři přetrvával díky konfiguračním souborům editorů a AI asistentů. |
| Září 2026 | Návrat | 7. září se payload vrátil ve čtyřech balíčcích, které prošly novým skenováním malwaru při publikaci v npm. |
| Říjen 2026 | tensorlake | 8. října byl publikován [email protected] s červem. OX Security zaznamenal nové klíče útočníků a naznačuje „nezávislého aktéra hrozby nebo novou skupinu používající jméno Shai-Hulud“. |
Co červ dělá v CI jobu
Podrobnosti se mezi vlnami mění, ale vzorec platí:
- Spouští se při instalaci. Skript
preinstallnebopostinstallse spustí, jakmilenpm installnebonpm cinarazí na infikovanou verzi. Nic ve vašem kódu ji nemusí importovat. - Sbírá tajné klíče. Proměnné prostředí, tokeny z
.npmrc, cloudové přístupové údaje ze služby metadat a ze správců tajných klíčů a cokoli, co TruffleHog najde na disku. Pozdější vlny četly přímo paměť runneru GitHub Actions, čímž získaly i tajné klíče, které kroku nikdy nebyly zpřístupněny a které by maskování v logu skrylo. - Vynáší je ven. Rané vlny vytvářely veřejné repozitáře GitHubu pojmenované po červovi a pushovaly do nich workflow, které posílalo všechny tajné klíče repozitáře na
webhook.site. Pozdější vlny používaly servery útočníků a komunikační sítě. - Šíří se a zůstává. S ukradeným tokenem npm publikuje infikované verze dalších balíčků. S tokenem GitHubu přidává workflow, registruje self-hosted runnery nebo podstrkuje konfigurační soubory, které se spustí, až někdo repozitář příště otevře.
- V CI se chová jinak. Payload z listopadu 2025 kontroloval proměnné
GITHUB_ACTIONS,BUILDKITE,CIRCLE_SHA1a podobné a v CI běžel synchronně, takže svou práci dokončil dřív, než build pokračoval.
Koho se to týká
Kohokoli, jehož počítače nebo buildy nainstalovaly infikovanou verzi v době, kdy byla v registru: notebooky vývojářů, CI joby, Docker buildy, které spouštějí npm install, a self-hosted runnery. Na balíčku nemusíte záviset přímo; stačí tranzitivní závislost nebo nástroj spuštěný přes npx. Každá vlna má vlastní seznam balíčků a verzí a infikované verze se obvykle odstraní během hodin nebo dnů, takže rozhoduje, zda je v tomto okně něco nainstalovalo.
Jak to funguje
Shai-Hulud není chyba v npm. Je to škodlivý kód publikovaný uvnitř jinak legitimních balíčků, který upozornění GitHubu na malware v infikovaných balíčcích klasifikují jako vložený škodlivý kód (CWE-506). Každá vlna kombinuje několik technik:
- Spuštění při instalaci. npm během instalace spouští skripty životního cyklu balíčku, jako
preinstall,postinstallaprepare, s oprávněními uživatele nebo CI jobu. Infikovaná verze musí být jen ve stromu závislostí; nic ji nemusí importovat. - Šíření s ukradenými právy k publikování. S tokenem npm nebo GitHubu odcizeným oběti červ publikuje nové verze dalších balíčků oběti s přidaným payloadem, takže každý infikovaný správce infikuje další balíčky.
- Publikování přes vlastní release pipeline oběti (TanStack, květen 2026). Podle upozornění GitHubu útočník zřetězil workflow
pull_request_target, cache GitHub Actions otrávenou přes hranici důvěry mezi forkem a základním repozitářem a OIDC token přečtený z paměti runneru a publikoval přes trusted publishing TanStacku, aniž by změnil publikační workflow. Infikované verze deklarovaly volitelnou závislost na commitu ve forku, jehož skriptpreparespustil payload a pak záměrně selhal, takže ho npm zahodil a vnode_modulesnezůstala žádná stopa. - Platná provenance (Miasma, červen 2026). Vložená workflow publikovala z vlastních repozitářů obětí, takže infikované verze nesly platnou provenance, a jedinečně zašifrovaný payload pro každou infekci znemožnil detekci podle hashů.
Jak zjistit, zda vás to zasáhlo
- Získejte seznam dotčených verzí pro danou vlnu. Bezpečnostní firmy je zveřejňují během několika hodin; Datadog například udržuje repozitář indikátorů kompromitace se seznamy balíčků a verzí.
- Prohledejte každý lockfile na každé větvi, na které v daném období běželo CI, nejen na výchozí větvi.
- Prohledejte samotné buildy. Hledejte v logech CI soubory
setup_bun.js,bun_environment.jsnebo jiné názvy souborů uvedené v upozornění a výstup instalace, který zmiňuje dotčené verze. Pokud uchováváte SBOM pro každý build, prohledejte je na tyto verze. - Zkontrolujte svou organizaci na GitHubu: nové repozitáře pojmenované po červovi, soubory workflow, které jste nenapsali (například
shai-hulud-workflow.yml,discussion.yamlneboformatter_*.yml), a self-hosted runnery, které jste neregistrovali.
Příkazy, které si můžete upravit. Chcete-li prohledat lockfily všech vzdálených větví, uložte dotčené verze dané vlny do affected.txt, jednu name@version na řádek:
git fetch --all --quiet
for ref in $(git for-each-ref --format='%(refname:short)' refs/remotes); do
while read -r pkg; do
name=${pkg%@*}; version=${pkg##*@}
git grep -n -A3 -F "$name" "$ref" -- '*package-lock.json' '*yarn.lock' '*pnpm-lock.yaml' \
| grep -F "$version" | sed "s|^|$pkg |"
done < affected.txt
done
Chcete-li vypsat self-hosted runnery své organizace, nové repozitáře, které zmiňují červa, a soubory workflow, o nichž je známo, že je červ vytváří (nahraďte ORG):
gh api --paginate orgs/ORG/actions/runners --jq '.runners[] | [.name, .os, .status] | @tsv'
gh repo list ORG --limit 1000 --json name,description,createdAt \
--jq '.[] | select((.description // "") | test("hulud"; "i")) | [.name, .createdAt] | @tsv'
for repo in $(gh repo list ORG --limit 1000 --json name --jq '.[].name'); do
gh api "repos/ORG/$repo/contents/.github/workflows" --jq '.[].name' 2>/dev/null \
| grep -E '^(shai-hulud-workflow\.yml|discussion\.yaml|formatter_.*\.yml)$' \
| sed "s|^|$repo |"
done
Repozitáře mohou mít i vlastní runnery (gh api repos/ORG/REPO/actions/runners) a rané vlny vytvářely repozitáře v osobních účtech vývojářů, jejichž tokeny ukradly, proto požádejte své vývojáře, aby zkontrolovali i ty své.
Proč lockfile nestačí
Standardní rada zní prohledat package-lock.json, yarn.lock nebo pnpm-lock.yaml na dotčené verze. Udělejte to jako první, ale čistý lockfile nedokazuje čistý build:
- Balíčky nainstalované mimo lockfile.
npx some-toolnainstaluje do cache npm balíček, který není závislostí projektu.npm install -gv kroku nastavení, nástroj nainstalovaný uvnitř build skriptu anpm installv Dockerfilu lockfile projektu obcházejí. - Dnešní lockfile na větvi není lockfile, který běžel. Build před třemi týdny použil to, co tehdy uváděl lockfile, na té větvi, která ho spustila. Pull requesty s aktualizacemi závislostí spouštějí CI proti novým verzím dřív, než je kdokoli zreviduje.
- Cache obnovují to, co bylo nainstalováno dříve. Obnovené
node_modulesnebo cache npm mohou obsahovat verzi, kterou aktuální lockfile už nezmiňuje. npm ciinstaluje to, co říká lockfile. Pokud byla infikovaná verze už zamčená, striktní instalace ji věrně nainstaluje.- I správně publikovaná verze může být škodlivá. TanStack i Miasma byly kompromitovány přes vlastní release workflow a infikované verze Miasma nesly platnou provenance. Ověření, že byla verze publikována řádně, neříká, že je čistá.
Ve skutečnosti potřebujete záznam toho, které verze balíčků si každý build stáhl, napříč všemi repozitáři a všemi větvemi, za celé období, kdy byla infikovaná verze v registru. Viz týká se nás to? jak posoudit novou CVE napříč vašimi buildy.
Jak to opravit a co dělat, když zatím nemůžete záplatovat
- Pokud některý build nainstaloval dotčenou verzi, považujte job za kompromitovaný. Vyměňte každý tajný klíč, který mohl číst: tokeny npm, tokeny GitHubu, cloudové přístupové údaje a deploy klíče. Protože pozdější vlny publikovaly ukradené tajné klíče do účtů jiných obětí, nepředpokládejte, že jste v bezpečí jen proto, že se ve vašem vlastním nic neobjevilo.
- Vymažte cache. Smažte cache GitHub Actions a cache npm na počítačích vývojářů a self-hosted runnerech, aby další build nainstaloval čisté verze:
gh cache delete --all --repo ORG/REPO # GitHub Actions caches, per repository
npm cache clean --force
rm -rf node_modules "$(pnpm store path)" # pnpm; for Yarn: yarn cache clean
- Připněte čistou verzi a odstraňte workflow, repozitáře, runnery a konfigurační soubory, které červ přidal.
Ověřte vyčištění
- Každý lockfile se vyřeší na čisté verze. Na každé větvi
npm ls <package>(nebopnpm whyčiyarn why) ukáže nainstalovanou verzi; žádná nesmí být na seznamu dané vlny. - Čistá instalace neobsahuje žádné soubory s payloadem. Po výše uvedeném vymazání cache instalujte znovu a hledejte soubory s payloadem uvedené v upozorněních:
find node_modules -name bundle.js -size +3M \
-o -name setup_bun.js -o -name bun_environment.js -o -name router_init.js
- Staré přístupové údaje už nefungují. Odvolané tokeny npm zmizely z
npm token list, staré tokeny GitHubu a deploy klíče jsou smazané a cloudové klíče jsou deaktivované, nejen nahrazené. - Cache jsou pryč.
gh cache listv žádném dotčeném repozitáři neukazuje cache vytvořenou před vyčištěním. - Nezůstalo nic, co červ přidal: žádné repozitáře, soubory workflow, self-hosted runnery ani konfigurační soubory editorů a AI asistentů, které jste nevytvořili.
- Další build je čistý. Zkontrolujte verze, které skutečně nainstaloval, z jeho logu nebo SBOM daného buildu, nejen z lockfilu.
Jak příště omezit škody
- Vypněte instalační skripty, kde to jde. npm v12, vydaný v červenci 2026, ve výchozím nastavení instalační skripty vypíná a umožňuje je povolit pro jednotlivé balíčky. pnpm a Yarn nabízejí podobné kontroly.
- S přijetím nových verzí počkejte. Většina infikovaných verzí je odhalena a odstraněna během hodin nebo dnů. Ochranná lhůta (cooldown) u aktualizací závislostí, kterou podporují Dependabot i Renovate, je udrží mimo vaše buildy. Package firewall od GitHubu, od 20. října 2026 ve veřejné preview pro GitHub Enterprise Cloud, kontroluje instalace z npm na škodlivé balíčky a nabízí také ochranné lhůty.
- Publikujte přes trusted publishing a krátkodobé tokeny. npm v prosinci 2025 odvolal všechny klasické tokeny. Místo dlouhodobých tokenů používejte trusted publishing z CI založený na OIDC a publikační workflow držte odděleně od workflow pro build a testy.
- Zabezpečte workflow, která červ zneužívá. Vyhněte se
pull_request_target, připínejte actions na SHA commitů a dejteGITHUB_TOKENjen oprávnění, která každý job potřebuje. Viz tajné klíče v GitHub Actions: jak unikají a jak je zabezpečit. - Omezte, kam se buildy mohou připojit. Build potřebuje své registry balíčků a několik API. Pokud dosáhne jen na ně, volání červa na
webhook.sitenebo na jeho vlastní servery selžou. Viz jak egress policy zastaví vynášení dat z GitHub Actions. To nezastaví vynášení dat přes hostitele, kterého build legitimně používá, například vlastní API GitHubu, na které některé vlny spoléhaly.
Zkontrolujte kompromitaci
Shai-Hulud je škodlivý už ze své podstaty: každý počítač nebo job, který nainstaloval infikovanou verzi, ho spustil. Indikátory se liší podle vlny, takže použijte upozornění pro vlnu, kterou kontrolujete. Z primárních zdrojů:
| Vlna | Indikátory | Zdroj |
|---|---|---|
| Září 2025 | Payload bundle.js, SHA-256 46faab8ab153fae6e80e7cca38eab363075bb524edd79e42269217a083628f09; workflow .github/workflows/shai-hulud-workflow.yml na větvi shai-hulud; veřejný repozitář s názvem Shai-Hulud; spojení na webhook.site | StepSecurity, CISA |
| Listopad 2025 | setup_bun.js, SHA-1 d1829b4708126dcc7bea7437c04d1f10eacd4a16; bun_environment.js, SHA-1 d60ec97eea19fffb4809bc35b91033b52490ca11 nebo 3d7570d14d34b0ba137d502f042b27b0f37a59fa; repozitáře se soubory cloud.json, contents.json, environment.json, truffleSecrets.json a actionsSecrets.json; workflow .github/workflows/discussion.yaml a formatter_*.yml; self-hosted runner s názvem SHA1HULUD | Wiz |
| Květen 2026, TanStack | "@tanstack/setup": "github:tanstack/router#79ac49eedf774dd4b0cfa308722bc463cfe5885c" v optionalDependencies; payload router_init.js (asi 2,3 MB) v kořeni balíčku; pomocný skript tanstack_runner.js; spojení na filev2.getsession.org a seed1.getsession.org až seed3.getsession.org a stahování druhé fáze z litter.catbox.moe | GHSA-g7cv-rxg3-hmpx |
Pozdější vlny měnily payload pro každou infekci, takže neshodující se hash souboru nic nedokazuje; porovnávejte nejprve verze balíčků.
Kdy předpokládat kompromitaci. Každého hostitele a CI job, který nainstaloval dotčenou verzi, považujte za kompromitovaný, i když instalace selhala nebo v node_modules nic nezanechala. Upozornění GitHubu k TanStacku uvádí, že každé vývojářské nebo CI prostředí, které nainstalovalo dotčenou verzi, „by mělo být považováno za kompromitované“. Kontrolujte od prvního publikování infikované verze, ne ode dne, kdy byla vlna ohlášena.
Vyměňte přístupové údaje. Vyměňte vše, na co instalace mohla dosáhnout. Ve vlně TanStack to podle postmortemu znamenalo přístupové údaje k AWS, GCP, Kubernetes, Vault, GitHubu, npm a SSH. Projděte auditní logy cloudu, zda nevykazují aktivitu z dotčených hostitelů během instalačního okna a po něm.
Vymažte cache a artefakty. Prohledejte repozitáře artefaktů a proxy závislostí na kopie dotčených verzí v cache a smažte je, jak doporučuje CISA. Smažte node_modules, cache npm a cache CI; ve vlně TanStack se útočník dostal dovnitř přes otrávenou cache úložiště pnpm, takže kontrolujte cache Actions i cache balíčků. Znovu sestavte a znovu vydejte vše, co vytvořil kompromitovaný job.
Co říct zákazníkům
Pokud dodáváte software sestavený s npm, zákazníci se budou ptát, zda váš produkt červa obsahuje nebo zda byla kompromitována vaše release pipeline. Odpovězte tím, co jste zkontrolovali a co jste udělali:
Zkontrolovali jsme každý build [produkt] mezi [datum] a [datum] proti zveřejněným seznamům verzí npm balíčků infikovaných červem Shai-Hulud. [Žádný build nenainstaloval dotčenou verzi.] / [Buildy [komponenta] ze dne [datum] nainstalovaly [package@version]; vyměnili jsme přístupové údaje, ke kterým měly tyto buildy přístup, znovu jsme sestavili [produkt] z čistých závislostí a vydali [verze]. Vydání [x až y] jsou dotčena; aktualizujte prosím na [verze].] Toto oznámení aktualizujeme, pokud nové vlny situaci změní.
Pokud stále kontrolujete, řekněte to a uveďte, kdy odpovíte. Úvodní prohlášení a šablony pro každý výsledek najdete v článku jak odpovědět zákazníkům po útoku na dodavatelský řetězec.
Jak pomáhá CRACI
CRACI spouští vaše joby GitHub Actions na vlastních runnerech (runs-on: craci) a zaznamenává každý balíček, který si každý job skutečně stáhne z npm a dalších registrů, včetně balíčků obnovených z cache. Tento záznam pokrývá výše uvedená slepá místa: nástroje přes npx, globální instalace i instalace uvnitř build skriptů se v něm objeví, protože záznam vychází z toho, co job stáhl, ne z toho, co deklaroval lockfile. Když je ohlášena nová vlna, vyhledáte dotčené verze v buildech, které CRACI zaznamenalo, místo abyste je rekonstruovali z lockfilů a logů, a záznam síťového provozu každého jobu ukazuje, kam se připojoval. Historie buildů uchovává každý minulý build s jeho SBOM, takže v rámci doby uchovávání vidíte, které buildy obsahovaly infikovanou verzi a v jakém okně jste byli vystaveni.
Egress policy CRACI omezuje, kam se job může připojit, ověřuje se před spuštěním jobu, při selhání vše zablokuje (fail-closed) a upozorní vás, když se job pokusí dosáhnout cíle, který politika nepovoluje. Infikovaná vydání se zveřejněnou CVE, jako ve vlně TanStack, se označí jako jakákoli jiná zranitelnost a policy gates mohou buildy, které je obsahují, zablokovat. CRACI nehodnotí balíčky podle škodlivého chování a neoznačí novou infikovanou verzi dřív, než existuje bezpečnostní upozornění; pokud chcete infikované verze blokovat ještě před instalací, zkombinujte ho s nástrojem pro hodnocení reputace balíčků.
Podívejte se na záznam balíčků jednoho z vašich buildů
Další incidenty: všechny zranitelnosti.
Zdroje a aktualizace
Primární zdroje a původní výzkum:
- CISA: výstraha z 23. září 2025, katalog KEV
- GitHub: GHSA-g7cv-rxg3-hmpx (CVE-2026-45321), náš plán na bezpečnější dodavatelský řetězec npm, odvolání klasických tokenů npm
- Postmortem TanStacku
- Wiz o vlně z listopadu 2025 a jejích následcích
- Microsoft o AntV a doporučení Microsoftu k detekci
- Datadog a Elastic o CHAINDROP
- StepSecurity o vlně ze září 2025
- FIRST: skóre EPSS pro CVE-2026-45321
Aktualizace:
- 2026-11-11: Do přehledu přidáno EPSS, třída CWE a poděkování, kontroly ke zkopírování a sekce o tom, jak červ funguje, jak ověřit vyčištění a jak zkontrolovat kompromitaci, s indikátory pro každou vlnu.
- 2026-11-11: Přesunuto z blogu do sekce zranitelností. Přidána vlna tensorlake z 8. října 2026, datum návratu v září 2026, CVE-2026-45321 a její zařazení do KEV pro vlnu TanStack a package firewall od GitHubu.
- 2026-09-25: Poprvé zveřejněno na blogu CRACI.