ChainDrop is a self-propagating npm supply-chain worm in the Shai-Hulud code lineage, identified in August 2026. Its campaign began with the compromise of a maintainer associated with the keyv ecosystem and spread to more than 400 npm packages. It targets developer workstations and CI/CD environments on Linux, macOS, and Windows, using compromised publishing identities and legitimate release infrastructure to distribute infected dependencies. Some malicious releases retained valid provenance attestations because they passed through authorized publishing workflows.
Infected packages preserve their legitimate functionality while adding a malicious preinstall hook, a dropper, and an obfuscated JavaScript payload. The dropper downloads the legitimate Bun runtime when necessary and uses it to execute the payload. ChainDrop harvests npm and GitHub tokens, cloud credentials, SSH keys, Kubernetes and Vault tokens, infrastructure configuration data, and AI development-tool secrets. It searches local files, environment variables, cloud metadata services, and GitHub Actions runner memory, including for short-lived OIDC tokens. It also validates credentials and enumerates accessible repositories, cloud resources, and permissions.
ChainDrop propagates by using stolen npm publishing credentials to retrieve writable packages, inject its payload, increment package versions, and republish infected releases. It also modifies accessible repositories to install VS Code folder-opening tasks and Claude Code session-start hooks, providing persistence and execution paths independent of package installation. Collected data is compressed and encrypted before exfiltration. The malware uses an Ethereum smart contract to resolve current command-and-control destinations, allowing operators to rotate infrastructure without updating infected packages. GitHub commit history provides a fallback resolution mechanism, and public repositories created under compromised accounts provide an additional exfiltration channel.
2 distinct threat actors Mallory has associated with this family across public reporting. Open in Mallory to see the full evidence chain and overlapping campaigns.
On August 4, 2026, a self-propagating worm called ChainDrop entered the npm ecosystem through a compromised maintainer account. ChainDrop is a variant of Mini Shai-Hulud linked to TeamPCP.
The attackers trojanized the monorepo and embedded a self-propagating worm called CHAINDROP that uses stolen npm credentials to automatically backdoor every other package the maintainer had publish rights to.
62 distinct techniques documented for this family, organized by ATT&CK tactic.
The typosquat is the injected dependency name @opensearch/setup, which imitates the real @opensearch-project scope.
Before publishing, the worm: Requests a second OIDC token (audience sigstore this time) Obtains a Fulcio certificate Builds an in-toto SLSA v1 provenance statement... DSSE-signs it with an ephemeral P-256 key Uploads the entry to the public Rekor transparency log
The worm component activates when the credential sweep turns up an npm token... Once a qualifying token is found, the worm enumerates every package the victim has publish rights to.
T1078.001 — Valid Accounts: Default Accounts (Defense Evasion)
If it finds any, it queries the GitHub API to list all accessible repositories and branches
Rather than just hunting for bugs in finished software, attackers are targeting the everyday tools and code developers rely on. Unit 42 research shows this happening at every step of the building process.
The operation targeted widely used deep infrastructure dependencies, such as keyv, flat-cache and cache-manager.
Attackers trojanized the keyv monorepo and embedded a self-propagating worm called CHAINDROP that uses stolen npm credentials to automatically backdoor every other package the maintainer had publish rights to.
T1199 — Trusted Relationship (Initial Access)
File path ~/.local/bin/gh-token-monitor.sh Latent Linux persistence artifact
Execution: Used a preinstall hook to download the Bun runtime and execute the obfuscated Math_Symbol.js payload. | Downloads the Bun JavaScript runtime v1.3.13 directly from its official GitHub release page. Launches the second-stage payload via Bun.
File path ~/.local/bin/gh-token-monitor.sh Latent Linux persistence artifact
a hidden Python script directly read live process memory from GitHub Actions runners
Execution is triggered via a preinstall hook in package.json. This abuses a legitimate npm feature that will run arbitrary commands before a package is installed.
Even if you never install an infected package (“ npm install ” in npm argot), you can still get the nasties – though that is one possible route of infection.
Persistence hooks planted in .claude/settings.json and .vscode/tasks.json survive package removal. The payload re-executes simply by opening the project in Visual Studio (VS) Code or Claude Code.
Attackers modified package manifests with a malicious preinstall script that downloaded the legitimate Bun runtime to silently launch a 727 KB obfuscated payload in the background.
Execution is triggered via a preinstall hook in package.json. This abuses a legitimate npm feature that will run arbitrary commands before a package is installed, requiring no further interaction from the victim after the installation.
File path ~/.local/bin/gh-token-monitor.sh Latent Linux persistence artifact
The worm component activates when the credential sweep turns up an npm token... Once a qualifying token is found, the worm enumerates every package the victim has publish rights to.
T1078.001 — Valid Accounts: Default Accounts (Defense Evasion)
If it finds any, it queries the GitHub API to list all accessible repositories and branches
File path ~/.config/systemd/user/gh-token-monitor.service Latent Linux systemd persistence artifact
File path ~/Library/LaunchAgents/com.user.gh-token-monitor.plist Latent macOS LaunchAgent persistence artifact
File path ~/.config/systemd/user/gh-token-monitor.service Latent Linux systemd persistence artifact
The threat actor has placed a SessionStart hook in settings.json under the Claude directory... For VS Code, a folderOpen task is used under tasks.json... when an infected repository is opened.
File path ~/.local/bin/gh-token-monitor.sh Latent Linux persistence artifact
a hidden Python script directly read live process memory from GitHub Actions runners to steal temporary OpenID Connect (OIDC) tokens and secrets
The article states the malware accessed /proc/<pid>/mem to extract GitHub Actions secrets from running processes and account data.
The worm component activates when the credential sweep turns up an npm token... Once a qualifying token is found, the worm enumerates every package the victim has publish rights to.
T1078.001 — Valid Accounts: Default Accounts (Defense Evasion)
If it finds any, it queries the GitHub API to list all accessible repositories and branches
File path ~/.config/systemd/user/gh-token-monitor.service Latent Linux systemd persistence artifact
File path ~/Library/LaunchAgents/com.user.gh-token-monitor.plist Latent macOS LaunchAgent persistence artifact
File path ~/.config/systemd/user/gh-token-monitor.service Latent Linux systemd persistence artifact
The threat actor has placed a SessionStart hook in settings.json under the Claude directory... For VS Code, a folderOpen task is used under tasks.json... when an infected repository is opened.
The payload is heavily obfuscated at 711 kilobytes, employing control-flow flattening with a string encoding scheme using Base91.
a hidden Python script directly read live process memory from GitHub Actions runners to steal temporary OpenID Connect (OIDC) tokens and secrets
The article states the malware accessed /proc/<pid>/mem to extract GitHub Actions secrets from running processes and account data.
Revoking a stolen token triggers the daemon to execute rm -rf ~/, wiping the victim’s entire home directory including source code, SSH keys, and cloud credentials.
After execution, the bun temporary directory is deleted to cover its tracks.
The worm component activates when the credential sweep turns up an npm token... Once a qualifying token is found, the worm enumerates every package the victim has publish rights to.
T1078.001 — Valid Accounts: Default Accounts (Defense Evasion)
If it finds any, it queries the GitHub API to list all accessible repositories and branches
Checks for a Russian-language system locale; if found, it exits without executing. This is a common evasion technique used to avoid targeting certain regions.
Persistence hooks planted in .claude/settings.json and .vscode/tasks.json survive package removal. The payload re-executes simply by opening the project in Visual Studio (VS) Code or Claude Code.
Attackers modified package manifests with a malicious preinstall script that downloaded the legitimate Bun runtime to silently launch a 727 KB obfuscated payload in the background.
Execution is triggered via a preinstall hook in package.json. This abuses a legitimate npm feature that will run arbitrary commands before a package is installed, requiring no further interaction from the victim after the installation.
It looks in shell configurations, environment variables and even live memory.
When executed, the software scours the user’s workspace for npm tokens with full write privileges
The malware steals GitHub tokens, cloud credentials, API keys, and other secrets
Its collector scanned for more than 300 credential patterns, including OpenAI, Anthropic and Cursor keys.
It looks in shell configurations, environment variables and even live memory.
It looks in shell configurations, environment variables and even live memory.
The payload has a component called collector that functions as a credential harvester... Many other credentials are targeted, such as GitHub ... SSH private keys, Kubernetes service account tokens, and npm tokens.
The malware also targets cloud provider credentials from AWS, GCP, Azure, and Alibaba Cloud.
The payload has a component called collector that functions as a credential harvester, scanning over 300 unique patterns across many credential stores found on a developer machine.
It parses local config files for npm and GitHub credentials
a hidden Python script directly read live process memory from GitHub Actions runners to steal temporary OpenID Connect (OIDC) tokens and secrets
The malicious packages contain an infostealer designed to harvest... and perform a generic file system scan.
Checks for a Russian-language system locale; if found, it exits without executing. This is a common evasion technique used to avoid targeting certain regions.
Upon execution, the malware determines whether it is operating on a developer workstation or within a CI/CD environment and adjusts its behavior to maximize credential collection opportunities available in each environment.
If it finds any, it queries the GitHub API to list all accessible repositories and branches
The malware can also alter repositories when it captures a GitHub App token. It adds malicious Claude Code and VS Code configuration files across as many as 50 branches, so opening an infected repository can create another execution route.
Малварь ChainDrop собирает с зараженных машин переменные окружения, локальные конфигурационные файлы и учетные данные разработчиков.
A separate routine in the same payload searches GitHub's commit API for that marker, double-decodes every match and keeps any token that passes a repository-scope check.
After the credentials are stolen, they are gzip-compressed and then encrypted with a randomly generated AES-256-GCM key.
It also retrieves its command destination through an Ethereum smart contract, letting the operator change the destination without updating infected packages.
ChainDrop fans out across three RPC providers ( eth.llamarpc.com , go.getblock.io , eth-mainnet.nodereal.io ), so a rate-limited or downed endpoint does not break resolution.
The C2 infrastructure is anchored in an Ethereum smart contract. The attacker can rotate C2 domains without modifying the deployed malware, making blocklists and DNS sinkholes ineffective.
Everything goes to hxxps://npm-cache[.]com:443/router over TLS.
Even its command infrastructure was parked inside an Ethereum smart contract, rendering any domain blocklists moot.
its command-and-control endpoint uses an Ethereum dead-drop, letting operators repoint infrastructure without shipping a hardcoded domain.
If these two previous paths fail, CHAINDROP will exfiltrate the data via a public GitHub repository ... using the compromised victim's account.
The dropper will detect the platform/architecture of the machine and download bun v1.3.13 directly from the official release page. It will extract bun, then use it to execute the payload.
The ABI-decoded response holds the C2 pointer. Operators pick the encoding: a plain UTF-8 URL, Base64, Base64 plus XOR, gzip plus Base64, or a bash one-liner.
CHAINDROP does not hardcode a C2 domain; instead, it queries an Ethereum smart contract... to retrieve the current exfiltration endpoint at runtime.
That AES key is then RSA-encrypted with the attacker's hardcoded public key. Only the attacker who holds the corresponding private key can decrypt the data.
The dropped bun binary posts a 136-byte eth_call body to Ethereum mainnet RPC providers, reads the contract, then exfiltrates to the resolved domain.
Once stolen, they’re encrypted and sent to a public GitHub repository with the description "Shai-Hulud: Here We Go Again."
Primary - GitHub dead-drop repositories: The worm uses the victim’s own GitHub token to create a new public repository under their identity.
The worm also plants a .github/workflows/codeql_analysis.yml file that serializes ${{ toJSON(secrets) }} and uploads it as an Actions artifact, another path to exfiltrate repository secrets. And it creates public repositories under the victim's GitHub account... using them as an additional exfiltration channel.
When a GitHub App token is among the stolen credentials, the worm extends this further by committing malicious hooks to up to 50 branches per accessible repository, injecting .claude/settings.json and .vscode/tasks.json into each.
28 indicators attributed across vendor reports, sandbox runs, and researcher write-ups. Values are masked here and available in full in Mallory.
32 sources tracked across advisories, community write-ups, and news. New activity surfaces here as Mallory finds it.