Four worm variants exploiting the npm ecosystem to propagate autonomously. September 2025 – February 2026.
Wiz assesses this campaign directly stems from the s1ngularity compromise (August 2025): GitHub token theft via pull_request_target injection → npm token theft → publication of malicious packages. The initial vector is GitHub Actions exploitation, not AiTM phishing.
Webpack bundle bundle.js (3.6 MB, minified) executed after npm install. Runs with the user's full privileges. Unit 42 assesses with moderate confidence that an LLM was used to generate the bash script (comments + emojis).
Layer 1 — Full process.env dump: captures GITHUB_TOKEN, NPM_TOKEN, AWS keys, CI/CD secrets.
Layer 2 — Direct file read: ~/.npmrc, project .npmrc.
Layer 3 — trufflehog filesystem /: full disk scan (800+ detectors).
Reads _authToken → calls /whoami → queries /v1/search?text=maintainer:$USER → sorts by popularity → publishes infected versions of up to 20 packages.
Public repos named Shai-Hulud with double Base64-encoded secrets (data.json). Injection of shai-hulud-workflow.yml to exfiltrate ${{ toJSON(secrets) }}. Migration of private repos → public with -migration suffix. Wiz identified 36 exposed users and 64 repos with a shai-hulud branch.
Autonomous self-propagation without human interaction. eslint-scope (2018) had attempted the same thing but a bug prevented propagation — Shai-Hulud succeeded. First self-replicating worm in a package ecosystem.
PostHog published a postmortem as "patient zero". Postman and AsyncAPI also impacted. The worm spilled over into the Java/Maven ecosystem via org.mvnpm:posthog-node:4.18.1 (Socket). AsyncAPI confirmed infection through an OpenVSX extension.
Switched from postinstall to preinstall — executes before installation completes. Disguised as Bun: setup_bun.js + bun_environment.js. 99% of infections via preinstall (Wiz). Up to 100 packages per victim (×5 vs v1). Packages from Zapier, PostHog, Postman, ENS Domains temporarily trojanized.
The code targets IMDS (AWS/GCP/Azure), Key Vault, ~/.netrc, cloud secrets managers. However, Wiz confirmed that cloud.json was never populated — a bug (missing try/catch + broken multi-cloud support) prevented effective exfiltration of cloud credentials.
Effective exfiltration relied on process.env, .npmrc, .netrc, and TruffleHog.
Searches GitHub for repos created by other victims via the "Sha1-Hulud: The Second Coming." marker. Uses one victim's tokens to publish another victim's data. Survives individual token rotations. 60% of exfiltrated npm tokens still valid weeks later (Wiz).
If no exploitable token found: secure overwrite + deletion of the entire home directory. Windows: cipher /W. Shift from espionage to punitive sabotage.
Sources: GitLab Vulnerability Research, Unit 42.
Self-hosted GH Actions runners (SHA1HULUD). GitHub Discussions as C2 channel. Wiz notes this channel does not appear to have been actively exploited.
Peak: ~13,686 repos in a single day (Nov 24), ~1,000 / 30 min (GHArchive via Wiz). Double Base64-encoded JSON files:
Wiz documented an active "long tail" 6+ days after containment. Private registries (Artifactory, Nexus) and IDE extensions kept the worm active. Potential link to a $7M Trust Wallet exploit — domain using the Dune naming convention, registered on Dec 8. Wiz: "registry removal is not the end of the incident."
Discovered by Aikido (Charlie Eriksen) in @vietmoney/react-big-calendar v0.26.2 (published Dec 28, 2025, original package from March 2021). Eriksen assesses: not a copycat but an actor with access to the original source code. The code was re-obfuscated from source, not modified in place. No major propagation — likely a payload test.
Reorganized file structure, renamed components. Better error handling, more modular code. Improved compatibility: Windows and Bun runtime. New GitHub marker: "Goldox-T3chs: Only Happy Girl" (replacing Shai-Hulud identifiers to evade detection).
The destruction mechanism observed in v2.0 was removed. Likely an evolution toward more stealth, or an artifact of the test context.
Appeared despite npm/GitHub hardening efforts (FIDO 2FA, Trusted Publishing, classic token revocation on Dec 9). The mechanism (lifecycle scripts) remains exploitable — only the credentials were hardened. Aikido quote: "the attacker seized the moment for a final strike before the npm deadlines."
19 packages under two npm aliases (official334, javaorg). Targets: claud-code, cloude-code, suport-color, opencraw (OpenClaw, 210k+ stars), crypto tools. Dune theme preserved: SANDWORM_* variables control runtime behavior.
Stage 1: on require(), decodes a 160+ KB base64 blob (zlib + XOR 32 bytes). Immediate crypto key collection via drainHotline — insurance if Stage 2 fails.
Stage 2: AES-256-GCM decryption in memory (/dev/shm or temp), deleted after execution. Activated after 48h — but this delay is skipped in CI/CD environments to accelerate propagation.
Vector 1 — npm republish: steals npm tokens → republishes infected versions of the victim's packages (patch version bump, malicious shim injection).
Vector 2 — GitHub injection: modifies the victim's repos' package.json to add a "carrier" package + GH Actions workflow injection (pull_request_target). Direct commit or PR + auto-merge if branch protection is enabled.
Vector 3 — SSH agent fallback if GitHub tokens fail.
+ a weaponized GitHub Action (ci-quality/code-quality-check, created Feb 17) that creates a bidirectional npm ↔ CI propagation loop.
Installs a rogue MCP server in ~/.dev-utils/, registered with AI assistants:
Innocuously named tools with hidden prompt injections in tool descriptions (only read by the AI). The assistant silently collects credentials and transmits them to the attacker's MCP server. The user never sees the malicious instructions.
Stage 1 (immediate): npm/GH tokens, env vars, proxy creds, .npmrc, crypto artifacts, LLM keys.
Stage 2 (48h): password vaults, SQLite databases (Apple Notes, macOS Messages, Joplin), crypto wallets.
1. HTTPS POST to Cloudflare Worker (pkg-metrics.official334.workers.dev/exfil).
2. GitHub API upload to private repos (double base64).
3. DNS tunneling via base32 to freefan.net / fanfree.net + DGA seed sw2025 (10 TLDs). Works even if HTTPS is blocked.
Modifies git config --global init.templateDir to inject malicious hooks. Every newly initialized repo inherits the hooks even after the npm package is removed. MCP servers also persist in the IDE config.
Dead switch: present in the code but enabled: false. Would destroy the home directory if activated — same logic as Shai-Hulud 2.0.
Polymorphic engine: code to call a local Ollama instance and rewrite the payload. Also disabled in observed samples. Socket assesses the campaign is likely still in a testing/development phase.
Attribution unconfirmed: Socket cannot determine whether this is the same actor as Shai-Hulud or a copycat. Dune theme is consistent, but obfuscation techniques are distinct.
| Feature | Shai-Hulud 1.0 | Shai-Hulud 2.0 | Shai-Hulud 3.0 | SANDWORM_MODE |
|---|---|---|---|---|
| Date | Sep 15–24, 2025 | Nov 21 – Dec 2025 | Dec 28–29, 2025 | Feb 20, 2026 |
| Initial vector | Downstream of s1ngularity (GH Actions) | PWN request (pull_request_target) | Compromised npm account | Typosquatting (19 packages) |
| Execution | postinstall (Webpack bundle) | preinstall (Bun) | preinstall (Bun) | require() (multi-stage AES) |
| Infected packages | ~500 (StepSecurity) | 796 / 1,092 versions (Wiz) | 1 (test) | 19 (typosquatted) |
| Self-propagation | Yes (npm republish ×20) | Yes (×100 + cross-victim recycling) | Code present, not observed | Yes (npm + GH inject + SSH + CI loop) |
| Secret scanning | TruffleHog + env dump | TruffleHog + env + IMDS (exfil failed) | Not confirmed | env dump + password managers + crypto |
| Dead man's switch | No | Yes (active) | Removed | Code present, disabled |
| Persistence | GH workflows | Self-hosted runners (not exploited) | Similar to v2 | Global git hooks + MCP servers |
| AI / MCP / LLM | No | No | No | MCP injection + Ollama polymorphism (disabled) |
| Exfiltration | Public GH repos "Shai-Hulud" | Random 18-char GH repos | New GH marker | HTTPS → GH API → DNS tunneling (DGA) |
| Payload delay | Immediate | Immediate | Immediate | 48h (bypassed in CI) |
| Cross-ecosystem | npm | npm + Maven + OpenVSX | npm | npm + CI/CD + IDE (MCP) |
| Impact | 36 GH users (Wiz) | ⅓ Fortune 100, 150+ orgs | 197 DL, no propagation | Unknown (likely in testing) |