Skip to content

Back to the catalog

SANDWORM_MODE: Dissecting a Multi-Stage npm Supply Chain Attack | Blog | Endor Labs
endorlabs.comwriteup

SANDWORM_MODE: Dissecting a Multi-Stage npm Supply Chain Attack | Blog | Endor Labs

Endor Labs dissects "SANDWORM_MODE", a multi-stage npm supply-chain attack using typosquatted packages (e.g. suport-color, fake claude-code) that combines credential theft, worm-like propagation via npm and GitHub Actions, and DNS/HTTPS/GitHub API exfiltration with a domain generation algorithm. Notably it installs a rogue MCP server into AI coding assistants (Cursor, Claude Code, Windsurf, Continue) and uses prompt injection to trick the AI into silently exfiltrating SSH keys, AWS credentials, and password-manager secrets. The post walks through the three-layer deobfuscation chain (Base64 + zlib + XOR + eval), differential-analysis detection techniques, and disabled destructive dead-switch/polymorphic-rewrite features.

TL;DR Typosquatted npm packages deploy a rogue MCP server to hijack AI coding assistants (Cursor, Claude Code) for credential exfiltration alongside worm-like npm/GitHub propagation.
Document

What it is

Endor Labs' analysis of the "SANDWORM_MODE" npm supply-chain campaign (first reported by Socket), a set of typosquatted packages — the reference sample is suport-color@1.0.1, a typosquat of supports-color. Endor frames it as an evolution of the 2025 Shai Hulud campaign, notable for weaponizing AI coding assistants. All known malicious packages have since been removed from npm.

How it works

The typosquat's index.js silently requires lib/color-support-engine.min.js, a ~167KB obfuscated blob (the page also cites 163KB) inside a try/catch so any load failure is swallowed. Deobfuscation runs three nested layers: Base64-decode → zlib-decompress → XOR-decrypt with a hardcoded 32-byte key → eval(). The decrypted Stage 1 is a webpack bundle that harvests six categories of secrets, each with a distinct technique, including from password managers named explicitly as Bitwarden, 1Password, and LastPass, plus environment variables and crypto wallets. (The feature list also cites AES-256-GCM; the walked-through chain is Base64+zlib+XOR.)

What to do

The reusable value here is detection, not remediation — the packages are already gone. Endor's scanner flagged them 2–5 minutes after publication on three signals an engineer can apply to any candidate package:

  1. A heavily obfuscated payload of considerable size (here the package is multiple times larger than the benign original due to the hidden ~167KB blob).
  2. Automated decryption + execution triggered on require.
  3. The same author publishing several comparable typosquats in a short timeframe.

Differential analysis sharpens this: diffing suport-color@1.0.3's index.js against supports-color@10.2.2 shows the entire benign file replaced by a few lines that dynamically construct a file path and require it inside a silencing try/catch. Diffing the earlier suport-color@1.0.1 shows the path was originally hard-coded, then made dynamic — evidence the author iterated to evade detection. Comparing a suspect package against both a prior version and its typosquat target is the concrete technique.

Caveats

The fetched text cuts off during Stage 1 (credential harvesting) — the detailed mechanics of the rogue MCP server, worm propagation, and DGA exfiltration described in the summary are not in the retrieved portion. Two of the most dangerous capabilities were disabled in the analyzed samples: the destructive dead switch and the polymorphic-rewrite-via-local-LLM feature. Their presence signals intent, but they did not fire in what Endor examined.

Organisations named here

  • Shai-Hulud

Technologies named here

  • npm
  • Model Context Protocol
  • Cursor
  • Claude Code
  • Windsurf
  • GitHub Actions

Threat actors named here

  • SANDWORM_MODE