The npm Threat Landscape: Attack Surface and Mitigations

The npm Threat Landscape: Attack Surface and Mitigations
The npm ecosystem shifted to a high-consequence threat landscape after the September 2025 Shai-Hulud worm enabled automated compromise and redistribution of malicious packages, accelerating token theft and CI/CD persistence. Unit 42 describes a multi-stage, heavily obfuscated payload attributed to TeamPCP that steals npm/GitHub/cloud credentials, exfiltrates via an HTTPS C2 and public GitHub repositories, and self-propagates by backdooring legitimate npm packages. #ShaiHulud #TeamPCP

Keypoints

  • The Shai-Hulud campaign abused a malicious @bitwarden/[email protected] package to steal developer and cloud credentials and to self-propagate by backdooring publishable npm packages.
  • Adversaries prioritize automated token theft (npm tokens and GitHub PATs), CI/CD persistence, and multi-stage “sleeper” dependencies to evade detection.
  • The malware downloads a Bun runtime, runs a ~10 MB obfuscated payload (bw1.js), and uses multiple credential providers to harvest files, environment variables, and cloud secrets.
  • Exfiltration is dual-path: encrypted HTTPS POSTs to audit.checkmarx[.]cx/v1/telemetry and covert staging in public GitHub repositories with distinctive commit/message formats.
  • The worm validates publish permissions for stolen npm tokens, modifies package tarballs to add a preinstall hook (setup.mjs), bumps versions, and republishes packages to achieve exponential spread.
  • Indicators include C2 domains/IPs, GitHub dead-drop commits and repos, file hashes for bw_setup.js/bw1.js/package.json, and the malicious npm package @bitwarden/[email protected]; immediate remediation actions include credential rotation, blocking IOCs, and pinning dependencies.

MITRE Techniques

  • [T1195 ] Supply Chain Compromise – Used to weaponize the npm registry and other distribution channels to distribute malware and backdoor legitimate packages (‘the npm registry could be used as a force multiplier for malware distribution’).
  • [T1078 ] Valid Accounts – Stole and reused npm tokens and GitHub Personal Access Tokens to publish backdoored packages and manipulate repositories (‘theft of npm tokens and GitHub Personal Access Tokens (PATs)’).
  • [T1555 ] Credentials from Password Stores – Read local credential files and config stores across OSes to harvest secrets (‘Reads sensitive files from the developer’s workstation, with per-OS path lists’).
  • [T1005 ] Data from Local System – Collected and read files from disk for inclusion in exfiltration payloads (‘All others are read in full and included in the exfiltration payload’).
  • [T1105 ] Ingress Tool Transfer – Downloaded the Bun JavaScript runtime during install to execute payload code (‘Downloads the Bun JavaScript runtime (v1.3.13) from the official github[.]com/oven-sh/bun releases’).
  • [T1027 ] Obfuscated Files or Information – Employed multiple obfuscation layers (string table rotation, seeded ASCII shuffle, gzip+Base64 blobs, mangled identifiers) to hinder analysis (‘The code employs multiple layers of obfuscation’).
  • [T1041 ] Exfiltration Over C2 Channel – Sent harvested credentials to a remote C2 via encrypted HTTPS POSTs (‘sent via POST hxxps[:]//audit.checkmarx[.]cx:443/v1/telemetry’).
  • [T1567 ] Exfiltration Over Web Service – Used stolen GitHub tokens to create public repositories and commit encrypted results for resilient exfiltration (‘Creates a new public repository under the victim’s account … Commits encrypted result files to a results/ directory’).
  • [T1098 ] Account Manipulation – Created repositories, branches, workflow files, and published packages using compromised accounts to expand access and persistence (‘Creates a new branch, commits .github/workflows/format-check.yml — a malicious workflow … Writes a .npmrc with the stolen token’s auth line … and runs bun publish’).
  • [T1505 ] Server Software Component – Embedded malicious components into developer tooling and CI/CD channels (GitHub Actions, Docker Hub images, VS Code extensions) to attain long-term distribution and pipeline persistence (’embedding themselves into continuous integration/continuous delivery (CI/CD) pipelines to attain long-term, undetectable access’).

Indicators of Compromise

  • [Domain ] C2 and attacker domains – audit.checkmarx[.]cx (C2 domain), checkmarx[.]cx (attacker-controlled domain)
  • [IP Address ] Attacker infrastructure – 94.154.172[.]43 (C2 IP), 91.195.240[.]123 (attacker IP)
  • [GitHub Repository ] Dead-drop and exfiltration staging – helloworm00/hello-world (dead drop repo), public repos named — with description “Checkmarx Configuration Storage” (exfiltration repos)
  • [Git Commit SHA ] Dead-drop commit – bc544f455d7c06c8a1f3446160a6d9a4a8236b11 (dead drop commit SHA1)
  • [Email Address ] Attacker contact – helloworm00@proton[.]me (attacker email)
  • [File Name ] Malicious scripts and artifacts – bw_setup.js (bootstrap script), bw1.js (obfuscated payload)
  • [SHA256 Hash ] Malicious file hashes – bw_setup.js: f35475829991b303c5efc2ee0f343dd38f8614e8b5e69db683923135f85cf60d; bw1.js: 18f784b3bc9a0bcdcb1a8d7f51bc5f54323fc40cbd874119354ab609bef6e4cb
  • [npm Package ] Malicious package and artifact – @bitwarden/[email protected] (malicious package), injected preinstall: “node setup.mjs” in package.json (injected hook)
  • [Process ] Runtime indicators – Unexpected bun process execution (runtime indicator) and presence of .github/workflows/format-check.yml on transient branches (workflow injection)


Read more: https://unit42.paloaltonetworks.com/monitoring-npm-supply-chain-attacks/