Campaign
Shai-Hulud: the self-spreading worm campaign on npm and PyPI
- affected packages
- 2,068
- affected versions
- 5,307
- OSV records
- 2,111
- incident groups
- 8
- OSV publication period
- 2025-09-15 → 2026-08-26
Counts are records in our OSV snapshot that mention Shai-Hulud: versions are deduplicated as package + version, and one record can cover several versions; this is not the full number of victims. Incident groups are our own grouping by date, and the operators are not necessarily the same.
Since September 2025, several supply-chain attacks using the Shai-Hulud name, code or techniques have planted malware in npm and PyPI packages; operators may differ.
| Who is affected | Developer machines and CI environments that installed or updated an affected version during a wave, and the GitHub, npm, PyPI and cloud accounts they could reach. |
|---|---|
| What to do now | Check your lockfile with the tool below; if you were hit, isolate the machine or remove persistence first, then revoke and rotate every credential. |
Attack flow
Read left to right: this is the order the attack happens in. A blue shield means the step can be stopped; the earlier, the better. Click an icon for details.
- Attacker
Get the maintainer's publish rights
Steal a maintainer account or token, abuse a GitHub Actions misconfiguration (such as pull_request_target), or abuse an over-permissive npm trusted publisher.
Defense: Phishing-resistant MFA on publishing accounts; limit trusted publishing to protected branches; never run external PR code in pull_request_target.
- Attacker
Publish a new version with an install script under the real identity
The package's own code barely changes; it gains a preinstall / postinstall script or a dependency. In 2026 the versions even carry genuine provenance and signatures.
Defense: Require human approval for new releases; watch your packages' publish history; remember that signed is not the same as safe.
- Your system
A developer or CI runs npm install
Version ranges (such as ^1.2.0) pull in the newest, malicious version and its install script runs; PyPI versions run at Python startup through a .pth file.
Defense: Commit a lockfile and use npm ci in CI; set ignore-scripts; add a cooldown for new versions (min-release-age).
- Your system
Collect secrets
Reads environment variables, .npmrc, cloud credentials and metadata services, GitHub tokens and AI-tool settings; early versions also ran TruffleHog across the whole disk.
Defense: Give CI and developer machines only short-lived, least-privilege tokens; keep secrets out of environment variables and plain-text files.
- External
Exfiltrate to a public GitHub repo
Encodes the stolen data into a new public repo in the victim's own account (descriptions often mention Shai-Hulud, Sha1-Hulud or Hades); later versions also use other channels.
Defense: Monitor new public repos in your organisation; restrict outbound traffic from CI and developer machines.
- Attacker
Use the stolen tokens to infect more packages
With the victim's npm token it lists every package they can publish, plants itself and publishes new versions, looping back to step 2. This is what makes it spread.
Defense: Revoke npm tokens as soon as you notice (but do step 7 first); require two-factor authentication to publish.
- Your system
Persist; some versions destroy data
Plants GitHub Actions workflows or a self-hosted runner, adds auto-run entries in .claude/ and .vscode/, installs a gh-token-monitor background service; some versions wipe the home directory when a token is revoked.
Defense: Isolate the machine or remove persistence first, then revoke tokens; check workflows and AI-tool settings.
This is the pattern shared by the waves; how each case got publish rights differs, see the waves and case pages. Step 6 loops back to step 2, which is what makes it spread.
Quick self-check
From fastest to most thorough. Every item links to its source.
- Check your packages: use the checker below with a package name or your lockfile [7]
Compares against every Shai-Hulud-related malicious version in OSV, in your browser only. Check CI and developer machine install logs too.
- Check your GitHub account for public repos you did not create [53]
Most waves store stolen data in a new repo under the victim's account; descriptions often mention Shai-Hulud, Sha1-Hulud, Hades or Miasma.
gh repo list <your-user-or-org> --limit 1000 --json name,description --jq '.[] | select((.description // "") | test("hulud|hades|miasma"; "i")) | .name' - Check repos for unfamiliar GitHub Actions workflows [53]
For example shai-hulud-workflow.yml, discussion.yaml, or any step that dumps toJSON(secrets); also look for a self-hosted runner named SHA1HULUD.
ls .github/workflows/ | grep -iE 'shai-hulud|discussion|formatter_' grep -rl 'toJSON(secrets)' .github/workflows/ - Check developer machines for persistence and AI-tool hooks [17]
The 2026 waves add auto-run entries to .claude/settings.json and .vscode/tasks.json and install background services such as gh-token-monitor.
grep -lE 'setup\.mjs|router_runtime|router_init|index\.js' .claude/settings.json .vscode/tasks.json 2>/dev/null ls ~/.local/bin/gh-token-monitor.sh ~/Library/LaunchAgents/*token-monitor* ~/Library/LaunchAgents/*kitty-monitor* 2>/dev/null systemctl --user list-units 2>/dev/null | grep -iE 'token-monitor|kitty-monitor|update-monitor' - Python environments: look for suspicious .pth startup files [54]
The Hades wave on PyPI uses *-setup.pth files to run at Python startup. **Do not check with python or pip**: starting Python runs a malicious .pth before your check does. The command below only lists files; review any unfamiliar .pth (especially *-setup.pth), and inspect a suspect environment from a separate clean machine.
find / \( -path '*/site-packages/*' -o -path '*/dist-packages/*' \) -name '*.pth' 2>/dev/null - If you were hit: isolate or remove persistence first, then revoke tokens [54]
Several reports say some versions wipe data once a token is revoked. After cleaning up, rotate every credential the machine could reach (npm, GitHub, cloud, SSH, AI services).
Am I affected?
Checks against 5,307 malicious versions of 2,068 packages (OSV, snapshot of 2026-09-23). Everything runs in your browser; nothing is uploaded.
Check a whole project: paste or choose a lockfile
Supports package-lock.json, pnpm-lock.yaml, yarn.lock and requirements.txt. The file is read only in your browser and never uploaded. Only the lockfile is needed; do not paste .npmrc or any file containing tokens.
No match does not guarantee you are safe: the list only has reported versions, and a lockfile may not match what was actually installed. Check CI and developer machine install logs too.
Timeline
New malicious version records per week (by OSV publication date).
Waves
Shai-Hulud (first wave)
npm2025-09-01 → 2025-10-31 · 203 OSV records · 203 packages · 589 versions
Malicious versions of @ctrl/tinycolor and other npm packages stole developer and CI credentials on install, then used the stolen npm tokens to infect the same maintainer's other packages: the first npm worm that spread by itself.
New in this wave:Compared with the Nx/s1ngularity attack before it: automatic spread to other packages, exfiltration through a GitHub Actions workflow, and secret hunting with TruffleHog.
Details: how they got in, what it did, scale and attribution, how to check
How they got in:How the attackers first got publish rights is unresolved; reports only confirm stolen maintainer accounts or tokens. Wiz links it to the August 2025 Nx attack, as an assessment.
What it did:A postinstall step ran bundle.js: it read environment variables, scanned the filesystem with TruffleHog, queried cloud metadata and secret stores, uploaded the results to a public repo named Shai-Hulud in the victim's account, and planted shai-hulud-workflow.yml to keep leaking secrets.
Scale (reports differ): 40+ packages at first(Socket [31]);16 Sept update: nearly 500 packages, including CrowdStrike packages(Socket [31]);500+ packages(StepSecurity [51]);195 packages listed with versions(StepSecurity [51]);187+ packages at the time of publication, expected to grow(Semgrep [22]);Over 100 packages, according to the article text(Wiz [58]);Appendix lists about 200+ package names and 200+ versions(Wiz [58]);The first 40 (citing Socket/StepSecurity), then "147+ additional"; whether that means 187 in total is unconfirmed(Aikido Security [38]);Removed 500+ compromised packages from npm(GitHub [3]);Over 500 packages(CERT/CC [47])
Attribution: Assesses that the campaign is "directly downstream" of the late-August 2025 s1ngularity/Nx compromise (an assessment; no confidence level given)(Wiz [58]);The article title links this wave to the s1ngularity/Nx attackers and says they reused much of the Nx playbook; Aikido reads the name "Shai-Hulud" as the attacker's own branding and does not attribute it to any named group(Aikido Security [38]);Says with "moderate confidence" that the attacker used a large language model (LLM) to help write the code, based on comments and emojis in the bash script; this does not name any actor(Palo Alto Networks Unit 42 [35])
- Compare your lockfile and installed versions against the published lists of affected versions (for example @ctrl/tinycolor 4.1.1 and 4.1.2).
- Run npm view <package-name> time to see when each version was published, and look for new versions that appeared around 15 September 2025.
- Search your GitHub account or organisation for shai-hulud-workflow.yml, a branch named shai-hulud, a repository named Shai-Hulud, and repositories whose names end in "-migration" or whose description is "Shai-Hulud Migration".
- Check GitHub audit logs and cloud audit logs for unusual secret access, such as BatchGetSecretValue, ListSecrets or GetSecretValue events in AWS CloudTrail.
- On build and developer machines, watch for these signs: TruffleHog scanning the whole filesystem, npm publish run with a force flag, or curl connecting to webhook.site.
Sha1-Hulud: The Second Coming (Shai-Hulud 2.0)
npmMaven2025-11-01 → 2026-01-31 · 796 OSV records · 796 packages · 1,125 versions
The largest wave: worms in hundreds of npm packages from Zapier, ENS, PostHog, Postman, AsyncAPI and others, with one version auto-mirrored to Maven Central. A variant called "The Golden Path" appeared in a single package in late December.
New in this wave:Runs at preinstall (even if install fails), switches to the Bun runtime, randomises exfiltration repo names, infects up to 100 packages per victim, adds a home-directory wiper, and persists through a self-hosted runner.
Details: how they got in, what it did, scale and attribution, how to check
How they got in:In PostHog's case, the attackers abused a pull_request_target workflow misconfiguration to steal a bot account's GitHub token, then rewrote the workflow to get publish rights.
What it did:setup_bun.js downloads Bun and runs the obfuscated bun_environment.js in the background: it collects environment variables, .npmrc, cloud credentials and GitHub tokens, puts them encoded into a new public repo, then infects the victim's other packages; without usable tokens it may wipe the home directory.
Scale (reports differ): 796 unique npm packages(Datadog Security Labs [21]);1,092 package versions(Datadog Security Labs [21]);About 700 npm packages (its appendix says "800+")(Wiz [57]);492 packages(Aikido Security [40]);795 npm packages(ReversingLabs [50]);"Over 70" packages at the time of writing(StepSecurity [53]);500+ packages(Socket [30]);700+ versions(Socket [30]);830+ npm packages(The Hacker News [32]);1 Maven artifact (org.mvnpm:posthog-node:4.18.1), the only Java package affected at the time of reporting(The Hacker News [32]);8 PostHog JavaScript packages compromised(PostHog [12]);"Golden Path" strain: 1 package (@vietmoney/react-big-calendar), with no major spread seen(Aikido Security [39])
Attribution: No attribution has been confirmed.(Wiz [57]);Aikido judges the December "Golden Path" strain "highly unlikely to be a copy-cat" because its code was re-obfuscated rather than edited, which points to the original actor. This is the researchers' assessment, not a confirmed attribution.(Aikido Security [39])
- Compare your lockfiles (e.g. package-lock.json) against the published lists of affected package versions, especially packages installed or updated after 21 November 2025.
- Search your GitHub accounts and organisations for repositories whose description contains "Sha1-Hulud" (or "Goldox-T3chs"), and for public repositories or branches you don't recognise.
- Look for a self-hosted runner named SHA1HULUD and for unexpected .github/workflows/discussion.yaml or formatter_123456789.yml workflows.
- On developer machines and CI, look for setup_bun.js or bun_environment.js (or bun_installer.js and environment_source.js), whether as files or running processes, and for a $HOME/.dev-env/ folder.
- Check your npm publish logs for versions you did not publish. If you find signs of infection, rotate all npm, GitHub, SSH, cloud and CI tokens and secrets.
Mini Shai-Hulud: SAP and PyTorch Lightning
npmPyPI2026-04-01 → 2026-05-06 · 5 OSV records · 5 packages · 6 versions
SAP's official @cap-js and mbt npm packages and PyTorch Lightning on PyPI (2.6.2, 2.6.3) were laced with credential stealers in quick succession, the campaign's first step into PyPI.
New in this wave:Abuses an over-permissive npm trusted publisher (OIDC); persists through VS Code tasks.json and Claude Code settings; fakes commits authored by "claude".
Details: how they got in, what it did, scale and attribution, how to check
How they got in:SAP: a developer's GitHub account was taken over and a publish workflow on a non-default branch was modified to obtain an npm token. Lightning: possibly a leaked project-scoped PyPI token, unconfirmed.
What it did:Runs on install or import, steals developer and CI credentials, and plants files and workflows in writable GitHub repos to keep spreading.
Scale (reports differ): SAP incident: 4 malicious versions (@cap-js/sqlite@2.2.2, @cap-js/postgres@2.2.2, @cap-js/db-service@2.10.1, mbt@1.2.48)(BleepingComputer [41]);The @cap-js packages have about 1M monthly downloads combined; mbt about 200K(SafeDep [16]);Over 1,000 GitHub repositories poisoned in the SAP wave(SafeDep [16]);2 malicious versions of the PyPI package lightning (2.6.2, 2.6.3)(Socket [25])
Attribution: Aikido and Socket linked the SAP incident to TeamPCP with medium confidence, based on code and tactics seen in the Trivy, Checkmarx and Bitwarden attacks.(BleepingComputer (reporting Aikido and Socket) [41]);The attacker posted a PGP-signed message branded "Team PCP" on a Tor site claiming LAPSUS$ was heavily involved. Socket has not verified the signature or the claims, and judges this to be a different actor from the original Shai-Hulud using a similar playbook.(Socket [25]);Attributes the Lightning incident to TeamPCP and repeats the LAPSUS$ claim, with no stated confidence level.(The Hacker News [33])
Mini Shai-Hulud: TanStack, UiPath, Mistral
npmPyPI2026-05-07 → 2026-05-15 · 180 OSV records · 179 packages · 428 versions
The attackers hijacked TanStack's GitHub Actions publish pipeline and shipped 84 malicious versions of 42 @tanstack/* packages under the official identity; UiPath, Mistral AI and others were hit the same day. The only wave with a CVE, listed in CISA KEV.
New in this wave:Chains pull_request_target, Actions cache poisoning and an OIDC token read from memory; the malicious versions carry valid SLSA provenance and Sigstore signatures.
Related pages on this site: TanStack npm supply-chain compromise (Mini Shai-Hulud): hijacked release pipeline pushed credential-stealing malware into 42 @tanstack packages
Mini Shai-Hulud: @antv and copycats
npm2026-05-16 → 2026-05-22 · 332 OSV records · 334 packages · 677 versions
Hundreds of packages under one npm maintainer (@antv, echarts-for-react, size-sensor and more) got malicious versions within a short window. Copycat packages built from the leaked source code appeared at the same time.
New in this wave:Hijacks AI coding assistants, takes commands via GitHub commit search, abuses Sigstore signing, attempts Docker container escape, and reads CI runner memory.
Details: how they got in, what it did, scale and attribution, how to check
Scale (reports differ): @antv wave: 637 versions across 317 packages (the URL says 314)(SafeDep [17]);@antv wave: 639 versions across 323 packages(Socket [24]);Whole campaign: 1,055 versions across 502 packages (npm 1,048 / 498; PyPI 6 / 3; Composer 1)(Socket [24]);About 1.9k GitHub repositories used to hold stolen data(Socket [24]);@antv wave: 645 versions across 324 packages (318 of the 547 packages under one maintainer account, plus 6 under another); about 16M combined weekly downloads(OpenSourceMalware [6]);Copycats: 4 packages with 2,678 weekly downloads in total(OX Security [49])
Attribution: Attributes the @antv wave to TeamPCP, without a confidence level or evidence in the text.(OpenSourceMalware [6]);Ties the @antv wave to the "Mini Shai-Hulud" campaign because the operating model is the same, without naming an actor.(Socket [24]);Says with high confidence that the four copycat packages come from a different actor, not TeamPCP, because they are barely obfuscated and their timing matches the source-code leak and a "supply chain attack competition" on BreachForums.(OX Security [49])
IronWorm (Rust variant)
npm2026-05-23 → 2026-05-31 · 33 OSV records · 33 packages · 36 versions
Dozens of WeaveDB / Arweave packages under one npm account were republished with malware. The payload is written in Rust, steals cloud, AI-service and developer-tool keys, and pushes itself to more repos with stolen GitHub access.
New in this wave:A compiled Rust binary instead of obfuscated JavaScript, per-string encryption, Tor communication and an eBPF rootkit; reuses Shai-Hulud's commit messages.
Details: how they got in, what it did, scale and attribution, how to check
How they got in:The attackers controlled the npm account that publishes these packages; how it was first compromised has not been disclosed.
What it did:A preinstall step runs ./tools/setup (a Linux binary) that collects dozens of cloud, AI-service and CI credentials, then pushes back-dated malicious commits to every writable GitHub repo.
Scale (reports differ): 33 malicious-package records (from our OSV snapshot)(OSV [10]);37 package@version entries in its IoC table (the text gives no total)(JFrog Security Research [13]);57 back-dated malicious commits across 9 GitHub organisations(JFrog Security Research [13]);About 4,500 contributions to private projects by the compromised account during the active period(JFrog Security Research [13]);37 packages, 9 organisations, 57 back-dated commits and about 4,500 private contributions; warns the public count is a lower bound(Phoenix Security [11]);43 malicious packages, 37 of them listed by name(SafeDep [19]);"Over 50" npm packages(The Hacker News [34])
Attribution: The operator is unknown. They made operational-security mistakes: putting their own wallet recovery phrase in the malware's skip-list, leaving the eBPF build path in the program, and not stripping debug metadata. The Ethereum address derived from the phrase is nearly empty.(JFrog Security Research [13]);An "unknown actor", operationally adjacent to Shai-Hulud / TeamPCP, with "no confirmed identity overlap".(Phoenix Security [11])
- Search package-lock.json, yarn.lock or pnpm-lock.yaml for the malicious versions JFrog lists, such as ai3@0.3.5, arnext@0.1.5 and weavedb-sdk@0.45.3.
- Look for tools/setup or .github/scripts/precheck files in your projects and node_modules, and for "preinstall": "./tools/setup" in package.json.
- Review GitHub repositories you can write to for commits by claude@users.noreply.github.com or fake dependabot/renovate/github-actions bots that are dated years in the past, and for the commit messages listed above.
- Check GitHub Actions workflows for new jobs named "Run checks", "Process results" or "Run analysis", for steps using toJSON(secrets), and for an artifact called format-results.txt.
- If a Linux machine installed any of these versions, treat it as compromised: reimage it and rotate every cloud, AI, npm, GitHub and Kubernetes credential.
Red Hat packages and Hades / Miasma (PyPI)
npmPyPI2026-06-01 → 2026-07-31 · 115 OSV records · 78 packages · 182 versions
Dozens of @redhat-cloud-services npm packages got malicious versions; about a week later the same family reached PyPI (named Hades by researchers), hitting bioinformatics, AI-agent and MCP packages. The late-August pantheon-agents advisory records a June compromise.
New in this wave:Malicious branches could obtain publish tokens through npm trusted publishing, so versions carry valid provenance; on PyPI it uses .pth startup hooks; the code contains prompts meant to make AI analysis tools refuse.
Details: how they got in, what it did, scale and attribution, how to check
How they got in:npm: the attackers created malicious branches in Red Hat repos and rewrote CI workflows; trusted publishing checked the repo and workflow file name but not the branch. PyPI: taken-over maintainer accounts or leaked API tokens.
What it did:After install (npm) or at Python startup (PyPI) it downloads Bun and runs a stealer that collects developer and cloud credentials, spreads to other packages and repos, and stores the data in public GitHub repos; reports disagree on whether it deletes files.
Scale (reports differ): npm: 32 @redhat-cloud-services packages, 96 malicious versions(SafeDep [18]);These packages come from 3 GitHub repositories: javascript-clients (15), frontend-components (14), platform-frontend-ai-toolkit (3)(SafeDep [18]);PyPI: 37 malicious wheel files across 19 projects(Socket [29]);Campaign-wide: 448 artifacts (411 npm across 106 packages, 37 PyPI across 19). The npm figure covers the whole campaign and is not a Red Hat count(Socket [29]);Campaign-wide: 471 artifacts (411 npm across 106 packages, 60 PyPI across 37 packages), including 23 newer PyPI package versions(Socket [27]);PyPI: 30 distinct packages, 36 compromised versions(StepSecurity [54]);PyPI: 6 bioinformatics packages, 1 malicious version each(Endor Labs [46]);PyPI: 5 confirmed packages(O3 Security [5]);pantheon-agents: affected versions 0.6.1 and 0.6.2 (OSV)(OSV [8]);pantheon-agents: GitLab's database lists the affected range as 0.6.1 through 0.6.3, but its own description names only 0.6.1 and 0.6.2(GitLab Advisory Database [2])
Attribution: Attributes the wave to TeamPCP and links the group to Shai-Hulud 2.0 and the LiteLLM compromise, without showing evidence in the post(O3 Security [5]);Mentions "operational security hardening by TeamPCP" in a single sentence, with no reasoning given(Endor Labs [46]);Calls it an evolution of the Miasma threat actor, based on overlapping techniques(StepSecurity [54]);Says attribution is unclear: TeamPCP released Shai-Hulud as open-source attack tooling and promoted a BreachForums contest, so many different actors could run similar operations(Socket [26]);Links the waves to Shai-Hulud / Miasma on technique but does not name an actor(Socket [29])
- Compare your lockfiles, CI logs and the package folder names in site-packages (list them with ls; do not run pip or python in a possibly infected environment) against the published lists of affected versions (for example @redhat-cloud-services packages, ensmallen 0.8.101, pantheon-agents 0.6.1/0.6.2). Note that the "latest" npm version may itself be malicious, and a "verified" result from npm audit signatures does not mean it is safe.
- Check Python site-packages folders for *-setup.pth or _index.js files, and look for /tmp/.bun_ran, /tmp/b-*/bun and /tmp/p*.js.
- Look for update-monitor or gh-token-monitor systemd services or LaunchAgents, and for ~/.local/share/updater/update.py. Also check /etc/hosts for lines containing stepsecurity.
- In your GitHub organisations, search for workflows named "Run Copilot", unexpected codeql.yml files, oidc-* branches, and repositories described as "Hades - The End for the Damned" or "Miasma: The Spreading Blight".
- If you find signs of infection, isolate or image the machine before revoking tokens, because reports say revoking them may trigger a wipe. Then rotate every credential that was on that machine.
keyv / cacheable (ChainDrop)
npm2026-08-01 → 2026-08-20 · 446 OSV records · 446 packages · 2,270 versions
Malicious versions of caching packages such as keyv and flat-cache (about 600 million monthly downloads each) and cacheable (about 30 million a month), spreading through stolen publish tokens to hundreds of packages from ServiceTitan, Qlik and others. Counts range from about 384 to 446 packages.
New in this wave:Looks up its server address through an Ethereum smart contract, targets more AI-tool credentials, and adds IDE and AI-assistant auto-start hooks; the malicious versions again carry genuine SLSA provenance.
Details: how they got in, what it did, scale and attribution, how to check
How they got in:Several reports say the maintainer's GitHub account was taken over and malicious code was pushed to main, then released by the normal trusted-publishing pipeline; how the account was taken is unconfirmed.
What it did:A preinstall setup.mjs downloads Bun and a second stage that steals cloud, GitHub, npm and AI-tool credentials, infects other packages the victim can publish, and installs gh-token-monitor for persistence.
Scale (reports differ): 444 package names(SafeDep [15]);2,234 poisoned versions(SafeDep [15]);Credentials of 12 organisations abused(SafeDep [15]);@servicetitan: 147 packages, 1,082 versions(SafeDep [15]);@onereach / @or-sdk: 155 packages, 487 versions(SafeDep [15]);@ornikar: 47 packages, 537 versions(SafeDep [15]);@qlik / @nebula.js: 59 packages(SafeDep [15]);At least 444 packages (5 August update)(Aikido Security [36]);1,381 versions (5 August update)(Aikido Security [36]);About 1,300 drop repositories(Aikido Security [36]);444 packages(OX Security [48]);821 repositories found through the marker string(OX Security [48]);More than 400 packages(JFrog Security Research [14]);More than 1,700 versions(JFrog Security Research [14]);384 packages (4 August, 11:00 PDT update)(Endor Labs [45]);1,136 versions (4 August, 11:00 PDT update)(Endor Labs [45]);About 515 million weekly downloads for the three seed packages(Endor Labs [45]);More than 400 packages, with more than 1.3 billion monthly downloads(Elastic Security Labs [44]);More than 1,300 versions, with about 2 billion combined monthly downloads(Cyber Security Agency of Singapore [43]);10 core packages; 400 to more than 2,200 downstream (the source does not make clear whether these are packages or versions); "5 confirmed malicious versions" (the source's wording is unclear)(Chainguard [42]);At least 10 confirmed packages, list still growing; tens of millions of weekly downloads(Socket [28]);July downloads: keyv 619.7M, flat-cache 579.8M, file-entry-cache 571.2M(Snyk [23]);446 records in PlainCVE's OSV snapshot (published 2026-08-04 to 08-06)(OSV / OpenSSF malicious-packages [9])
Attribution: The methods "closely match" Shai-Hulud, but Socket makes no firm attribution to a campaign.(Socket [28]);This is a descendant of "Mini" Shai-Hulud, heavily based on public Mini Shai-Hulud repositories published by TeamPCP. This only shows where the code came from; it does not show that TeamPCP carried out this attack.(Wiz [55]);The marker string and the GitHub drop-repository method match the May 2026 TanStack compromise, but SafeDep calls the link "circumstantial".(SafeDep [15]);Describes ChainDrop as a variant of the Shai-Hulud family.(Cyber Security Agency of Singapore [43]);No one has claimed the attack and no attacker is named. Chainguard mentions TeamPCP only for the May @antv wave, not for this one.(Chainguard [42])
- Check lockfiles and dependency trees, including indirect and dev dependencies, for these versions: keyv 6.0.0, flat-cache 6.1.24, file-entry-cache 11.1.6 (Socket says 11.1.7), cacheable 2.5.1, cacheable-request 13.0.20, cache-manager 7.2.10 and ecto 5.0.1. Known clean versions are keyv 5.6.0, flat-cache 6.1.23 and file-entry-cache 11.1.5.
- Search node_modules for package.json files whose preinstall is "node setup.mjs", and for files named Math_Symbol.js or math_init.js.
- Look for gh-token-monitor files on your machine (~/.local/bin/gh-token-monitor.sh, a LaunchAgent or a systemd user service). If you find them, stop and remove the service before revoking any GitHub token, because revoking first triggers the attacker's command.
- Before opening a repository, check .claude/settings.json and .vscode/tasks.json for unfamiliar entries that run setup.mjs automatically.
- In your own or your organisation's GitHub accounts, search for repositories described as "Shai-Hulud: Here We Go Again", commits authored by claude@users.noreply, and workflows containing toJSON(secrets).
Other (outside the waves above):1 OSV records · 1 packages · 1 versions
Download the data
- Affected packages (CSV) 2,196 rows (one row per package within an OSV record, so more rows than OSV records), copied from our OSV snapshot: package, version, OSV ID, date, wave
- Indicators of compromise (CSV) 237 rows, each with its source URL
- Full index (JSON, the same data the checker uses)
The package list comes from OSV (osv.dev); each record keeps the license of its original source. Indicators are compiled from public reports. Verify before blocking or alerting on them.
Indicators of compromise
(237)
Only indicators published in public reports, each with its source. Domains and URLs are defanged with [.] to prevent accidental clicks.
| Type | Indicator | What it is | Attack | |
|---|---|---|---|---|
| Hash | 46faab8ab153fae6e80e7cca38eab363075bb524edd79e42269217a083628f09 | bundle.js SHA-256 (published by Wiz, StepSecurity, Socket and Unit 42) | Attack 1 | [58] |
| Hash | de0e25a3e6c1e1e5998b306b7141b3dc4c0088da9d7bb47c1c00c91e6e4f85d6 | bundle.js SHA-256 (published by Semgrep) | Attack 1 | [22] |
| Hash | 81d2a004a1bca6ef87a1caf7d0e0b355ad1764238e40ff6d1b1cb77ad4f595c3 | bundle.js SHA-256 (published by Semgrep) | Attack 1 | [22] |
| Hash | 83a650ce44b2a9854802a7fb4c202877815274c129af49e6c2d1d5d5d55c501e | bundle.js SHA-256 (published by Semgrep) | Attack 1 | [22] |
| Hash | 4b2399646573bb737c4969563303d8ee2e9ddbd1b271f1ca9e35ea78062538db | bundle.js SHA-256 (published by Semgrep) | Attack 1 | [22] |
| Hash | dc67467a39b70d1cd4c1f7f7a459b35058163592f4a9e8fb4dffcbba98ef210c | bundle.js SHA-256 (published by Semgrep) | Attack 1 | [22] |
| Hash | b74caeaa75e077c99f7d44f46daaf9796a3be43ecf24f2a1fd381844669da777 | bundle.js SHA-256 (published by Semgrep) | Attack 1 | [22] |
| URL | webhook[.]site/bb8ca5f6-4175-45d2-b042-fc9ebb8170b7 | exfiltration URL (defanged) | Attack 1 | [58] |
| GitHub workflow | .github/workflows/shai-hulud-workflow.yml | malicious workflow that sends out repo secrets on every push | Attack 1 | [51] |
| File name | bundle.js | malicious payload run by postinstall | Attack 1 | [31] |
| File name | data.json | stolen data in the "Shai-Hulud" repo | Attack 1 | [31] |
| File path | /tmp/processor.sh | script dropped during the attack | Attack 1 | [58] |
| File path | /tmp/migrate-repos.sh | script that copies private repos to public ones | Attack 1 | [58] |
| GitHub repo | Shai-Hulud | public repo in the victim's account holding stolen data | Attack 1 | [58] |
| GitHub repo | -migration | name suffix on private repos that were made public | Attack 1 | [58] |
| Other | Shai-Hulud Migration | description text on repos that were made public | Attack 1 | [58] |
| Other | Shai-Hulud Repository | GitHub search term suggested by Semgrep | Attack 1 | [22] |
| Other | Shai-Hulud-Migration | GitHub search term suggested by Semgrep | Attack 1 | [22] |
| File name | setup_bun.js | loader run at install time | Attack 2 | [53] |
| File name | bun_environment.js | main obfuscated payload | Attack 2 | [53] |
| Install script | "preinstall": "node setup_bun.js" | install script injected into package.json | Attack 2 | [53] |
| File name | cloud.json | exfiltrated data file (listed by Wiz) | Attack 2 | [57] |
| File name | contents.json | exfiltrated data file (listed by Wiz) | Attack 2 | [57] |
| File name | environment.json | exfiltrated data file (environment) | Attack 2 | [57] |
| File name | truffleSecrets.json | exfiltrated data file (TruffleHog findings) | Attack 2 | [57] |
| File name | actionsSecrets.json | exfiltrated data file (Actions secrets); also in the Golden Path strain | Attack 2 | [57] |
| File name | system.json | exfiltrated data file (listed by StepSecurity) | Attack 2 | [53] |
| File name | secrets.json | exfiltrated data file (listed by StepSecurity) | Attack 2 | [53] |
| File name | npm.json | exfiltrated data file (listed by StepSecurity) | Attack 2 | [53] |
| File path | $HOME/.dev-env/ | install location of the rogue self-hosted runner | Attack 2 | [53] |
| Process / service | SHA1HULUD | name of the rogue self-hosted GitHub Actions runner | Attack 2 | [53] |
| GitHub workflow | .github/workflows/discussion.yaml | persistence workflow (named "Discussion Create") | Attack 2 | [57] |
| GitHub workflow | .github/workflows/formatter_123456789.yml | malicious workflow (listed by Wiz only) | Attack 2 | [57] |
| Other | Sha1-Hulud: The Second Coming | description of exfiltration repositories (Datadog's version ends with a period) | Attack 2 | [53] |
| Other | Sha1-Hulud: The Continued Coming | repository description in a possible second phase | Attack 2 | [57] |
| Other | # Free AI at api.airforce | later repository description | Attack 2 | [57] |
| GitHub repo | [0-9a-z]{18} | random name pattern of exfiltration repositories | Attack 2 | [21] |
| Other | GET /-/whoami | npm API call to identify the stolen token's account | Attack 2 | [21] |
| Other | GET /-/v1/search?text=maintainer%3A[username]&size=100 | npm API call to list the victim's packages (up to 100) | Attack 2 | [21] |
| Hash | a3894003ad1d293ba96d77881ccd2071446dc3f65f434669b49b3da92421901a | SHA256 of setup_bun.js | Attack 2 | [21] |
| Hash | 62ee164b9b306250c1172583f138c9614139264f889fa99614903c12755468d0 | SHA256 of bun_environment.js | Attack 2 | [21] |
| Hash | cbb9bc5a8496243e02f3cc080efbe3e4a1430ba0671f2e43a202bf45b05479cd | SHA256 of bun_environment.js | Attack 2 | [21] |
| Hash | f099c5d9ec417d4445a0328ac0ada9cde79fc37410914103ae9c609cbc0ee068 | SHA256 of bun_environment.js | Attack 2 | [21] |
| Hash | d60ec97eea19fffb4809bc35b91033b52490ca11 | SHA1 of bun_environment.js (Socket gives it for the Maven artifact too) | Attack 2 | [57] |
| Hash | 3d7570d14d34b0ba137d502f042b27b0f37a59fa | SHA1 of bun_environment.js | Attack 2 | [57] |
| Hash | d1829b4708126dcc7bea7437c04d1f10eacd4a16 | SHA1 of setup_bun.js | Attack 2 | [57] |
| Other | org.mvnpm:posthog-node:4.18.1 | infected artifact mirrored to Maven Central | Attack 2 | [30] |
| Other | @vietmoney/react-big-calendar | npm package carrying the Golden Path strain (version unconfirmed) | Attack 2 | [39] |
| File name | bun_installer.js | Golden Path loader | Attack 2 | [39] |
| File name | environment_source.js | Golden Path payload | Attack 2 | [39] |
| File name | 3nvir0nm3nt.json | Golden Path exfiltrated data file | Attack 2 | [39] |
| File name | cl0vd.json | Golden Path exfiltrated data file | Attack 2 | [39] |
| File name | c9nt3nts.json | Golden Path data file (spelled c0nt3nts.json in the code; unconfirmed) | Attack 2 | [39] |
| File name | pigS3cr3ts.json | Golden Path exfiltrated data file | Attack 2 | [39] |
| Other | Goldox-T3chs: Only Happy Girl | Golden Path exfiltration repository description | Attack 2 | [39] |
| File name | setup.mjs | SAP-wave install script / loader | Attack 3 | [16] |
| File name | execution.js | SAP-wave obfuscated payload | Attack 3 | [16] |
| File path | .vscode/tasks.json | Persistence task that runs when the folder is opened | Attack 3 | [16] |
| File path | .claude/setup.mjs | Persistence script launched by tasks.json | Attack 3 | [16] |
| Install script | "preinstall": "node setup.mjs" | Install hook in malicious SAP versions | Attack 3 | [16] |
| File path | lightning/_runtime/start.py | Lightning payload launcher | Attack 3 | [25] |
| File path | lightning/_runtime/router_runtime.js | Lightning obfuscated payload | Attack 3 | [25] |
| File path | .claude/router_runtime.js | Payload copy planted in repositories (Lightning wave) | Attack 3 | [25] |
| File path | .claude/settings.json | Abused Claude Code config (persistence) | Attack 3 | [25] |
| GitHub workflow | .github/workflows/format-check.yml | Workflow planted in the Lightning wave | Attack 3 | [25] |
| Hash | 5f5852b5f604369945118937b058e49064612ac69826e0adadca39a357dfb5b1 | SHA256 of router_runtime.js | Attack 3 | [25] |
| Hash | f1b3e7b3eec3294c4d6b5f87854a52471f03997f | SHA1 of router_runtime.js | Attack 3 | [25] |
| Hash | 40d0f21b64ec8fb3a7a1959897252e09 | MD5 of router_runtime.js | Attack 3 | [25] |
| Hash | 8046a11187c135da6959862ff3846e99ad15462d2ec8a2f77a30ad53ebd5dcf2 | SHA256 of start.py | Attack 3 | [37] |
| URL | zero[.]masscan[.]cloud:443/v1/telemetry | Lightning exfiltration endpoint (news report only, unconfirmed) | Attack 3 | [33] |
| Commit author | "claude" <claude@users.noreply.github.com> | Commit author posing as Claude Code | Attack 3 | [25] |
| Other | A Mini Shai-Hulud has Appeared | Description of exfiltration repositories (SAP/Lightning) | Attack 3 | [41] |
| File name | router_init.js | Obfuscated payload, about 2.3 MB | Attack 4 | [4] |
| Domain | filev2[.]getsession[.]org | Exfiltration domain (official advisory) | Attack 4 | [4] |
| Install script | @tanstack/setup (optionalDependencies) | Injected dependency | Attack 4 | [4] |
| File path | .vscode/setup.mjs | File planted in the @antv wave | Attack 5 | [17] |
| File path | .claude/index.js | Payload planted in the @antv wave | Attack 5 | [17] |
| File path | ~/.claude/package/index.js | Payload copy in the home folder | Attack 5 | [17] |
| Process / service | kitty-monitor.service | Linux persistence service | Attack 5 | [17] |
| File name | com.user.kitty-monitor.plist | macOS persistence LaunchAgent | Attack 5 | [17] |
| File path | ~/.local/share/kitty/cat.py | Script run by the persistence service | Attack 5 | [17] |
| File path | ~/.local/bin/gh-token-monitor.sh | Script that checks stolen GitHub tokens every 60 seconds | Attack 5 | [17] |
| File path | /var/tmp/.gh_update_state | Malware state file | Attack 5 | [17] |
| Hash | a68dd1e6a6e35ec3771e1f94fe796f55dfe65a2b94560516ff4ac189390dfa1c | SHA256 of the @antv payload | Attack 5 | [17] |
| Install script | bun run index.js | preinstall hook in malicious @antv versions | Attack 5 | [17] |
| Install script | bun run index.js && exit 1 | prepare script of the @antv/setup dependency | Attack 5 | [17] |
| Domain | t[.]m-kosche[.]com | @antv exfiltration domain | Attack 5 | [24] |
| URL | hxxps://t[.]m-kosche[.]com:443/api/public/otel/v1/traces | Exfiltration endpoint disguised as OpenTelemetry | Attack 5 | [24] |
| Other | Shai-Hulud: Here We Go Again | Description of exfiltration repositories (@antv) | Attack 5 | [24] |
| File path | results/results-*.json | Files inside exfiltration repositories | Attack 5 | [24] |
| GitHub repo | sayyadina-stillsuit-852 | Example Dune-themed exfiltration repository name | Attack 5 | [24] |
| GitHub workflow | .github/workflows/codeql.yml | Secret-dumping workflow named "Run Copilot" | Attack 5 | [17] |
| Hash | 1916faa365f2788b6e193514872d51a242876569 | Imposter commit in antvis/G2 | Attack 5 | [17] |
| Hash | 7cb42f57561c321ecb09b4552802ae0ac55b3a7a | Imposter commit in antvis/G2 | Attack 5 | [17] |
| Hash | dc3d62a2181beb9f326952a2d212900c94f2e13d | Imposter commit in antvis/G2 | Attack 5 | [17] |
| Domain | 87e0bbc636999b[.]lhr[.]life | Copycat C2 server | Attack 5 | [49] |
| Domain | b94b6bcfa27554[.]lhr[.]life | Copycat C2 server | Attack 5 | [49] |
| Domain | edcf8b03c84634[.]lhr[.]life | Copycat C2 server | Attack 5 | [49] |
| IP | 80[.]200[.]28[.]28:2222 | Copycat C2 server | Attack 5 | [49] |
| Other | A Mini Sha1-Hulud has Appeared | Copycat exfiltration repository description | Attack 5 | [49] |
| Commit author | claude <claude@users.noreply.github.com> | Spoofed author of malicious commits | Attack 6 | [13] |
| Commit author | dependabot[bot] | Spoofed bot identity | Attack 6 | [11] |
| Commit author | renovate[bot] | Spoofed bot identity | Attack 6 | [11] |
| Commit author | github-actions[bot] | Spoofed bot identity | Attack 6 | [11] |
| File path | tools/setup | Dropper location | Attack 6 | [13] |
| File path | .github/scripts/precheck | Dropper location | Attack 6 | [13] |
| Install script | "preinstall": "./tools/setup" | Install hook that runs the dropper | Attack 6 | [13] |
| File path | /var/run/secrets/kubernetes.io/serviceaccount/token | Kubernetes token path swept | Attack 6 | [11] |
| File name | format-results.txt | Workflow artifact holding dumped secrets | Attack 6 | [13] |
| URL | hxxp://127[.]0[.]0[.]1:8738 | Local wallet-theft listener | Attack 6 | [13] |
| Other | /api/agent | Command-and-control path on Tor hidden service | Attack 6 | [13] |
| File name | q2.bpf.c | Rootkit source file name | Attack 6 | [13] |
| Other | 0x7e28D9889f414B06c19a22A9Bd316f0AC279a4d6 | Operator's own Ethereum address (nearly empty) | Attack 6 | [13] |
| Other | weavedb-lite@0.1.1 | Malicious npm version | Attack 6 | [13] |
| Other | weavedb-sdk-base@0.21.1 | Malicious npm version | Attack 6 | [13] |
| Other | test-weavedb-sdk@1.1.1 | Malicious npm version | Attack 6 | [13] |
| Other | weavedb-warp-contracts-plugin-deploy@1.0.11 | Malicious npm version | Attack 6 | [13] |
| Other | arnext-arkb@0.0.2 | Malicious npm version | Attack 6 | [13] |
| Other | weavedb-console@0.2.1 | Malicious npm version | Attack 6 | [13] |
| Other | arnext@0.1.5 | Malicious npm version | Attack 6 | [13] |
| Other | roidjs@0.1.7 | Malicious npm version | Attack 6 | [13] |
| Other | weavedb-exm-sdk@0.7.4 | Malicious npm version | Attack 6 | [13] |
| Other | create-arnext-app@0.0.10 | Malicious npm version | Attack 6 | [13] |
| Other | weavedb-tools@0.45.3 | Malicious npm version | Attack 6 | [13] |
| Other | wdb-core@0.1.2 | Malicious npm version | Attack 6 | [13] |
| Other | cwao-tools@0.3.1 | Malicious npm version | Attack 6 | [13] |
| Other | test-ajs@0.1.19 | Malicious npm version | Attack 6 | [13] |
| Other | monade@0.0.7 | Malicious npm version | Attack 6 | [13] |
| Other | weavedb-exm-sdk-web@0.7.4 | Malicious npm version | Attack 6 | [13] |
| Other | testnpmnmp@1.0.21 | Malicious npm version | Attack 6 | [13] |
| Other | warp-contracts-plugin-deploy-test@3.0.1 | Malicious npm version | Attack 6 | [13] |
| Other | wdb-cli@0.1.1 | Malicious npm version | Attack 6 | [13] |
| Other | ai3@0.3.5 | Malicious npm version (matches OSV MAL-2026-4476) | Attack 6 | [10] |
| Other | cwao-units@0.8.3 | Malicious npm version | Attack 6 | [13] |
| Other | atomic-notes@0.5.3 | Malicious npm version | Attack 6 | [13] |
| Other | cwao@0.5.6 | Malicious npm version | Attack 6 | [13] |
| Other | weavedb-client@0.45.3 | Malicious npm version | Attack 6 | [13] |
| Other | wdb-sdk@0.1.2 | Malicious npm version | Attack 6 | [13] |
| Other | weavedb-offchain@0.45.4 | Malicious npm version | Attack 6 | [13] |
| Other | fpjson-lang@0.1.7 | Malicious npm version | Attack 6 | [13] |
| Other | weavedb-contracts@0.45.2 | Malicious npm version | Attack 6 | [13] |
| Other | weavedb-node-client@0.45.3 | Malicious npm version | Attack 6 | [13] |
| Other | arjson@0.1.4 | Malicious npm version | Attack 6 | [13] |
| Other | hbsig@0.3.2 | Malicious npm version | Attack 6 | [13] |
| Other | zkjson@0.8.5 | Malicious npm version | Attack 6 | [13] |
| Other | aonote@0.11.1 | Malicious npm version | Attack 6 | [13] |
| Other | weavedb-base@0.45.3 | Malicious npm version | Attack 6 | [13] |
| Other | weavedb-sdk-node@0.45.3 | Malicious npm version | Attack 6 | [13] |
| Other | wao@0.41.2 | Malicious npm version | Attack 6 | [13] |
| Other | weavedb-sdk@0.45.3 | Malicious npm version | Attack 6 | [13] |
| File path | /tmp/p<random>.js | npm loader temp file | Attack 7 | [18] |
| File path | /tmp/b-<random>/bun | downloaded Bun binary | Attack 7 | [18] |
| File path | /tmp/kitty-<random> | background process | Attack 7 | [18] |
| File path | /tmp/b-*/b.zip | Bun download archive | Attack 7 | [26] |
| File path | /tmp/b-*/bun.exe | Bun binary (Windows) | Attack 7 | [26] |
| File name | tmp.0987654321.lock | background-run lock file | Attack 7 | [26] |
| File name | *-setup.pth | Python startup hook | Attack 7 | [29] |
| File name | _index.js | main payload | Attack 7 | [29] |
| File path | /tmp/.bun_ran | run-once marker | Attack 7 | [29] |
| File path | %TEMP%\.bun_ran | run-once marker (Windows) | Attack 7 | [29] |
| File path | /tmp/b.zip | Bun download archive | Attack 7 | [29] |
| File path | /tmp/b/bun | Bun binary | Attack 7 | [29] |
| File path | /tmp/bun | Bun binary | Attack 7 | [5] |
| File path | /tmp/bun.zip | Bun download archive | Attack 7 | [5] |
| File name | langchain_core-setup.pth | split loader | Attack 7 | [27] |
| File name | ensmallen_haswell.abi3.so | trojanized compiled extension | Attack 7 | [27] |
| File name | ensmallen_core2.abi3.so | trojanized compiled extension | Attack 7 | [27] |
| File path | /tmp/.sshu-setup.js | SSH lateral-movement copy | Attack 7 | [27] |
| File path | /tmp/tmp.0144018410.lock | lock file | Attack 7 | [54] |
| File path | ~/.local/share/updater/update.py | hourly GitHub C2 poller | Attack 7 | [54] |
| File path | ~/.config/systemd/user/update-monitor.service | Linux persistence service | Attack 7 | [54] |
| File path | ~/.config/systemd/user/gh-token-monitor.service | token-monitor persistence service | Attack 7 | [54] |
| File path | ~/Library/LaunchAgents/com.user.update-monitor.plist | macOS persistence | Attack 7 | [54] |
| File path | ~/Library/LaunchAgents/com.user.gh-token-monitor.plist | macOS persistence | Attack 7 | [54] |
| File path | ~/.config/gh-token-monitor/token | stored stolen token | Attack 7 | [54] |
| File path | ~/Library/LaunchAgents/com.github.token-monitor.plist | macOS persistence (Socket's name) | Attack 7 | [29] |
| File path | .gemini/settings.json | AI-tool hook planted in repos | Attack 7 | [46] |
| Other | Miasma: The Spreading Blight | exfiltration repo description (npm) | Attack 7 | [18] |
| Other | Hades - The End for the Damned | exfiltration repo description (PyPI) | Attack 7 | [29] |
| Other | stygian, tartarean, cerberus, charon, styx, lethe, thanatos, persephone | exfiltration repo name words | Attack 7 | [29] |
| GitHub repo | thanatatos-<random> | exfiltration repo name (O3 spelling) | Attack 7 | [5] |
| GitHub repo | stygian-cerberus-[0-9]+ | exfiltration repo name pattern | Attack 7 | [54] |
| GitHub repo | tartarean-charon-[0-9]+ | exfiltration repo name pattern | Attack 7 | [54] |
| GitHub repo | lethean-tartarus-61322, abyssal-acheron-97481, charonian-phlegethon-92465, cimmerian-cerberus-93715, erebean-eidolon-54723, funereal-thanatos-2755, plutonian-erebus-24120, nekyian-charon-76242 | example exfiltration repo names | Attack 7 | [46] |
| Other | results/results-<timestamp>-<counter>.json | encrypted exfiltration file | Attack 7 | [29] |
| Other | IfYouInvalidateThisTokenItWillNukeTheComputerOfTheOwner | commit marker (npm) | Attack 7 | [26] |
| Other | IfYouYankThisTokenItWillNukeTheComputerOfTheOwnerFully | commit marker (Hades) | Attack 7 | [29] |
| Other | firedalazer | Python C2 channel name / keyword | Attack 7 | [54] |
| Other | actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd | pinned action in injected workflow | Attack 7 | [18] |
| GitHub workflow | .github/workflows/codeql-[random].yml | injected workflow | Attack 7 | [54] |
| GitHub workflow | Run Copilot | injected workflow name | Attack 7 | [29] |
| Other | format-results | exfiltration Actions artifact | Attack 7 | [29] |
| Other | setup-bun@0c5077e | injected workflow step | Attack 7 | [46] |
| Process / service | __IS_DAEMON=1 | daemon-mode environment variable | Attack 7 | [18] |
| Other | python-requests/2.31.0 | network User-Agent | Attack 7 | [26] |
| Other | Bun/1.3.13 | PyPI upload User-Agent | Attack 7 | [46] |
| Other | Bun/1.3.14 | PyPI upload User-Agent | Attack 7 | [27] |
| Hash | 88896d478986d453f5da79b311de39d9b4b1bea95c21af1d8ef181b0f4e52fe9 | @redhat-cloud-services/chrome 2.3.1 tarball | Attack 7 | [26] |
| Hash | 21b6409a7b84446310daca5409ad6112ac60a1e4bef97736e53fff5f63bfdef4 | chrome 2.3.1 index.js | Attack 7 | [26] |
| Hash | ee262510cb246d2b904991aee7fc61162bdae34463439ec6383bd5356479d362 | chrome 2.3.1 package.json | Attack 7 | [26] |
| Hash | ac2a2208e1726e008be6c73dc0872d9bba163319259dff1b62055ac933ca46b6 | Bun helper | Attack 7 | [26] |
| Hash | 0dc06ecdaa63fe24859cfd955053c23245c536e4733480239d14bebf12688e35 | decrypted main payload (same value from Socket and SafeDep) | Attack 7 | [18] |
| Hash | 031ba872d5a84bfb18115f432811e4b45180346a1bae653f7fd85f918e7bb3a3 | patch-client 4.0.4 tarball | Attack 7 | [18] |
| Hash | df1732f5bfec12e066be44dee02ec8a243e4868d38672c1b1d065359dd735a14 | patch-client 4.0.4 index.js | Attack 7 | [18] |
| Hash | dc48b09b2a5954f7ff79ab8a2fd80202bd3b59c08c7cdbc6025aa923cb4c0efe | _index.js variant 1 | Attack 7 | [29] |
| Hash | e1342a80d4b5e83d2c7c22e1e0aaa95f2d88e3dbf0d853a4994b180c93a4b17d | _index.js variant 2 | Attack 7 | [29] |
| Hash | c539766062555d47716f8432e73adbe3a0c0c954a0b6c4005017a668975e275c | *-setup.pth | Attack 7 | [29] |
| Hash | 6d332f814f15f19758d65026bbfd0a8c49671b319ec77b8fa1b27fc48afff7d9 | langchain_core_mcp-1.4.2 wheel | Attack 7 | [27] |
| Hash | 6506d31707a39949f89534bf9705bcf889f1ecae3dbc6f4ff88d67a8be3d01b2 | langchain_core-setup.pth | Attack 7 | [27] |
| Hash | 54dc7ea54a1317cca0e890a2770630cf7fa6c97813e0cb9d2caa93012b350668 | setup.mjs loader inside npm tarballs (first wave, SHA-256) | Attack 8 | [28] |
| Hash | fd3ca4007b225fdf8de7af4345a19179d5efa8c4bb9205f88cda806e5684b1eb | Later-wave / community-spread setup.mjs loader (SHA-256) | Attack 8 | [36] |
| Hash | 9fc2570b7cef51c1b8df116d144d11ff4096357be7d2c4c6367cfc2509cf1bcc | Math_Symbol.js / math_init.js second stage (SHA-256) | Attack 8 | [44] |
| Hash | 927387d0cfac1118df4b383decc2ea6ba49c9d2f98b47098bcbcba1efc026e1f | Malicious .vscode/tasks.json (SHA-256) | Attack 8 | [20] |
| Hash | 14eb4ce01dd4307759887ff819359b70d7d9ff709ecde039a5abc1aac325b128 | Malicious .claude/settings.json (SHA-256) | Attack 8 | [20] |
| Hash | d584f9b6af48b7ed1f93713944f033783bf149e1c25e1643eb8c0e9df5dc7782 | keyv-6.0.0.tgz (SHA-256) | Attack 8 | [23] |
| Hash | 37f9f847e9c3e520b47d83a9029e199dbc30c6a195a1d80467d0fb1a6fd5068728ad4d1a422995ca578359263afdac5d3b4fed7a6b9befad27de19cca6966952 | keyv-6.0.0.tgz (SHA-512, hex) | Attack 8 | [28] |
| Hash | sha512-N/n4R+nD5SC0fYOpAp4ZnbwwxqGVodgEZ9D7Gm/VBocorU0aQimVyleDWSY6/axdO0/temub760n3hnMppZpUg== | keyv@6.0.0 lockfile integrity string | Attack 8 | [15] |
| Hash | 35a672cf34b996b91f3e1c28cbf3a05a37e036e4 | Math_Symbol.js / math_init.js (40-character hash, apparently SHA-1; Wiz does not label the algorithm) | Attack 8 | [55] |
| Hash | 686aa40d0fc22c8d569494543a0f891f359f2f99 | setup.mjs in .claude (apparently SHA-1) | Attack 8 | [55] |
| Hash | f525d52ceb966516686b482d3dc0137028cc6a63 | setup.mjs in .vscode (apparently SHA-1) | Attack 8 | [55] |
| Domain | npm-cache[.]com | Attacker server domain (Aikido says it was registered 2026-05-22) | Attack 8 | [36] |
| URL | hxxps://npm-cache[.]com:443/router | Exfiltration URL | Attack 8 | [36] |
| IP | 104[.]21[.]35[.]216 | IP for npm-cache[.]com (Cloudflare) | Attack 8 | [55] |
| Domain | awqhnjewqjkl[.]icu | Attacker server domain | Attack 8 | [44] |
| Domain | pypi-get[.]com | Attacker server domain | Attack 8 | [55] |
| Domain | js-mirror[.]com | Attacker server domain | Attack 8 | [55] |
| Other | 0xE1f2395ee43e45A1556EC6438a88c31B83493103 | Ethereum smart contract used to look up attacker servers | Attack 8 | [44] |
| File name | Math_Symbol.js | Second stage in first-wave packages | Attack 8 | [36] |
| File name | math_init.js | Second stage in later infections | Attack 8 | [36] |
| File name | router_runtime.js | File in packages (listed by OX only) | Attack 8 | [48] |
| File path | ~/.config/gh-token-monitor/{token,handler,started_at} | Watcher config and stolen token | Attack 8 | [28] |
| File path | /tmp/tmp.dpkg_14527.lock | Single-instance lock file | Attack 8 | [14] |
| Process / service | com.user.gh-token-monitor | macOS LaunchAgent label | Attack 8 | [28] |
| Process / service | GitHub Token Validity Monitor | systemd service description | Attack 8 | [28] |
| Commit author | claude@users.noreply.github.com | Author of malicious commits (message: chore: update config) | Attack 8 | [36] |
| Other | thebeautifulmarchoftime | Malware marker string | Attack 8 | [48] |
| Other | IfYouBlockThisAPIKeyItWillCrashTheLiveProductionServersOfAllThirdPartyClients | Intimidation string meant to discourage token revocation | Attack 8 | [48] |
Why most waves have no CVE
These incidents are not bugs in the software itself but takeovers of the publishing pipeline, so they are usually tracked by OSV malicious-package IDs (MAL-) or GitHub advisories. Only a few have a CVE, such as TanStack (CVE-2026-45321) and SAP @cap-js (CVE-2026-46421). TanStack was separately added to CISA KEV; KEV requires evidence of exploitation, so having a CVE does not by itself put an entry there.
How the technique evolved
- 2025: started with postinstall scripts, TruffleHog secret hunting and public exfiltration repos; the second wave moved to preinstall, switched to Bun and added a home-directory wiper.
- 2026 (Mini Shai-Hulud): the focus moved to CI publishing pipelines and npm trusted publishing (OIDC), so malicious versions carry genuine provenance and signatures; persistence moved into AI-tool settings (
.claude/,.vscode/) and commits impersonate an AI assistant. - Other ecosystems: PyPI (
.pthstartup hooks), IronWorm rewritten in Rust, and the keyv wave that looks up its server through an Ethereum smart contract. - Attribution is uncertain: researchers link most 2026 waves to TeamPCP, but the attack code has leaked and copycats exist, so a given wave’s operator is not necessarily the same.
Key point: “signed, with provenance” only tells you which pipeline built a package, not that its contents are safe.
Data and method
The affected package list comes from OSV (the open source vulnerability database) records that mention this campaign (summary, details and reference links, pattern: sha[i1]-?hulud), from PlainCVE's snapshot of 2026-09-23. Waves are split by publication date. The numbers are records in OSV, not the real number of victims; research firms count differently and report different numbers.
Verification Reviewed and checked against sources; not yet reproduced in our lab
| Reviewed | 2026-09-24 |
|---|
Verification records describe the environment and the result only, never reproduction steps or code that could attack other people’s systems. See our policy.
Sources
- AdvisoryWidespread Supply Chain Compromise Impacting npm Ecosystem · CISA, 2025-09-23 · accessed 2026-09-23
- AdvisoryTrojanized pantheon-agents 0.6.1 and 0.6.2 on PyPI ship a credential stealer · GitLab Advisory Database, 2026-08-26 · accessed 2026-09-23
- VendorOur plan for a more secure npm supply chain · GitHub, 2025-09-22 · accessed 2026-09-23
- AdvisoryGHSA-g7cv-rxg3-hmpx: Malware in 42 @tanstack/* packages · TanStack (GitHub), 2026-05-11 · accessed 2026-09-23
- Research5 Malicious PyPI Packages Found Stealing Credentials via Hidden .pth Files (Miasma Campaign) · O3 Security, 2026-06-08 · accessed 2026-09-23
- ResearchTeamPCP Compromises AntV and 322 Other NPM Packages · OpenSourceMalware, 2026-05-20 · accessed 2026-09-23
- CVE / NVD / OSVOSV: open source vulnerability database (malicious package records) · OpenSSF / Google · accessed 2026-09-23
- AdvisoryGHSA-93qj-5q5v-3c2h: Trojanized pantheon-agents 0.6.1 and 0.6.2 on PyPI · OSV, 2026-08-26 · accessed 2026-09-23
- AdvisoryMAL-2026-11524: Malicious code in keyv (npm) · OSV / OpenSSF malicious-packages, 2026-08-04 · accessed 2026-09-23
- AdvisoryMAL-2026-4476: Malicious code in ai3 (npm) · OSV / OpenSSF Malicious Packages, 2026-05-26 · accessed 2026-09-23
- VendorIronWorm (No CVE): Rust-Built npm Worm Ships an eBPF Rootkit, Tor C2, and a Self-Propagating Supply Chain Implant Across 37 Packages · Phoenix Security, 2026-06-04 · accessed 2026-09-23
- VendorPost-mortem of Shai-Hulud attack on November 24th, 2025 · PostHog, 2025-11-26 · accessed 2026-09-23
- ResearchIronWorm: Shai-Hulud's rustier cousin · JFrog Security Research, 2026-06-03 · accessed 2026-09-23
- ResearchMajor Shai Hulud campaign strikes npm again, affecting keyv and 400+ packages · JFrog Security Research, 2026-08-04 · accessed 2026-09-23
- Researchkeyv and cacheable npm compromise: 400+ packages · SafeDep, 2026-08-04 · accessed 2026-09-23
- ResearchMini Shai Hulud and SAP Compromise · SafeDep, 2026-04-29 · accessed 2026-09-23
- ResearchMini Shai-Hulud Strikes Again: 317 npm Packages Compromised · SafeDep, 2026-05-19 · accessed 2026-09-23
- ResearchMini Shai-Hulud "Miasma: The Spreading Blight" Hits @redhat-cloud-services · SafeDep, 2026-06-01 · accessed 2026-09-23
- VendorIronWorm — Campaign — SafeDep Threat Intelligence · SafeDep · accessed 2026-09-23
- Research'ChainDrop' worm compromises hundreds of popular npm packages · Datadog Security Labs, 2026-08-04 · accessed 2026-09-23
- ResearchThe Shai-Hulud 2.0 npm worm: analysis, and what you need to know · Datadog Security Labs, 2025-11-25 · accessed 2026-09-23
- ResearchSecurity Advisory: NPM packages using secret scanning tools to steal credentials · Semgrep, 2025-09-15 · accessed 2026-09-23
- ResearchInside the keyv npm Compromise: preinstall Malware, Trusted Provenance, and IDE Hooks · Snyk, 2026-08-04 · accessed 2026-09-23
- ResearchMini Shai-Hulud Hits @antv Ecosystem, 639 Compromised npm Package Versions · Socket, 2026-05-19 · accessed 2026-09-23
- ResearchPyTorch Lightning PyPI Package Compromised in Supply Chain Attack · Socket, 2026-04-30 · accessed 2026-09-23
- ResearchMini Shai-Hulud Campaign Hits Red Hat Cloud Services npm Packages · Socket, 2026-06-01 · accessed 2026-09-23
- ResearchMini Shai-Hulud, Miasma, and Hades Worms Target Bioinformatics and MCP Developers via Malicious PyPI Wheels · Socket, 2026-06-08 · accessed 2026-09-23
- ResearchPopular npm Packages in the keyv and Cacheable Namespaces Compromised in Active Supply Chain Attack · Socket, 2026-08-04 · accessed 2026-09-23
- ResearchShai-Hulud Descends to Hades: Miasma Worm Campaign Spreads with New PyPI Wave · Socket, 2026-06-07 · accessed 2026-09-23
- ResearchShai Hulud Strikes Again (v2) · Socket, 2025-11-24 · accessed 2026-09-23
- ResearchTinycolor supply chain attack affects 40 packages · Socket, 2025-09-15 · accessed 2026-09-23
- NewsShai-Hulud v2 Spreads From npm to Maven, as Campaign Exposes Thousands of Secrets · The Hacker News, 2025-11-26 · accessed 2026-09-23
- NewsPyTorch Lightning and Intercom-client Hit in Supply Chain Attacks to Steal Credentials · The Hacker News, 2026-04-30 · accessed 2026-09-23
- NewsIronWorm and New Miasma Worm Variant Hit npm in Supply Chain Attacks · The Hacker News, 2026-06-05 · accessed 2026-09-23
- Research"Shai-Hulud" Worm Compromises npm Ecosystem in Supply Chain Attack · Palo Alto Networks Unit 42, 2025-11-25 · accessed 2026-09-23
- ResearchKeyv and friends compromised in active Shai-Hulud supply chain attack · Aikido Security, 2026-08-04 · accessed 2026-09-23
- ResearchPopular PyTorch Lightning Package Compromised by Mini Shai-Hulud · Aikido Security, 2026-04-30 · accessed 2026-09-23
- ResearchS1ngularity/nx attackers strike again · Aikido Security, 2025-09-16 · accessed 2026-09-23
- ResearchShai Hulud strikes again - The golden path · Aikido Security, 2025-12-28 · accessed 2026-09-23
- ResearchShai Hulud Launches Second Supply-Chain Attack: Zapier, ENS, AsyncAPI, PostHog, Postman Compromised · Aikido Security, 2025-11-24 · accessed 2026-09-23
- NewsOfficial SAP npm packages compromised to steal credentials · BleepingComputer, 2026-04-29 · accessed 2026-09-23
- ResearchThe keyv and cacheable npm Supply Chain Attack: Inside the Mini Shai-Hulud Campaign · Chainguard, 2026-08-04 · accessed 2026-09-23
- AdvisoryOngoing npm Supply Chain Attack Affecting Keyv and Related Packages ("Shai-Hulud" Worm) · Cyber Security Agency of Singapore, 2026-08-06 · accessed 2026-09-23
- ResearchShai-Hulud strikes again: CHAINDROP worm hits 400+ npm packages · Elastic Security Labs, 2026-08-06 · accessed 2026-09-23
- ResearchNPM Malware Compromises keyv and cacheable with 500M+ Weekly Downloads and Spreads to Hundreds of Packages · Endor Labs, 2026-08-04 · accessed 2026-09-23
- ResearchShai-Hulud "Hades" Wave Hits Six PyPI Bioinformatics Packages via Stolen Tokens · Endor Labs, 2026-06-08 · accessed 2026-09-23
- AdvisoryVU#534320 NPM supply chain compromise exposes challenges to securing the ecosystem from credential theft and self-propagation · CERT/CC, 2025-09-29 · accessed 2026-09-23
- ResearchA Massive Shai-Hulud Campaign Hits npm: +440 Packages Compromised, Over 2B Monthly Downloads · OX Security, 2026-08-04 · accessed 2026-09-23
- ResearchNew Actors Deploy Shai-Hulud Clones: TeamPCP Copycats Are Here · OX Security, 2026-05-17 · accessed 2026-09-23
- ResearchAnother Shai-hulud npm worm is spreading: here's what you need to know · ReversingLabs, 2025-12-09 · accessed 2026-09-23
- Researchctrl/tinycolor and 40+ NPM packages compromised · StepSecurity, 2025-09-15 · accessed 2026-09-23
- ResearchTeamPCP's Mini Shai-Hulud Is Back · StepSecurity, 2026-05-11 · accessed 2026-09-23
- ResearchSha1-Hulud: The Second Coming – Zapier, ENS Domains, and Other Prominent NPM Packages Compromised · StepSecurity, 2025-11-23 · accessed 2026-09-23
- ResearchThe Hades Campaign: Graph ML PyPI Packages Deploy Cross-Platform Memory Scrapers, AI Analyst Misdirection, and a Wiper Deterrent · StepSecurity, 2026-06-08 · accessed 2026-09-23
- Researchkeyv and cacheable npm Package Hijacked in Supply Chain Attack · Wiz, 2026-08-04 · accessed 2026-09-23
- ResearchMini Shai-Hulud Strikes Again: TanStack + more npm Packages Compromised · Wiz, 2026-05-11 · accessed 2026-09-23
- ResearchShai-Hulud 2.0 Supply Chain Attack · Wiz, 2025-11-24 · accessed 2026-09-23
- ResearchShai-Hulud npm supply chain attack · Wiz, 2025-09-16 · accessed 2026-09-23
How to cite this page
This article is CC BY 4.0. Please keep the attribution and link when republishing.
PlainCVE Team (2026). "Shai-Hulud: the self-spreading worm campaign on npm and PyPI". PlainCVE. https://plaincve.date/en/campaigns/shai-hulud (accessed YYYY-MM-DD)BibTeX
@misc{shaihulud2026,
title = {Shai-Hulud: the self-spreading worm campaign on npm and PyPI},
author = {PlainCVE Team},
year = {2026},
howpublished = {PlainCVE},
url = {https://plaincve.date/en/campaigns/shai-hulud},
note = {Updated 2026-09-24}
}