ChainDrop: The npm Worm That Hit 2 Billion Downloads in Hours
On August 4, 2026, a self-propagating supply chain worm dubbed ChainDrop tore through the npm registry, infecting over 1,300 packages representing a combined 2 billion monthly downloads in under four hours. It is the largest npm supply chain incident since the smaller Shai-Hulud attack earlier this summer — a direct and more destructive descendant that abuses the same underlying mechanisms at dramatically greater scale.
By the Numbers
| Metric | Detail |
|---|---|
| First malicious release | keyv@6.0.0 at 09:35:00 UTC, August 4, 2026 |
| Packages compromised (Aikido) | 868+ packages, 1,381+ versions |
| Packages compromised (StepSecurity) | 444 packages, 2,212 versions in under 4 hours |
| Poisoned versions (Semgrep) | 1,557 versions across 435 packages |
| Combined monthly downloads | ~2 billion |
| Time for initial wave | Under 4 hours |
| Enterprise scopes reached | @servicetitan, @qlik, @nebula.js, @onereach |
How the Worm Spread
The Origin: Jared Wray's Repositories
The attack originated inside three GitHub repositories belonging to popular caching library maintainer Jared Wray:
jaredwray/keyv— ~150 million weekly downloadsjaredwray/cacheablejaredwray/ecto
Once the first poisoned release (keyv@6.0.0) was published at 09:35 UTC, the worm's automated second wave reached 433 additional packages by 13:20 UTC.
The Infection Mechanism
ChainDrop rides entirely on legitimate infrastructure, making it extremely difficult to detect or block at the network layer:
| Infrastructure Abused | Purpose |
|---|---|
| GitHub release automation | Automated tagging and release creation |
| npm OIDC trusted publishing | Legitimate package publication without stored credentials |
| Bun runtime distribution | Worm payload bundled with Bun |
Each infected package receives obfuscated loader files — observed as setup.mjs and math_init.js — wired into the package's preinstall lifecycle hook:
{
"scripts": {
"preinstall": "node setup.mjs"
}
}Why preinstall Is Particularly Dangerous
The preinstall hook runs during dependency resolution, not at import time. This means:
- A developer running
npm installis sufficient to trigger the payload - The malicious code executes before the package is even used
- CI/CD pipelines that restore dependency trees are equally vulnerable
- The application code never has to
require()the infected package
What the Worm Does
ChainDrop is a credential-harvesting worm designed to:
- Self-propagate — Use harvested npm and GitHub tokens to publish poisoned versions to other packages the compromised maintainer owns
- Harvest CI/CD credentials — Capture npm tokens, GitHub tokens, and other secrets from the build environment
- Exfiltrate via Ethereum dead-drop C2 — Commands and stolen data are relayed through an Ethereum-based command-and-control channel, making traditional network-level blocking ineffective
Compromised maintainer account
↓
preinstall hook executes on npm install
↓
Payload harvests: npm tokens, GitHub tokens, env vars
↓
Stolen credentials used to publish poisoned versions
of other packages the maintainer owns
↓
Worm spreads to adjacent packages and scopes
↓
Stolen credentials exfiltrated to Ethereum dead-drop C2Notable Packages Compromised
| Package | Monthly Downloads | Owner/Scope |
|---|---|---|
| keyv | ~150 million/week | Jared Wray |
| cacheable | High | Jared Wray |
| flat-cache | High | Jared Wray |
| file-entry-cache | High | Jared Wray |
| @servicetitan/* | Enterprise | ServiceTitan |
| @qlik/* | Enterprise | Qlik |
| @nebula.js/* | Enterprise | Qlik |
| @onereach/* | Enterprise | OneReach |
| @or-sdk/* | Enterprise | OneReach |
Enterprises affected: Deliveroo, Ornikar, OneReach, Picsart, Qlik, ServiceTitan — meaning the worm moved from public developer tooling into corporate application build pipelines within its first two hours.
Relationship to Shai-Hulud
ChainDrop is built on the same attack pattern as Shai-Hulud, a smaller npm worm observed earlier in 2026. Key differences:
| Attribute | Shai-Hulud | ChainDrop |
|---|---|---|
| Scale | Limited | 1,300+ packages |
| Monthly downloads affected | Millions | 2 billion |
| Self-propagation | Yes | Yes (improved) |
| C2 mechanism | Traditional | Ethereum dead-drop |
| Enterprise scope reach | Limited | Major enterprise SDKs |
Immediate Response Steps
1. Audit Your Dependency Tree
# Check if any installed packages are on the compromised list
npm ls keyv cacheable flat-cache file-entry-cache
# Check for unusual preinstall scripts in your installed packages
cat node_modules/keyv/package.json | grep preinstall
cat node_modules/cacheable/package.json | grep preinstall
# Look for suspicious loader files in node_modules
find node_modules -name "setup.mjs" -o -name "math_init.js" 2>/dev/null
# Use npm audit to check for known malicious versions
npm audit2. Pin to Known Good Versions
# Check the earliest malicious version (keyv@6.0.0 released 09:35 UTC Aug 4)
# Pin to the last confirmed clean version
npm install keyv@5.x.x --save-exact
# Lock your package versions
npm shrinkwrap
# or use a lockfile policy to prevent automatic updates3. Rotate Credentials If You Ran npm install Today
If npm install was run in any environment after 09:35 UTC on August 4, 2026, and any affected packages were present:
# Revoke and regenerate npm tokens
npm token revoke <token>
# Revoke GitHub personal access tokens exposed to the build environment
# GitHub Settings → Developer settings → Personal access tokens → Revoke
# Audit CI/CD secrets for exposure
# GitHub Actions: Settings → Secrets → audit and rotate4. Verify No Poisoned Packages Remain in Production
# If using Docker, rebuild from a clean base with pinned versions
docker build --no-cache .
# Clear npm cache before reinstalling
npm cache clean --force
rm -rf node_modules
npm install # after pinning clean versionsPlatform Responses
GitHub / npm is taking structural action in response to this and the Shai-Hulud pattern:
- npm v12 will disable preinstall scripts by default, eliminating the primary propagation mechanism
- Dependabot is introducing a default package cooldown — waiting at least 3 days before opening a PR for a new release, giving time for malicious packages to be detected before reaching production
Detection Indicators
| Indicator | Description |
|---|---|
setup.mjs or math_init.js in node_modules | ChainDrop loader files |
| Unexpected network connections to Ethereum RPC endpoints | Dead-drop C2 communication |
| npm/GitHub token usage from unexpected IPs | Compromised credentials in use |
| New package versions published across multiple scopes | Worm propagation activity |
Unusual process spawning during npm install | preinstall hook execution |
Sources
- BleepingComputer — Massive ChainDrop npm Supply-Chain Attack Infects Hundreds of Packages
- StepSecurity — ChainDrop npm Worm: Bun-loaded CI/CD Credential Harvester with Ethereum Dead-drop C2
- Semgrep — It's Not npm-ver Yet: NPM Worm ChainDrop Hits 400+ Packages
- The Next Web — ChainDrop: The npm Worm That Faked Being Legitimate
- GitHub Blog — Disrupting Supply Chain Attacks on npm and GitHub Actions
- Techmeme — ChainDrop Compromises 1,300+ npm Packages