Preface

On August 4, 2026, Microsoft Threat Intelligence disclosed a large-scale npm supply chain attack codenamed ChainDrop. Attackers published over 440 packages and more than 2,200 malicious versions to the npm registry within just a few hours, targeting high-frequency dependencies including keyv, flat-cache, and cache-manager, with a combined weekly download volume exceeding 500 million times. This is not a simple package poisoning campaign: the worm automatically executes via the preinstall lifecycle hook during the npm install phase, stealing credentials from npm, GitHub, AWS, Kubernetes, HashiCorp Vault and other platforms, then uses the stolen identities to automatically modify and republish more packages, forming a self-propagating infection chain. Some variants also inject Claude and VS Code configuration files into repositories to establish persistent backdoors.

This article sorts out ChainDrop’s attack chain, technical characteristics and actionable protection recommendations based on public analysis reports from Microsoft, StepSecurity, SecurityWeek and other sources.

Event Overview: From 11 Seed Packages to 400+ Infections

ChainDrop is an evolved version of the Mini Shai-Hulud worm family, and the latest round in the npm supply chain attack wave since 2025. It is similar to the previous Shai-Hulud attacks, but introduces new technologies such as EtherHiding (Ethereum blockchain C2).

Timeline (UTC, 2026-08-04):
1. Attackers first compromised the GitHub account of Jared Wray, maintainer of the keyv, cacheable and other namespaces.
2. At approximately 09:00 UTC, 11 “seed packages” including keyv@6.0.0 were published, carrying the full worm payload.
3. After the malicious packages are executed on developer workstations and CI/CD runners, they steal credentials and automatically republish infected packages to npm.
4. Within four hours, StepSecurity observed 444 packages and 2,212 versions being poisoned; the npm team began removing malicious versions within about two hours, but the worm could still spread continuously via newly compromised accounts.

Affected Scope:

Metric Data
Number of infected packages 440+ (StepSecurity counted 444)
Number of malicious versions 2200+ (StepSecurity counted 2212)
Seed packages 11 (keyv, cacheable ecosystem)
Automatically propagated by worm 433 additional packages
Weekly downloads Over 500 million times (SecurityWeek)

Typical high-risk packages include keyv@6.0.0 (~150 million weekly downloads), flat-cache@6.1.24 (~150 million weekly downloads), and file-entry-cache@11.1.6 (~147 million weekly downloads) — they are widely used in toolchains such as ESLint and cache-manager, meaning a large number of JavaScript projects may indirectly depend on infected versions.

Attack Chain: How the preinstall Hook Became the Entry Point

Microsoft’s analysis points out that many malicious versions do not have corresponding GitHub commits, PRs or Tags, indicating that attackers did not compromise source code repositories one by one, but directly modified the tarball and reuploaded it via stolen npm publish tokens. This is one of the core reasons for the extremely fast spread speed of ChainDrop.

1. Entry Point: preinstall Lifecycle Hook

npm executes lifecycle scripts in sequence when installing dependencies. ChainDrop added a preinstall hook in the package.json of infected packages, pointing to the setup.mjs inside the package:

{
  "scripts": {
    "preinstall": "node setup.mjs"
  }
}

Since preinstall runs before the package installation is completed, the malicious code can finish executing before regular security checks such as unit tests and SAST scans start — both local npm install and dependency installation steps in CI pipelines may trigger it.

2. Two-Stage Payload: Bun Runtime + Obfuscated Script

setup.mjs will download the legitimate Bun JavaScript runtime and load an approximately 710 KB obfuscated two-stage script (common file names are Math_Symbol.js or math_init.js). Using Bun instead of Node.js itself helps bypass some monitoring rules targeting Node.js processes.

Microsoft Defender has marked this behavior as detections such as Suspicious usage of Bun runtime and Trojan:NPM/MalBun.A.

3. Credential Collection: From Local Files to Cloud APIs

After the payload starts, it distinguishes between developer workstations and CI/CD environments:
- On developer machines, the process will detach to the background and continue running;
- In CI environments, the process remains attached to read workflow secrets, runner credentials and GitHub Actions OIDC publish permissions.

The collection scope includes:
1. Local credential files, shell history, SSH keys, cloud configuration files;
2. Process environment variables (including NPM_TOKEN, GITHUB_TOKEN, etc.);
3. Tokens obtained via CLIs such as gh auth token, gcloud, az;
4. Using obtained credentials to call AWS, Kubernetes, HashiCorp Vault APIs to enumerate and pull more secrets.

4. Data Exfiltration: HTTPS + GitHub Backup Channel

Stolen data is serialized into JSON, compressed with gzip, then encrypted with AES-256-GCM, and the encryption key is wrapped with the attacker’s RSA public key. The exfiltration paths are:
1. Primary Channel: Dynamic HTTPS endpoints controlled by the attackers. C2 domain names are distributed via Ethereum smart contracts (0xE1f2395ee43e45A1556EC6438a88c31B83493103) or signed GitHub commits (marked thebeautifulmarchoftime). The active domain name during analysis was npm-cache.com, and historical candidates include pypi-get.com, js-mirror.com.
2. Backup Channel: Use stolen GitHub tokens to create public repositories described as “Shai-Hulud: Here We Go Again”, and submit encrypted results as results-*.json files.

5. Worm Propagation: One Token Infects All Publishable Packages

The most destructive capability of ChainDrop is automated propagation. After obtaining an npm publish token, the worm will:
1. Enumerate all packages accessible to the identity;
2. Download the latest tarball of each package;
3. Insert malicious setup.mjs and Math_Symbol.js, replace the lifecycle scripts;
4. Increment the patch version number and republish.

A single compromised maintainer account can produce hundreds of malicious patch versions within hours — and these versions often appear to be just ordinary patch updates, with no changes to the source code repository.

6. Abuse of GitHub Actions OIDC

For repositories configured with npm Trusted Publishing (OIDC), the worm has a dedicated path: use the workflow identity to obtain publish permissions with legitimate provenance, allowing malicious packages to carry seemingly credible source signatures. This means that relying solely on provenance verification is not enough to defend against such attacks.

7. Persistence: Claude / VS Code Configuration Injection

The worm will also use stolen GitHub credentials to inject configuration files into writable repository branches:
- .claude/settings.json, .claude/setup.mjs
- .vscode/tasks.json, .vscode/setup.mjs

When developers later open the project using Claude Code, VS Code or GitHub Copilot, the payload may be triggered again, forming a developer-to-developer secondary infection path. Some macOS/Linux variants also install a “dead man’s switch”: poll the GitHub API every 60 seconds to check token validity, and self-destruct if the token is revoked; there is also a 24-hour self-cleanup mechanism.

Evolutionary Relationship with Shai-Hulud

ChainDrop is regarded by the security community as a heavily evolved version of Shai-Hulud 2.0 worm, with the following main new capabilities:

Feature Early Shai-Hulud ChainDrop
Execution Entry postinstall / preinstall preinstall + Bun two-stage
C2 Communication Static domain name / GitHub EtherHiding (dynamically distributed via Ethereum contracts)
Propagation Method Manual or semi-automatic Fully automatic tarball modification + republishing
Persistence Limited Claude / VS Code configuration injection
CI Target Credential theft Credential theft + OIDC publish abuse

The worm will also detect Russian system environments and actively exit, a feature mentioned in both Microsoft and StepSecurity’s analyses.

How to Check: Is Your Project Affected

1. Check Lockfiles and node_modules

Compare the affected package lists maintained by vendors such as StepSecurity, Wiz and Socket, and retrieve the version numbers in package-lock.json, yarn.lock, pnpm-lock.yaml. Do not only check direct dependencies — infected packages are mostly transitive dependencies.

# Example: Search for known high-risk packages in lockfiles
grep -E '"(keyv|flat-cache|file-entry-cache|cache-manager)"' package-lock.json

2. Monitor IOCs (Indicators of Compromise)

File hashes published by Microsoft:

SHA256 Description
54dc7ea54a1317cca0e890a2770630cf7fa6c97813e0cb9d2caa93012b350668 setup.mjs (npm preinstall loader)
fd3ca4007b225fdf8de7af4345a19179d5efa8c4bb9205f88cda806e5684b1eb setup.mjs (.claude / .vscode loader)
9fc2570b7cef51c1b8df116d144d11ff4096357be7d2c4c6367cfc2509cf1bcc Math_*.js worm main body

Network IOCs: npm-cache.com, pypi-get.com, js-mirror.com, and https://npm-cache.com:443/router.

3. Check CI and Local Environments

Retrieve the following process characteristics in terminals or SIEM systems:

# Suspicious preinstall execution
node setup.mjs

# Bun two-stage loading (path contains bun-dl- or node_modules)
bun ... Math_Symbol.js

If a match is found, the machine should be treated as compromised.

Emergency Response and Long-Term Protection

If an Infected Version Has Already Been Installed

The recommendations from Microsoft and JFrog can be summarized as the following steps:
1. Isolate: Disconnect affected workstations and CI runners from the network, retain tarballs, npm logs, CI logs and GitHub audit logs to define the exposure window.
2. Clean Up: Delete node_modules and npm/yarn caches (including shared CI caches); rebuild runner images and golden build environments.
3. Rotate Credentials: Revoke and rotate npm tokens, GitHub PATs, AWS keys, K8s ServiceAccounts, Vault tokens and other credentials in a known clean environment — the order matters, isolate first before rotating.
4. Audit Repositories: Check for abnormal injections in the .claude/ and .vscode/ directories; review GitHub Actions workflow changes and npm publish records.
5. Rebuild Artifacts: Rebuild from a known good dependency baseline, confirm that no infected hashes exist in caches and artifact repositories.

Daily Protection Recommendations

  1. Upgrade npm CLI to v12+, enable the min-release-age function to delay installation of newly published versions, buying time for the community to remove malicious packages.
  2. Lock dependency versions (pin / lockfile commit), avoid automatically pulling the latest patches without locking in CI.
  3. Disable lifecycle scripts in CI or use --ignore-scripts (need to evaluate impact on native modules):
npm ci --ignore-scripts
  1. OIDC Trusted Publishing is still recommended, but it needs to be used with workflow approval, environment protection rules and abnormal publish alerts, and cannot be used as the sole defense line.
  2. Principle of Least Privilege: npm publish tokens, GitHub PATs and cloud IAM roles should be limited to necessary scopes; CI secrets should be isolated per job.
  3. Supply Chain Monitoring: Integrate tools such as Socket, Snyk and Dependabot to perform diff and behavior analysis on newly added patch versions.

Final Notes

ChainDrop once again proves that the security boundary of the npm supply chain has long exceeded “whether a package has been compromised” — CI/CD credentials, OIDC publish chains, AI development tool configurations may all become attack jump points. A single maintainer account’s GitHub compromise can evolve into a worm storm affecting hundreds of packages and 500 million weekly downloads within four hours.

For ordinary developers, the most practical action right now is to check lockfiles, clean caches and rotate potentially exposed tokens. For teams, dependency installation, publish processes and credential management need to be regarded as part of the same security system — because the next attack will most likely start with that single preinstall script in npm install.