Shai-Hulud showed that a single stolen npm publishing token is enough to start a worm: each infected install harvests more tokens, and each token republishes more packages. Across waves in September 2025, November 2025 and the 2026 Mini Shai-Hulud campaign, the malware stole GitHub, npm and cloud credentials from developer laptops and CI runners and used them to spread. This guide profiles the worm family: names and waves, who is behind the later variant, techniques mapped to MITRE ATT&CK v20, how it abuses non-human credentials, and the detection and containment steps that work. It is written for platform, AppSec, DevOps and identity teams; it goes deeper than the Malicious Package and Dependency Confusion Guide, which covers package threats in general.
Key takeaways
- The worm spreads on identities, not exploits. Shai-Hulud authenticates to npm as the victim with a stolen token and republishes the victim's other packages, so every compromised maintainer becomes a new source.
- Install time is execution time. The first wave used a postinstall script; the second moved to preinstall with a fake Bun installer, so simply installing a dependency ran the payload on laptops and CI runners.
- CI runners are the richest target. Runners hold publishing tokens, cloud keys and OIDC identities, and Mini Shai-Hulud read runner memory to recover short-lived tokens and publish through trusted publishing.
- Containment means rotating everything reachable. CISA's advice was to rotate all developer credentials, not only npm tokens, and to check for new repositories, workflows and self-hosted runners created by the worm.
- Trusted publishing helps but is not the end state. Removing stored tokens closes the first-wave path; tightly scoped OIDC, disabled install scripts in CI and release approval close the later ones.
Who they are (names and aliases)
Shai-Hulud is malware, not a named group. MITRE ATT&CK tracks the original worm as software S9008, first reported in September 2025, and the derivative Mini Shai-Hulud as S9043, which it attributes to the group TeamPCP (G1056). No public source has attributed the 2025 waves to a named actor.
| Name or ID | Period | What it refers to |
|---|---|---|
| Shai-Hulud (S9008), first wave | From 15 September 2025 | Postinstall worm; widely reported starting with @ctrl/tinycolor; GitHub removed more than 500 packages |
| Sha1-Hulud: The Second Coming, also called Shai-Hulud 2.0 | From 24 November 2025 | Preinstall variant with setup_bun.js and bun_environment.js; exfiltration repositories carry that description |
| Mini Shai-Hulud (S9043) | 2026, including SAP packages on 29 April 2026 | Derived credential stealer and worm used by TeamPCP; targets CI workflows, OIDC tokens and AI tool configuration |
| TeamPCP (G1056) | 2026 | Group ATT&CK links to Mini Shai-Hulud. See the TeamPCP Threat Actor Profile Guide |
Copycat worms reuse the code and the name. Treat the label in a report as a starting point and check indicators before assuming a known variant.
Targeting and motivation
- Who is hit: package maintainers and anyone who installs their packages. The worm's victims are defined by the dependency graph, not by sector.
- What it wants: credentials. CISA lists GitHub personal access tokens and API keys for AWS, Google Cloud and Microsoft Azure; ATT&CK adds .npmrc tokens, environment variables, cloud instance metadata and secrets from AWS Secrets Manager, Google Cloud Secret Manager and Azure Key Vault.
- Why publishing tokens matter most: a publishing token is both loot and the propagation mechanism. Every token with publish rights to popular packages multiplies reach.
- Destruction as leverage: the second wave tried to overwrite and delete the user's home directory if it could not steal credentials or exfiltrate; Mini Shai-Hulud carries a wiper that ATT&CK says fires only under specific location conditions, including Iran and Israel, behind a random one-in-six gate.
- AI tooling: Mini Shai-Hulud modified AI coding agent configuration files to create execution hooks. See the AI IDE and Coding Agent Security Guide.
Tactics and techniques (MITRE ATT&CK)
IDs are ATT&CK v20 relationships recorded against S9008 and S9043.
| Tactic | Technique | Behaviour | Primary defence |
|---|---|---|---|
| Initial access | T1195.001 Compromise Software Dependencies and Development Tools | Trojanised package versions published from compromised maintainer accounts | Release cooldown before adopting new versions; lockfiles |
| Persistence and execution | T1546.016 Installer Packages; T1059.007 JavaScript | postinstall (wave one) and preinstall (wave two) lifecycle hooks | Disable install scripts in CI; allowlist the few that need them |
| Stealth | T1036.005 Match Legitimate Resource Name or Location; T1678 Delay Execution | Payload masquerades as a Bun installer and forks into a detached background process | Alert on package installs downloading runtimes |
| Credential access | T1552.001 Credentials In Files; T1552.005 Cloud Instance Metadata API; T1555.006 Cloud Secrets Management Stores; T1528 Steal Application Access Token | .npmrc, environment variables, AWS and GCP metadata endpoints, cloud secret stores; TruffleHog run against the file system | No long-lived secrets on runners; IMDSv2 and metadata restrictions; short-lived credentials |
| Credential access | T1003.007 Proc Filesystem (S9043) | Runner process memory scraped for short-lived identity tokens, then exchanged for npm trusted-publisher tokens | Separate release jobs from jobs that install third-party code |
| Lateral movement | T1550.001 Application Access Token; T1021.007 Cloud Services (S9043) | Stolen npm tokens used to enumerate and republish packages; AWS SSM Send-Command to reach EC2 | Granular, single-package tokens; restrict SSM permissions |
| Execution in pipelines | T1677 Poisoned Pipeline Execution | Malicious .github/workflows/discussion.yaml; infected host registered as a self-hosted runner named SHA1HULUD | Branch protection; review workflow changes; alert on new runners |
| Persistence (S9043) | T1543.001 Launch Agent; T1053.006 Systemd Timers; T1546 Event Triggered Execution | gh-token-monitor daemon polling GitHub every 60 seconds; hooks in AI agent configuration | Endpoint monitoring of user launch agents and systemd user units |
| Exfiltration | T1567.001 Exfiltration to Code Repository; T1567.004 Exfiltration Over Webhook | Public repository named Shai-Hulud (or a generated name) under the victim's account; double Base64 encoded JSON; webhook.site | Block webhook.site; alert on new public repositories |
| Persistence and impact | T1098 Account Manipulation; T1485 Data Destruction | Private repositories switched to public; home directory wipe fallback | Org policy blocking visibility changes; backups of developer workstations |
How they abuse identities and credentials
Shai-Hulud is the clearest example so far of a worm that propagates through non-human identities. Each credential type plays a different role.
| Credential | How the worm uses it | Control that breaks it |
|---|---|---|
| npm publishing token (classic or granular) | Republish the owner's packages with the payload | Trusted publishing; "Require two-factor authentication and disallow tokens" in package settings |
| GitHub personal access token | Create exfiltration repositories, push workflows, flip repository visibility, register runners | Fine-grained PATs scoped to named repositories; short expiry; org approval. See the GitHub Personal Access Tokens Security Guide |
| CI OIDC identity | Mint short-lived publish tokens from inside a compromised job (Mini Shai-Hulud) | Bind trust to a specific workflow, branch and protected environment with required reviewers |
| Cloud access keys and instance roles | Read secret stores; move to EC2 through SSM | Workload identity federation; least privilege; no secret store read on build roles |
| Bot and automation tokens | Entry point: NHIMG's breach analysis of the second wave describes a bot PAT stolen through a pull_request_target workflow | Never run untrusted pull request code with secrets; minimise bot scopes |
The common thread is long-lived or broadly scoped credentials sitting where third-party code runs. See the npm Tokens Security Guide and the CI/CD Pipeline Identity Security Guide.
Notable incidents
- First wave, September 2025: GitHub was notified on 14 September; trojanised versions starting with @ctrl/tinycolor followed, CISA issued an alert on 23 September and GitHub removed more than 500 packages and blocked uploads matching the malware's indicators. Unit 42 notes initial access may have come from phishing that impersonated npm. See the Shai-Hulud first wave analysis.
- Second wave, November 2025: Unit 42 counted more than 25,000 malicious repositories across about 350 users and assessed with moderate confidence that a large language model generated part of the bash tooling. The Shai-Hulud campaign that reached 25,000+ GitHub repositories breach page covers the CI-heavy infection pattern and npm's revocation of classic tokens in December 2025.
- Mini Shai-Hulud, 2026: SAP Cloud Application Programming Model packages were compromised on 29 April 2026, with stolen data encrypted and exfiltrated through public GitHub repositories, followed by further waves. The Mini Shai-Hulud SAP packages breach page shows how attacker code inside a pipeline could publish through trusted publishing.
- Platform response: GitHub announced that npm publishing would narrow to local publishing with mandatory 2FA, granular tokens limited to seven days and trusted publishing, with classic tokens and TOTP 2FA deprecated in favour of FIDO-based 2FA.
Detection and defence
What to look for
- GitHub audit log: repositories created with the name Shai-Hulud or the description "Sha1-Hulud: The Second Coming", repository visibility changes from private to public, new workflow files such as discussion.yaml, and self-hosted runners registered with unexpected names such as SHA1HULUD. See the GitHub Audit Log Detection Guide.
- npm registry: new versions of your packages published outside the release workflow, or by a token rather than trusted publishing.
- Build and endpoint telemetry: npm install spawning curl or PowerShell to fetch Bun, node processes running TruffleHog, outbound requests to webhook.site, and on CI runners, processes reading other processes' memory.
- Cloud logs: secret store reads (GetSecretValue and equivalents) and SSM Send-Command calls from build identities that do not normally make them.
- Developer hosts: new user launch agents or systemd user services such as gh-token-monitor, and unexpected edits to AI agent and editor configuration.
Containment steps after exposure
- Identify affected versions in lockfiles, including nested dependencies, and in artifact caches and proxies, as CISA advised.
- Pin to known good versions and rebuild from clean runners.
- Rotate every credential reachable from affected hosts and runners: npm, GitHub, cloud, Kubernetes, CI, SSH keys and third-party API keys. Revoke before re-issuing.
- Remove worm artefacts: exfiltration repositories, injected workflows, rogue self-hosted runners, unexpected GitHub Apps and OAuth apps, and restore repository visibility.
- Check publish history for every package the exposed identities could publish and deprecate malicious versions.
- Monitor exposed credentials for reuse for at least 30 days. The Malicious Package Worm Response Playbook sets out roles and evidence.
Prevention, in rollout order
- Move publishing to trusted publishing on GitHub Actions, GitLab CI/CD or CircleCI cloud (npm CLI 11.5.1 or later; self-hosted runners are not supported), then set packages to require 2FA and disallow tokens. Consider allowing only npm stage publish so a maintainer approves each release. See the npm Trusted Publishing and Provenance Guide.
- Disable lifecycle scripts in CI with ignore-scripts and use npm ci against a committed lockfile; test on one pipeline first and allowlist packages that genuinely need build scripts. Roll back per project if a native module fails to build. See the npm Install-Time Security Guide.
- Add a release cooldown in your registry proxy so brand-new versions are not installed for a set period; most waves were removed within hours to days.
- Split jobs: dependency install and test jobs get no publishing or cloud write credentials; release jobs run in protected environments with reviewers.
- Harden GitHub: phishing-resistant MFA for maintainers, branch protection, secret scanning, block repository visibility changes by members, and restrict self-hosted runner registration.
Decision guidance
- Package publishers gain most from trusted publishing plus disallowing tokens; it removes the stored credential the first wave relied on.
- Package consumers gain most from cooldowns, lockfiles and disabled install scripts; they cannot control maintainers' tokens.
- Small teams on hosted CI can adopt all of this in days; large estates should start with the repositories that publish public packages and the runners with cloud write access.
Metrics
- Token-free publishing: percentage of published packages using trusted publishing with tokens disallowed.
- Script exposure: percentage of CI pipelines running installs with lifecycle scripts disabled.
- Secret reach: number of install or test jobs with access to publishing or cloud write credentials (target: zero).
- Rotation time: hours from worm advisory to full rotation of exposed credentials.
- Cooldown coverage: percentage of dependency resolutions passing through a proxy with a minimum release age.
Framework mapping
| Control area | NIST CSF 2.0 | NIST SP 800-53 Rev. 5 |
|---|---|---|
| Supplier and component risk | GV.SC-07 | SR-3, SR-11 |
| Build configuration and least functionality | PR.PS-01 | SA-10, CM-7 |
| Credential management for publishing and cloud | PR.AA-01 | IA-5 |
| Monitoring of build and developer environments | DE.CM-09 | SI-4 |
Practitioner checklist
- Search GitHub audit logs for Shai-Hulud repositories, visibility flips, discussion.yaml workflows and unexpected self-hosted runners.
- Move every published package to trusted publishing and disallow tokens.
- Scope OIDC trust to one workflow, branch and protected environment with reviewers.
- Disable install scripts in CI and pin dependencies with committed lockfiles.
- Apply a minimum release age to new package versions in the registry proxy.
- Remove publishing and cloud write credentials from install and test jobs.
- Block webhook.site egress from build networks.
- Pre-agree a rotation runbook covering npm, GitHub, cloud, Kubernetes, CI and SSH credentials.
- Monitor developer hosts for new launch agents, systemd user units and AI tool config changes.
Standards and references
- MITRE ATT&CK: Shai-Hulud (S9008)
- MITRE ATT&CK: Mini Shai-Hulud (S9043)
- CISA: Widespread Supply Chain Compromise Impacting npm Ecosystem
- npm Docs: Trusted Publishing for npm Packages
- GitHub: Our Plan for a More Secure npm Supply Chain
- Palo Alto Networks Unit 42: Shai-Hulud Worm Compromises npm Ecosystem
- Sophos: Mini Shai-Hulud Supply Chain Attack Targets SAP npm Packages
- NIST: Cybersecurity Framework 2.0
Related NHI Mgmt Group resources: Malicious Package and Dependency Confusion Guide · npm Tokens Security Guide · Malicious Package Worm Response Playbook · GlassWorm VS Code Extension Worm Guide