V roce 2026 o 131% více nahlášených CVE denně než loni.
← Všechny zranitelnosti

Shai-Hulud: npm červ a jak zjistit, zda zasáhl vaše buildy

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

KdyVlnaCo bylo jinak
Září 2025Shai-HuludSkript 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 2025Sha1-Hulud: The Second ComingPř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 2025Test „3.0“Upravená verze v jediném balíčku, bez významného šíření.
Květen 2026TanStack84 š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 2026AntV639 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 2026Miasma32 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 2026CHAINDROPPopulá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áří 2026Návrat7. 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 2026tensorlake8. ří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í:

  1. Spouští se při instalaci. Skript preinstall nebo postinstall se spustí, jakmile npm install nebo npm ci narazí na infikovanou verzi. Nic ve vašem kódu ji nemusí importovat.
  2. 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.
  3. 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ě.
  4. Šíří 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.
  5. V CI se chová jinak. Payload z listopadu 2025 kontroloval proměnné GITHUB_ACTIONS, BUILDKITE, CIRCLE_SHA1 a 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, postinstall a prepare, 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ž skript prepare spustil payload a pak záměrně selhal, takže ho npm zahodil a v node_modules nezů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

  1. 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í.
  2. Prohledejte každý lockfile na každé větvi, na které v daném období běželo CI, nejen na výchozí větvi.
  3. Prohledejte samotné buildy. Hledejte v logech CI soubory setup_bun.js, bun_environment.js nebo 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.
  4. Zkontrolujte svou organizaci na GitHubu: nové repozitáře pojmenované po červovi, soubory workflow, které jste nenapsali (například shai-hulud-workflow.yml, discussion.yaml nebo formatter_*.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-tool nainstaluje do cache npm balíček, který není závislostí projektu. npm install -g v kroku nastavení, nástroj nainstalovaný uvnitř build skriptu a npm install v 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_modules nebo cache npm mohou obsahovat verzi, kterou aktuální lockfile už nezmiňuje.
  • npm ci instaluje 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

  1. 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.
  2. 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
  1. 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í

  1. Každý lockfile se vyřeší na čisté verze. Na každé větvi npm ls <package> (nebo pnpm why či yarn why) ukáže nainstalovanou verzi; žádná nesmí být na seznamu dané vlny.
  2. Č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
  1. 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é.
  2. Cache jsou pryč. gh cache list v žádném dotčeném repozitáři neukazuje cache vytvořenou před vyčištěním.
  3. 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.
  4. 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 dejte GITHUB_TOKEN jen 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.site nebo 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ů:

VlnaIndikátoryZdroj
Září 2025Payload 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.siteStepSecurity, CISA
Listopad 2025setup_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 SHA1HULUDWiz
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.moeGHSA-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:

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.

Časté dotazy

Je Shai-Hulud stále aktivní?

Ano. Od září 2025 se vrací ve vlnách a v květnu 2026 jeho autoři zveřejnili zdrojový kód, takže pozdější vlny mohou pocházet od různých skupin. Nejnovější vlna popsaná zde zasáhla 8. října 2026 balíček tensorlake.

Má Shai-Hulud CVE?

Červ jako celek žádnou nemá. GitHub zveřejňuje upozornění na malware pro jednotlivé infikované balíčky a vlna TanStack z května 2026 má CVE-2026-45321, kterou CISA 27. května 2026 přidala do svého katalogu Known Exploited Vulnerabilities.

Znamená čistý lockfile, že nás to nezasáhlo?

Ne. Balíčky nainstalované přes npx, globální instalace, instalace uvnitř build skriptů a Dockerfilů, starší lockfily na jiných větvích a obnovené cache mohou přinést infikovanou verzi, kterou aktuální lockfile neukazuje. Potřebujete záznam toho, co si každý build skutečně stáhl.

Jak zjistím, zda mě to zasáhlo?

Získejte dotčené verze balíčků pro danou vlnu, prohledejte každý lockfile na každé větvi, na které v daném období běželo CI, prohledejte logy CI a SBOM jednotlivých buildů na tyto verze a názvy souborů s payloadem a zkontrolujte ve své organizaci na GitHubu repozitáře, workflow a self-hosted runnery, které jste nevytvořili.

Co dělat, když build nainstaloval infikovanou verzi?

Považujte job za kompromitovaný. Vyměňte každý tajný klíč, který mohl číst, včetně tokenů npm, tokenů GitHubu, cloudových přístupových údajů a deploy klíčů, smažte cache GitHub Actions a cache npm a zkontrolujte workflow, repozitáře a runnery, které červ přidal.

Jak zjistím, zda se červ spustil v našich buildech?

Prohledejte logy buildů, lockfily a nainstalované balíčky na indikátory dané vlny: soubory s payloadem jako bundle.js, setup_bun.js, bun_environment.js nebo router_init.js, repozitáře a workflow, které jste nevytvořili, self-hosted runner s názvem SHA1HULUD a spojení na webhook.site nebo getsession.org. Každý job, který nainstaloval dotčenou verzi, payload spustil, takže ho považujte za kompromitovaný, i když nic dalšího nenajdete.