ChainDrop NPM Worm Analysis | ThreatLabz
ChainDrop is a self-propagating NPM worm that entered the npm ecosystem on August 4, 2026 through a compromised maintainer account of the keyv ecosystem. The attacker pushed malicious commits to source repositories, causing trusted GitHub Actions release pipelines to build and publish poisoned packages with valid SLSA Build Level 3 provenance. The worm spread to over 400 packages by stealing npm tokens and republishing infected versions, using Bun runtime to evade Node.js-focused security tools and anchoring C2 infrastructure in an Ethereum smart contract for resilient domain rotation.
- domainnpm-cache[.]comFallback C2 domain retrieved from Ethereum smart contract via eth_call when GitHub exfiltration fails
- filename.claude/settings.jsonPersistence hook planted by ChainDrop to execute node .vscode/setup.mjs at the start of a Claude Code session; survives package removal
- filename.github/copilot-instructions.mdUsed in some variants to inject instructions into the GitHub Copilot chat context for persistence or manipulation
- filenameMath_Symbol.jsStage 2 heavily obfuscated payload executed by Bun runtime; performs credential theft, worm propagation, data exfiltration, and persistence hook installation
- filenamesetup.mjsStage 1 dropper executed via preinstall hook in package.json; detects OS, checks for Russian locale, downloads Bun runtime, and launches Math_Symbol.js
- filename.vscode/tasks.jsonPersistence hook planted by ChainDrop to execute the payload when a project folder is opened in VS Code; survives package removal
Detection / Hunteropenrouter
What Happened
A malicious worm called ChainDrop infected the npm package ecosystem on August 4, 2026 by taking over a package maintainer's GitHub account. The attacker injected harmful code into popular JavaScript packages, and because the code came from the legitimate maintainer's account, automated security tools could not distinguish the poisoned packages from safe ones. The worm spread to over 400 packages within four hours by stealing developer authentication tokens and using them to publish more infected packages. When developers try to revoke stolen tokens, the worm retaliates by deleting all files in the user's home directory. Anyone using npm packages in their development environment or CI/CD pipelines should check whether they installed any of the affected packages, rotate their tokens and credentials, and review their project configuration files for unexpected modifications.
Key Takeaways
- ChainDrop is a self-propagating NPM worm that compromised the keyv maintainer's GitHub account and pushed malicious commits directly to source repositories, triggering trusted CI/CD pipelines to publish poisoned packages with valid SLSA Build Level 3 provenance.
- The worm spread to over 400 packages in under four hours by stealing npm tokens from infected environments and republishing infected versions of every package those tokens could reach.
- C2 infrastructure is anchored in an Ethereum smart contract, allowing the attacker to rotate C2 domains via a single blockchain transaction, rendering domain-based blocklists ineffective.
- Token revocation triggers a destructive wiper (rm -rf ~/) that deletes the victim's home directory including source code, SSH keys, and cloud credentials.
- Persistence hooks in .claude/settings.json and .vscode/tasks.json survive package removal and re-execute the payload when a developer opens a project in VS Code or Claude Code.
Affected Systems
- npm ecosystem packages (keyv, cacheable, flat-cache, file-entry-cache)
- Node.js and JavaScript development environments
- CI/CD pipelines using GitHub Actions with OIDC trusted publishing
- VS Code and Claude Code IDE users
- Systems with npm publishing tokens, GitHub PATs, AWS/GCP/Azure credentials, Kubernetes configs, or HashiCorp Vault tokens
Vulnerabilities (CVEs)
None identified.
Attack Chain
- Initial Access: Attacker compromises GitHub account of keyv maintainer and pushes malicious setup.mjs and Math_Symbol.js to main branches with a preinstall hook in package.json
- Execution: npm install triggers the preinstall hook which runs setup.mjs; setup.mjs downloads Bun v1.3.13 and launches Math_Symbol.js via Bun to bypass Node.js monitoring
- Credential Theft: Math_Symbol.js harvests npm tokens, GitHub PATs, OIDC tokens, AWS/GCP/Azure credentials, Kubernetes configs, HashiCorp Vault tokens, SSH keys, and API keys from files and environment variables
- Propagation: Worm uses stolen npm tokens to enumerate writable packages, inject malicious files, bump patch versions, and republish infected packages to npm registry
- Persistence: Hooks planted in .claude/settings.json and .vscode/tasks.json re-execute payload when project is opened in VS Code or Claude Code, surviving package removal
- Exfiltration: Stolen credentials encrypted with AES-256-GCM and RSA-4096, exfiltrated via GitHub dead-drop repos or Ethereum smart contract C2 fallback to npm-cache.com
- Impact: Token revocation triggers gh-token-monitor daemon to execute rm -rf ~/ wiper, deleting home directory contents
Detection Availability
- YARA Rules: No
- Sigma Rules: No
- Snort/Suricata Rules: No
- KQL Queries: No
- Splunk SPL Queries: No
- EQL Queries: No
- Other Detection Logic: No
- Platforms: Zscaler ThreatLabz coverage (referenced but rules not included in article)
The article references Zscaler Coverage but does not include any detection rules, queries, or signatures in the blog post text. No YARA, Sigma, Snort, Suricata, KQL, SPL, or EQL rules are provided.
Detection Engineering Assessment
| Dimension | Rating | Rationale |
|---|---|---|
| EDR Visibility | Medium | EDR tools that monitor Node.js child-process behavior will miss this worm because it executes via the Bun runtime outside of the standard node process. File creation events for setup.mjs and Math_Symbol.js, credential file access patterns, and the rm -rf wiper execution should still be visible to EDR. The gh-token-monitor background process polling GitHub API every 60 seconds is detectable if EDR captures network behavior of processes. |
| Network Visibility | Medium | The worm downloads Bun from its official GitHub release page, which appears as legitimate traffic. C2 communication to npm-cache.com and eth_call requests to the Ethereum smart contract address are detectable at the network level. GitHub API calls for exfiltration and token polling blend with normal developer traffic. DNS queries for npm-cache.com may be observable. |
| Detection Difficulty | Hard | The worm carries valid SLSA Build Level 3 provenance, making it indistinguishable from clean releases to automated security tooling. It uses the Bun runtime to bypass Node.js-focused EDR monitoring. C2 domains are dynamically resolved via Ethereum smart contract, defeating static blocklists. Each infection can generate unique payloads to defeat hash-based detection. Persistence hooks in IDE config files are not typically monitored by security tools. |
Required Log Sources
- Process creation and execution logs (EDR/Sysmon Event ID 1)
- File creation and modification events (Sysmon Event ID 11, EDR file telemetry)
- Network connection events (Sysmon Event ID 3, firewall/proxy logs)
- DNS query logs for npm-cache.com and Ethereum RPC endpoints
- npm install execution logs and package.json modification events
- GitHub Actions workflow execution logs
- GitHub audit logs for repository creation and package publishing events
- File access events for ~/.npmrc, ~/.aws/credentials, ~/.kube/config, ~/.ssh/
Hunting Hypotheses
| Hypothesis | Telemetry | ATT&CK Stage | FP Risk |
|---|---|---|---|
| Consider hunting for processes executing bun.exe or bun in development environments or CI runners where Node.js is the expected runtime, as ChainDrop uses Bun specifically to evade Node.js monitoring | Process execution logs showing bun.exe or bun spawning from npm install contexts or package directories | Execution | Medium - Bun is a legitimate runtime that developers may use intentionally; correlate with npm install activity and unexpected file creation |
| Consider hunting for processes accessing multiple credential file locations in rapid succession, such as ~/.npmrc, ~/.aws/credentials, ~/.kube/config, and ~/.ssh/ within a short time window, as this pattern indicates the credential harvesting behavior of Math_Symbol.js | File access events from EDR or Sysmon showing a single process reading multiple credential stores | Credential Access | Low - legitimate development tools rarely access all credential stores simultaneously in a single execution |
| Consider hunting for GitHub API calls occurring at regular 60-second intervals from developer workstations or CI runners, as this pattern matches the gh-token-monitor daemon polling for token revocation status | Network connection logs or proxy logs showing periodic GitHub API requests at 60-second intervals from a single host | Defense Evasion | Medium - some legitimate developer tools may poll GitHub APIs; look for the specific 60-second interval pattern and background process context |
| Consider hunting for newly created GitHub repositories with descriptions containing 'Shai-Hulud' or the reversed string 'niagA oG eW ereH :duluH-iahS', as these are exfiltration dead-drop repositories created under victim identities | GitHub audit logs or API logs showing repository creation events with matching description strings | Exfiltration | Low - the specific description strings are unlikely to appear in legitimate repository descriptions |
| Consider hunting for eth_call JSON-RPC requests to Ethereum RPC endpoints originating from developer workstations or CI runners, as ChainDrop uses Ethereum smart contract calls to retrieve C2 domains | Network proxy logs or DNS logs showing requests to Ethereum RPC endpoints (e.g., infura.io, alchemy.com) from development environments | Command and Control | Medium - developers working with blockchain applications may make legitimate eth_call requests; correlate with the specific contract address if possible |
Control Gaps
- SLSA provenance verification does not detect this attack because the attacker controls the source repository and the build pipeline produces valid attestations
- Domain-based blocklists are ineffective because C2 domains are dynamically resolved via Ethereum smart contract
- Node.js-focused EDR monitoring is bypassed by executing payloads via the Bun runtime
- The --ignore-scripts npm flag does not prevent execution because persistence hooks in IDE config files trigger payload re-execution without npm install
- Hash-based detection is defeated by unique payload generation per infection in some variants
- Package removal does not remediate the infection because persistence hooks in .claude/settings.json and .vscode/tasks.json survive cleanup
Key Behavioral Indicators
- Preinstall hook in package.json referencing setup.mjs or Math_Symbol.js
- Bun runtime execution originating from npm install or package directory contexts
- Background process named gh-token-monitor polling GitHub API at 60-second intervals
- GitHub repositories created with description 'Shai-Hulud: Here We Go Again' or reversed variant
- eth_call JSON-RPC requests to Ethereum smart contract 0xE1f2395ee43e45A1556EC6438a88c31B83493103
- Unexpected modifications to .claude/settings.json or .vscode/tasks.json containing references to setup.mjs
- Process accessing ~/.npmrc, ~/.aws/credentials, ~/.kube/config, and ~/.ssh/ in rapid succession
- npm package version bumps with preinstall hooks added between versions
- Russian locale check behavior where process exits on detecting ru_RU locale
False Positive Assessment
Medium - The Bun runtime is a legitimate tool used by developers, and eth_call requests to Ethereum RPC endpoints may occur in blockchain development environments. GitHub API polling at regular intervals may be performed by legitimate developer tools. However, the combination of bun.exe execution following npm install, rapid credential file access, and the specific repository description strings provide strong correlation signals with low false positive risk.
Recommendations
Immediate Mitigation
- Verify against your organization's incident response runbook and team escalation paths before acting. Consider auditing all development environments and CI runners for the presence of setup.mjs, Math_Symbol.js, or unexpected modifications to .claude/settings.json and .vscode/tasks.json.
- Consider revoking and rotating all npm tokens, GitHub PATs, cloud credentials, Kubernetes service account tokens, HashiCorp Vault tokens, and CI/CD secrets on any system that may have executed affected packages. Be aware that token revocation may trigger the destructive wiper.
- If token revocation is necessary, consider first isolating the affected host or taking filesystem snapshots to preserve evidence and prevent the rm -rf ~/ wiper from executing.
- Consider blocking network access to npm-cache.com and monitoring for eth_call requests to the Ethereum smart contract address 0xE1f2395ee43e45A1556EC6438a88c31B83493103.
- Consider pinning all dependencies using lockfiles (package-lock.json, pnpm-lock.yaml) and using npm ci instead of npm install in all CI environments.
Infrastructure Hardening
- Consider implementing private registry proxies with Software Composition Analysis (SCA) filtering to intercept and inspect third-party packages before they reach development environments.
- Evaluate whether restricting outbound network access from CI build environments would reduce exfiltration paths without breaking legitimate build requirements.
- Consider enforcing a release cooldown period for newly published packages to delay adoption and allow time for emerging supply chain attacks to be detected.
- Consider auditing pull_request_target usage in GitHub Actions workflows and restricting privileged operations and secret access to non-fork contexts.
- Consider monitoring repositories with publish permissions for orphan commits and unexpected workflow files or new files in main branches.
- Consider layering SLSA/Sigstore provenance verification with anomaly detection on publishing behavior such as off-hours publishing, bulk version publishing, and first-time publishers.
User Protection
- Consider enabling phishing-resistant multifactor authentication (FIDO2, WebAuthn) on npm, GitHub, and cloud platforms, as the campaign harvested live TOTP codes via adversary-in-the-middle phishing.
- Consider applying least-privilege principles using scoped, short-lived keys and tokens for npm publishing and CI/CD workflows.
- Consider treating IDE and AI-agent configuration files (.claude/, .cursor/, .vscode/, .gemini/) as executable code and reviewing them with the same rigor as source files.
- Consider restricting open-source package consumption on corporate devices to enterprise-approved package managers and blocking access to internet package managers where supported by your tooling.
Security Awareness
- Consider incorporating training on supply chain attack indicators into existing developer security awareness programs, focusing on unexpected package.json modifications and preinstall hooks.
- Consider educating developers that SLSA provenance validates the build process but does not guarantee the safety of the content or the identity of the account running the CI system.
- Consider adding guidance for developers to report unexpected files in project directories, particularly setup.mjs, Math_Symbol.js, or unexpected modifications to IDE configuration files.
- Consider rolling into existing awareness programs the recommendation to not treat scanner safety refusals or absence of verdict as a pass, particularly for AI-based security scanning pipelines.
MITRE ATT&CK Mapping
Initial Access
Execution
Persistence
Stealth
Credential Access
Command and Control
Exfiltration
Impact
Additional IOCs
- File Paths:
.claude/settings.json- Persistence hook planted by ChainDrop to execute node .vscode/setup.mjs at the start of a Claude Code session; survives package removal.vscode/tasks.json- Persistence hook planted by ChainDrop to execute the payload when a project folder is opened in VS Code; survives package removal.github/copilot-instructions.md- Used in some variants to inject instructions into the GitHub Copilot chat context for persistence or manipulation
- Command Lines:
- Purpose: Destructive wiper triggered when the gh-token-monitor daemon detects token revocation (4xx API response); deletes entire home directory including source code, SSH keys, and cloud credentials | Tools:
rm| Stage: Impact |rm -rf - Purpose: Execute obfuscated Math_Symbol.js payload via Bun runtime to bypass Node.js child-process monitoring by EDR and security tools | Tools:
bun.exe,bun| Stage: Execution |bun.exe
- Purpose: Destructive wiper triggered when the gh-token-monitor daemon detects token revocation (4xx API response); deletes entire home directory including source code, SSH keys, and cloud credentials | Tools:
- Other:
gh-token-monitor- Background process installed by the worm that polls the GitHub API every 60 seconds using the stolen token; triggers destructive wiper on 4xx response indicating token revocationShai-Hulud: Here We Go Again- GitHub repository description string used to identify exfiltration dead-drop repositories created under the victim's identityniagA oG eW ereH :duluH-iahS- Reversed variant of the GitHub repository description string used in some ChainDrop infections for exfiltration dead-drop identificationcacheable- npm package poisoned by ChainDrop through the compromised keyv maintainer account; specific version not stated in articleflat-cache- npm package poisoned by ChainDrop through the compromised keyv maintainer account; specific version not stated in articlefile-entry-cache- npm package poisoned by ChainDrop through the compromised keyv maintainer account; specific version not stated in article