On August 4, 2026, a sophisticated software supply chain attack dubbed ChainDrop struck the npm ecosystem through the compromise of a maintainer's GitHub account for the widely used Keyv and Cacheable open-source packages. The resulting self-propagating worm spread to over 2251 versions of 452 unique packages with approximately 2 billion monthly downloads, affecting organisations including Deliveroo, Ornikar, OneReach, Picsart, Qlik, and ServiceTitan.

Keyv is a simple key-value store for Node.js with support for multiple backends, while Cacheable is its underlying HTTP cache framework. Together, these packages serve as foundational caching infrastructure across the JavaScript ecosystem. Both packages are maintained by the same developer, whose GitHub account was compromised by the threat actor.

The attacker published the first malicious release, keyv@6.0.0, at approximately 9:00 UTC on August 4, 2026. The malware is a descendant of the "Mini" Shai-Hulud family, attribution to TeamPCP remains unconfirmed. The worm propagated rapidly by harvesting npm and GitHub tokens from compromised environments and using them to publish further poisoned releases. Socket verified 2251 poisoned versions across 452 packages.

The payload steals a comprehensive range of sensitive data including cloud credentials, CI/CD secrets, developer tokens, AI configuration files, and cryptocurrency wallets. It also plants Claude Code and Visual Studio Code hooks for persistent IDE-based execution. No CVE identifiers have been assigned to this campaign at the time of writing.

 

Vulnerability Overview

Initial Access

The threat actor compromised the GitHub account of the Keyv maintainer, granting direct push access to the source repositories for Keyv, Cacheable, cache-manager, flat-cache, file-entry-cache, and numerous related packages within the maintainer's monorepo.

Malicious Publication

Starting at 9:00 UTC, the attacker introduced IDE persistence payloads to the Keyv repository, then published keyv@6.0.0 and subsequent malicious versions. Because the packages were built and published through their legitimate GitHub Actions workflows, the compromised releases carried valid SLSA provenance information, bypassing integrity verification controls. Each poisoned package contained a "preinstall": "node setup.mjs" entry in its package.json, causing the dropper to execute automatically before any npm install completed.

Worm Propagation

The malware's self-spreading capability allowed it to infect packages belonging to other maintainers whose projects depended on a previously compromised package. By harvesting valid npm publishing tokens from CI/CD runners and developer workstations, the worm republished downstream packages with the malicious payload injected, creating a cascading chain of compromise. This mechanism enabled the attack to escape the original Keyv/Cacheable namespace and propagate across organisational boundaries.

Affected Packages

The core compromised packages included:

Package Name

Malicious Affected Version(s)

Verified Safe / Restored Clean Version

keyv

6.0.0 (and subsequent malicious iterations)

5.6.0

flat-cache

6.1.24

6.1.23

file-entry-cache

11.1.6

11.1.5 (or previous stable version)

cacheable-request

13.0.20

13.0.19 (or previous stable version)

cache-manager

7.2.10

7.2.9

cacheable

2.5.1

2.5.0 (or previous stable version)

@cacheable/utils

2.5.1

2.5.0 (or previous stable version)

@cacheable/memory

2.2.1

2.2.0 (or previous stable version)

@cacheable/node-cache

3.1.2

3.1.1 (or previous stable version)

@cacheable/net

2.1.1

2.1.0 (or previous stable version)

ecto

5.0.1

5.0.0 (or previous stable version)

The worm subsequently spread to packages within organisations such as Ornikar (including @ornikar/eslint-config-*, @ornikar/prettier-config, @ornikar/babel-preset-*, and many more), Qlik (@nebula.js/nucleus), HubSync (@hubsync/web-sdk-react), and numerous others. A full, continuously updated list is maintained by Wiz Research on their public GitHub repository.

Important: The npm registry changed rapidly throughout the incident. At 5:40 p.m. IST on August 4, earlier clean versions were restored as latest for at least nine core packages, including keyv@5.6.0, flat-cache@6.1.23, and cache-manager@7.2.9. Organisations must verify exposure using exact package versions resolved in lockfiles, not current registry tags.

 

Malware Analysis

Payload Overview

The malware deploys in a two-stage architecture:

Stage 1 - setup.mjs (Dropper): This lightweight Node.js script serves as the initial payload. It checks for the Bun JavaScript runtime and, if absent, downloads Bun v1.3.13 from the official GitHub releases. It then hands execution off to the main infostealer bundle before deleting the temporary runtime directory to reduce forensic footprint.

Stage 2 - Math_Symbol.js / math_init.js (Infostealer): A heavily obfuscated, 727,680-byte compiled bundle executed via the Bun runtime. This is the core credential-harvesting engine. Both filenames (Math_Symbol.js and math_init.js) have been observed in the wild, containing identical functionality at the same SHA-1 hash.

Credential Harvesting Targets

The infostealer systematically enumerates and exfiltrates:

  • Cloud credentials: AWS access keys, SSM Parameter Store values (with WithDecryption: true), AWS Secrets Manager secrets, Azure and GCP credentials
  • CI/CD secrets: GitHub Actions secrets, including extraction of "isSecret":true values from self-hosted runner memory, GitHub workflow tokens
  • Developer tokens: GitHub Personal Access Tokens (ghp_, gho_, ghs_ prefixes), npm publishing tokens (npm_ prefix), every token validated in real-time against registry.npmjs.org/-/whoami before exfiltration
  • Infrastructure secrets: Kubernetes secrets from all accessible namespaces, HashiCorp Vault tokens and KV secrets, Terraform credentials, SSH keys, database connection strings
  • AI and IDE configuration: Claude API configuration files, AI-related service credentials
  • Cryptocurrency wallets: Local wallet files and keys
  • Service credentials: Stripe, Slack, Twilio, and other third-party API keys
  • Complete process environment: All environment variables from the compromised process

Persistence Mechanisms

The Keyv repository retained separate Claude Code (.claude/settings.json) and Visual Studio Code (.vscode/) hooks. These IDE-level persistence mechanisms can execute the payload once a user trusts the workspace or permits the project configuration, providing a secondary execution path independent of npm install lifecycle scripts.

The malware also installs a credential-revocation watcher — a trap that triggers an attacker-supplied local handler when tokens are rotated. Responders must remove this watcher before rotating any credentials.

Command and Control

The malware retrieves its C2 domains from an Ethereum smart contract (StringListStore) using an eth_call rather than embedding them in the payload. This blockchain-based C2 resolution allows the operator to update infrastructure without modifying the malware. On-chain history shows the contract was initially configured with three domains before being updated to return only npm-cache[.]com. The contract owner was funded by an address previously flagged for scam activity.

Exfiltration

Stolen data is encrypted using a hybrid AES-256 and RSA-4096 scheme, then exfiltrated through two channels:

  • Public GitHub repositories created under compromised identities, bearing the description "Shai-Hulud: Here We Go Again"
  • The domain npm-cache[.]com

The initial commits to exfiltration repositories contain the intimidation string: "IfYouBlockThisAPIKeyItWillCrashTheLiveProductionServersOfAllThirdPartyClients."

 

Known Indicators of Compromise (IoCs)

Category

Indicator

Description

Domain

npm-cache[.]com

Primary C2 and data exfiltration domain; served via Cloudflare

Domain

registry[.]npmjs[.]org

Token validation endpoint abused for live credential verification prior to exfiltration

Domain

eth-mainnet[.]nodereal[.]io

Ethereum RPC endpoint used for smart-contract-based C2 domain retrieval

Domain

go[.]getblock[.]io

Ethereum RPC endpoint used for smart-contract-based C2 domain retrieval

Domain

eth[.]llamarpc[.]com

Ethereum RPC endpoint used for smart-contract-based C2 domain retrieval

Domain

pypi-get[.]com

Additional attacker-controlled infrastructure

Domain

js-mirror[.]com

Additional attacker-controlled infrastructure

IPv4

104[.]21[.]35[.]216

Cloudflare IP associated with npm-cache[.]com

IPv4

172[.]67[.]167[.]200

Cloudflare IP associated with eth[.]llamarpc[.]com

IPv4

185[.]44[.]207[.]215

IP associated with go[.]getblock[.]io ETH RPC

IPv4

35[.]175[.]164[.]77

AWS IP associated with eth-mainnet[.]nodereal[.]io

Filename

Math_Symbol.js

Obfuscated infostealer payload (727,680 bytes); deployed via npm package preinstall scripts

Filename

math_init.js

Variant of the infostealer payload with identical functionality

Filename

setup.mjs

Stage-1 dropper; downloads Bun runtime and triggers infostealer execution

File Hash (SHA-1)

35a672cf34b996b91f3e1c28cbf3a05a37e036e4

Math_Symbol.js / math_init.js infostealer payload

File Hash (SHA-1)

686aa40d0fc22c8d569494543a0f891f359f2f99

setup.mjs located in .claude directory (Claude Code hook)

File Hash (SHA-1)

f525d52ceb966516686b482d3dc0137028cc6a63

setup.mjs located in .vscode directory (VS Code hook)

User-Agent

Bun/1.3.13

HTTP user-agent string used during Bun runtime download and C2 communications

Filesystem Path

/tmp/bun-dl-*/

Temporary directory used for Bun runtime download and staging

Filesystem Path

node_modules/keyv/Math_Symbol.js

Location of the infostealer payload within the installed package tree

String

Shai-Hulud: Here We Go Again

GitHub repository description used for exfiltration repos

String

IfYouBlockThisAPIKeyItWillCrashTheLiveProductionServersOfAllThirdPartyClients

Intimidation string in initial exfiltration commits

 

 

Detections and Threat Hunting

Security teams should implement the following detection measures:

  • Dependency audit: Scan all package-lock.json, yarn.lock, pnpm-lock.yaml, and bun.lockb files for the affected packages and versions. Do not rely on current registry tags — use resolved versions from lockfiles exclusively.
  • Filesystem indicators: Search development workstations, CI/CD runners, and build servers for the presence of Math_Symbol.js, math_init.js, and setup.mjs within node_modules trees and temporary directories matching /tmp/bun-dl-*/.
  • Network monitoring: Alert on outbound connections and DNS queries to npm-cache[.]com, pypi-get[.]com, js-mirror[.]com, and the Ethereum RPC endpoints listed in the IOCs above. Monitor for the Bun/1.3.13 user-agent string.
  • GitHub audit: Review organisations for unexpected public repositories with the description "Shai-Hulud: Here We Go Again" or commits containing the intimidation string.
  • Preinstall script monitoring: Audit package.json files across all projects for unexpected preinstall script entries referencing setup.mjs.
  • IDE workspace trust: Check for unauthorised .claude/settings.json and .vscode/ configurations that may load external payloads.

 

Mitigations and Remediations

Immediate Actions

  • Identify and remove affected versions: Audit all environments and remove any installation of the malicious package versions. Upgrade to clean, verified versions.
  • Remove the credential-revocation watcher first: Do not rotate tokens before removing the malware. The payload installs a watcher that triggers on token revocation. Rotating credentials prematurely can execute an attacker-supplied handler. Eradicate the malware from affected systems before proceeding to credential rotation.
  • Assume full credential compromise: If any affected version was installed, treat all credentials accessible to that environment as stolen. This includes cloud credentials, npm tokens, GitHub PATs, SSH keys, CI/CD secrets, database credentials, and third-party API keys.
  • Rotate all exposed credentials: After removing the malware, rotate: all npm publishing tokens, GitHub Personal Access Tokens and OAuth tokens, AWS IAM access keys, Azure and GCP service principal credentials, Kubernetes service account tokens, HashiCorp Vault tokens, database passwords, SSH keys, and third-party API keys (Stripe, Slack, Twilio, etc.).
  • Revoke and reissue npm/GitHub tokens: Use the npm and GitHub admin consoles to forcibly revoke all potentially exposed tokens. Generate new tokens with minimal required scopes.

System Recovery

  • Rebuild affected systems: Treat any development workstation, CI/CD runner, or build server where a malicious package was installed as potentially compromised. Rebuild from a known-clean image or snapshot.
  • Purge local caches: Clear npm cache (npm cache clean --force), Yarn cache, pnpm store, and Bun cache to eliminate cached malicious packages.
  • Delete and recreate virtual environments: Do not simply downgrade affected packages. Delete node_modules directories and recreate Python virtual environments entirely.

Cloud and Source Code Review

  • Audit cloud environments: Review AWS CloudTrail, Azure Activity Logs, and GCP Audit Logs for unauthorised API calls originating from the timeframe of package installation. Check for unexpected IAM user or role creation, SSM Parameter Store access with decryption, and Secrets Manager access.
  • Review GitHub audit logs: Examine organisation audit logs for unexpected repository creation, token generation, and workflow modifications.
  • Scan for exfiltration repositories: Search for public repositories created under your organisation's identities with the description "Shai-Hulud: Here We Go Again."

Supply Chain Hardening

  • Pin dependencies: Use exact version pinning in all package.json files and commit lockfiles to version control.
  • Upgrade to npm 12: npm 12 blocks unapproved dependency lifecycle scripts by default, preventing automatic preinstall script execution. Earlier npm clients remain vulnerable.
  • Enforce SLSA and OIDC: Require SLSA provenance attestations and verify them at build time. However, note that this campaign demonstrated that provenance alone is insufficient when the attacker controls the build pipeline — provenance must be combined with identity verification.
  • Enable dependency allowlisting: Restrict allowed package sources and versions through tools such as Socket, Semgrep Supply Chain, or your artifact registry's policies.
  • Implement package integrity verification: Use npm audit signatures and verify registry integrity hashes where supported.
  • Enable MFA on all maintainer accounts: Require hardware-backed MFA for all npm and GitHub accounts with publishing permissions.
  • Block malicious domains: Add the IOCs listed above to your network perimeter blocklists and DNS filtering solutions.

If you are worried about any of the threats outlined in this bulletin or need help in determining what steps you should take to protect yourself from the most material threats facing your organisation, please contact your account manager, or alternatively get in touch to find out how you can protect your organisation.

 

Contact Us