Shai-Hulud 1.0

Sep 15–24, 2025
500+
infected packages
20
max packages / victim
3.6 MB
payload size
~10 days
active window
Initial vector

Downstream of the s1ngularity/Nx attack

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.

Execution

postinstall lifecycle script

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).

Credential theft

Three-layer exfiltration

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 3trufflehog filesystem /: full disk scan (800+ detectors).

~/.npmrc process.env TruffleHog
Self-propagation

npm token → enumeration → publish

Reads _authToken → calls /whoami → queries /v1/search?text=maintainer:$USER → sorts by popularity → publishes infected versions of up to 20 packages.

steal npm token enumerate pkgs sort by popularity npm publish ×20 ↻ per victim
GitHub abuse

Exfiltration repos + Actions injection

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.

Key innovation

First true npm worm

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.

Shai-Hulud 2.0 — "The Second Coming"

Nov 21 – Dec 2025
796
packages (1,092 versions)
25K+
malicious GH repos
~350
GH users
of Fortune 100 impacted
Initial vector

PWN request (pull_request_target) confirmed

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.

Execution

preinstall + Bun runtime changed

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.

Credential theft

Extended surface — but cloud.json bugged Wiz nuance

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.

~/.npmrc ~/.netrc process.env TruffleHog IMDS (code present, exfil failed)
Cross-victim recycling

Distributed self-resilient network new

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).

Destructive sabotage

Dead man's switch new

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.

Persistence

Self-hosted runners + C2 via Discussions

Self-hosted GH Actions runners (SHA1HULUD). GitHub Discussions as C2 channel. Wiz notes this channel does not appear to have been actively exploited.

Exfiltration

Randomly named 18-character repos

Peak: ~13,686 repos in a single day (Nov 24), ~1,000 / 30 min (GHArchive via Wiz). Double Base64-encoded JSON files:

contents.json environment.json cloud.json (empty) truffleSecrets.json actionsSecrets.json
Long tail · Wiz Dec 2025

Persistence via private registries, IDE extensions, and Trust Wallet link

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."

Shai-Hulud 3.0 — "The Golden Path"

Dec 28–29, 2025
1
package identified
197
downloads
~0
observed propagation
Aikido
discovery
Context

Test variant, same author

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.

Technical evolution

Enhanced obfuscation + cross-platform

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).

Notable change

Dead man's switch removed removed

The destruction mechanism observed in v2.0 was removed. Likely an evolution toward more stealth, or an artifact of the test context.

Assessment

Signal of a persistent threat

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."

SANDWORM_MODE

Feb 20, 2026 · Socket Research Team
19
typosquatted packages
MCP
AI poisoning
48 h
time gate (CI bypass)
3
exfil channels
Initial vector

Typosquatting targeting AI tools + crypto

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.

Execution

Multi-stage encrypted, execution on import

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.

Self-propagation — 3 vectors

npm worm + GH injection + SSH fallback yes, worm

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.

AI poisoning

MCP server injection new paradigm

Installs a rogue MCP server in ~/.dev-utils/, registered with AI assistants:

Claude Desktop Cursor VS Code Continue

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.

Credential theft

Wide collection surface

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.

Bitwarden 1Password LastPass LLM providers crypto wallets Apple Notes / Messages
Exfiltration — 3 cascading channels

HTTPS → GitHub API → DNS tunneling

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.

Persistence

Global git hooks new

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.

Dormant capabilities (disabled)

Dead switch + Ollama polymorphism code present, disabled

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.

Comparison table

Feature Shai-Hulud 1.0 Shai-Hulud 2.0 Shai-Hulud 3.0 SANDWORM_MODE
DateSep 15–24, 2025Nov 21 – Dec 2025Dec 28–29, 2025Feb 20, 2026
Initial vectorDownstream of s1ngularity (GH Actions)PWN request (pull_request_target)Compromised npm accountTyposquatting (19 packages)
Executionpostinstall (Webpack bundle)preinstall (Bun)preinstall (Bun)require() (multi-stage AES)
Infected packages~500 (StepSecurity)796 / 1,092 versions (Wiz)1 (test)19 (typosquatted)
Self-propagationYes (npm republish ×20)Yes (×100 + cross-victim recycling)Code present, not observedYes (npm + GH inject + SSH + CI loop)
Secret scanningTruffleHog + env dumpTruffleHog + env + IMDS (exfil failed)Not confirmedenv dump + password managers + crypto
Dead man's switchNoYes (active)RemovedCode present, disabled
PersistenceGH workflowsSelf-hosted runners (not exploited)Similar to v2Global git hooks + MCP servers
AI / MCP / LLMNoNoNoMCP injection + Ollama polymorphism (disabled)
ExfiltrationPublic GH repos "Shai-Hulud"Random 18-char GH reposNew GH markerHTTPS → GH API → DNS tunneling (DGA)
Payload delayImmediateImmediateImmediate48h (bypassed in CI)
Cross-ecosystemnpmnpm + Maven + OpenVSXnpmnpm + CI/CD + IDE (MCP)
Impact36 GH users (Wiz)⅓ Fortune 100, 150+ orgs197 DL, no propagationUnknown (likely in testing)