Back to Blog

ChainDrop: 444 npm packages compromised and not a single patch to apply

It is not a code flaw. It is a stolen account.
ChainDrop · 444 packages · 4 August 2026

We have spent the summer arguing about whether autonomous agents are good for anything. On 4 August, between 09:35 and 13:20 UTC, a program without a single line of intelligence did something on its own, unsupervised, in under four hours, that no corporate pilot has managed: it compromised 444 npm packages, spread using the credentials it stole along the way and, while it was at it, helped itself to the AI tool keys of the developers it infected. It is called ChainDrop. It is not an agent: it is a worm. And what matters for your company is that there is no patch to apply here.

The GitHub account of the keyv maintainer — a key-value storage library almost nobody installs on purpose, because it arrives as a dependency of a dependency — was compromised. With that access, the attacker pushed malicious code to the main branch and let the legitimate release pipeline do the rest. At 09:35 UTC, [email protected] shipped. From 09:38 until 13:20, a fully automated second wave reached another 433 packages using the credentials the first one had harvested.

The numbers deserve their caveats, because not everyone counts the same thing. On the package count there is agreement: 444. On versions there is not: StepSecurity counts 2,212 and Aikido 1,381, most likely because each of them stopped counting at a different moment in a campaign that was still moving. Microsoft describes it cautiously as "more than 400 packages across unrelated publishers". The three highest-reach packages, per StepSecurity, are keyv (153.7 million weekly downloads), flat-cache (149.9) and file-entry-cache (147.6). Aikido adds them up at over 2 billion monthly installs. Nobody has counted how many companies sit behind those installs; what that figure measures is the bore of the pipe it slipped through.

The signature was valid. And it was telling the truth.

Here is the detail we find most important in the whole episode, and the one that has been covered least. [email protected] was published through trusted publishing with OIDC and shipped with valid provenance: the cryptographic attestation certifying which repository and which workflow built that artifact. Nobody forged anything: the official pipeline compiled, signed and published code that arrived from the repository already trojanised.

Socket puts it in a sentence worth copying onto the whiteboard: provenance attests build integrity, not source integrity. It is the difference between the notary's seal and the contents of the contract. The notary certifies that you signed, in their office, on that day. It does not certify that you knew what you were signing. Provenance answers "did the official workflow build this from that commit?" — yes — and does not answer "was that commit written by the person it should have been?".

The practical consequence is uncomfortable and worth saying out loud: if your dependency policy is "we only install packages with verified provenance", on 4 August that policy waved this through. There is no shame in admitting it; provenance raises the bar and should still be demanded. But it is not a seatbelt, and selling it as one — which a fair amount of marketing material does — leaves plenty of people reassured for the wrong reason.

The code runs before you decide anything

Every compromised package received two files — setup.mjs, the loader, and Math_Symbol.js, 727,680 bytes, which in the packages reached by the second wave appears as math_init.js — plus one line in package.json: "preinstall": "node setup.mjs". That line is the entire attack. npm runs preinstall scripts before the install completes: before your tests, before your dependency analysis, before anyone looks at a diff. On the developer laptop and on the CI runner, exactly the same.

The mitigation fits in a flag: npm ci --ignore-scripts. And since we do not like selling shortcuts, the caveat: it is not free. Packages with native extensions that compile at install time stop working, so you have to identify them and allow them one by one. That is one-off work that does not come back, and it is the difference between "I downloaded malicious code onto disk" and "I ran it as my user".

It downloaded Bun from GitHub, which is why your proxy said nothing

The loader did not bring its own interpreter: it downloaded a legitimate Bun v1.3.13 from github.com/oven-sh/bun/releases, with the right artifact per platform (Linux x64 with glibc and with musl, Linux arm64, macOS and Windows). This is living off the land applied to telemetry: the network indicator left in your logs points at github.com, a domain nobody blocks and which on a development machine sees traffic all day long.

If your detection leans on "connections to odd domains", nothing rings here. What is anomalous and can be searched for: a bun process running on a runner for a project that has never used Bun, and new files named after a maths utility inside node_modules. The signal exists, and you have to go looking for it in process behaviour.

What it took, and the line almost nobody has written down

The sweep was wide, and each shop measures it differently: StepSecurity mentions around 140 credential paths, Aikido around 200 search patterns and Elastic more than 300 unique patterns. Different methodologies, same conclusion: it was not looking for one specific thing, it was looking for everything. npm and GitHub CLI tokens, AWS credentials — including Secrets Manager and SSM across sixteen regions — Google Cloud, Azure, HashiCorp Vault tokens, Kubernetes service accounts and SSH keys.

And then there is the new line item: .claude/credentials.json, .cursor/credentials.json, .openai/auth.json, .anthropic/auth.json. Try this at home: open your company's secret rotation procedure and look for those four paths. They are not there. Those accounts tend to be created without going through any process, end up on somebody's team card, and rarely make it into the inventory. An AI provider API key is money directly — it gets consumed and billed — and as those tools connect to repositories and internal documentation, it is increasingly a door into company context too. It is the natural evolution of malware that already aims at developers and their agents rather than at the usual server.

The nastiest detail is the one StepSecurity notes about GitHub Actions runners: the payload reads process memory through /proc/<pid>/mem looking for the string "isSecret":true. Translated: the secrets the platform masks with asterisks in the log sit in cleartext in the memory of the process using them. Masking protects the log, not against someone already inside the machine. It is the same lesson we drew from the CI/CD server that holds the keys to production, now applied to the ephemeral runner most people treat as disposable precisely because it lasts five minutes.

The command centre was on Ethereum

The worm did not carry its server address inside. It looked it up: an eth_call to a contract on Ethereum mainnet, 0xE1f2395ee43e45A1556EC6438a88c31B83493103, fired at 75 public RPC endpoints until one answered. The observed exfiltration domain was npm-cache.com; Elastic also documents awqhnjewqjkl[.]icu. If the contract did not answer, the worm fell back to hunting for backup infrastructure in GitHub commits. And Microsoft describes a noisy plan C: creating a public repository with the description "Shai-Hulud: Here We Go Again", a nod to the worm family this one descends from.

Blocking npm-cache.com at the proxy is a good idea and takes a minute. But it is worth understanding what it buys: the domain is the disposable part. The destination address is read from a public blockchain nobody can take down, seize or deregister, and the attacker updates it whenever they like with a transaction. Domain blocklists still do what they do, which is plenty; they simply do not close this particular door.

The trap: revoking the token is what fires it

If you take one paragraph away from this article, make it this one. The payload leaves a watcher installed: ~/.local/bin/gh-token-monitor.sh, started by a systemd service on Linux or a LaunchAgent on macOS. It polls api.github.com/user every 60 seconds for 24 hours. When the stolen token stops working — that is, at the exact instant you revoke it — it runs an attacker payload. Microsoft describes it as a component that "maintains credential access and contains a destructive handler if the monitored token is revoked".

This inverts the correct reflex, which is what makes it dangerous. Anyone decently trained, on learning a token has been stolen, goes into GitHub and revokes it. It is the first thing we teach. Here, that click is the starting gun. The order StepSecurity recommends, and the only sensible one as far as we can see, swaps the two steps: first find and remove the watcher — the file, its user systemd unit, its LaunchAgent — and then rotate, from a clean machine. On runners too, even though intuition says you need not bother because they tear themselves down.

This is the detail that turns the episode from "supply chain scare" into "you need a written procedure". Improvising the response at eleven at night, with the development team watching and somebody asking every ten minutes whether it is done yet, is exactly the scenario in which the wrong button gets pressed.

There is nothing to patch here

A week ago we wrote about a library nobody chose that opened a hole in Rails applications. That case had a CVE, an official advisory, a range of affected versions and a list of fixed ones. You could cross one list against the other, count how many machines fell in range and schedule a window. It was unpleasant, but it was a problem with a familiar shape.

Not this one. There is no code flaw: there is a stolen account. There is no [email protected] fixing what 6.0.0 did, because 6.0.0 was carrying a passenger with a valid ticket. What there is is rolling back to an earlier version — or moving up to the clean one where the maintainer has republished, which has happened for a handful of the 444 — and assuming that any machine that ran the install has already handed over its secrets. The operational question is not "am I patched?"; it is "which machines ran npm install from 09:35 UTC on 4 August onwards, and what credentials were sitting on each of them?".

The order we would do it in

  • Find out whether it hit you, by looking at files rather than memory. In the lockfiles (package-lock.json, pnpm-lock.yaml, yarn.lock), which versions of the keyv, cacheable, flat-cache and file-entry-cache family came in from 4 August onwards. On disk, the files setup.mjs, Math_Symbol.js and math_init.js inside node_modules, and any package.json carrying "preinstall": "node setup.mjs".
  • Before touching a single token: remove the watcher. ~/.local/bin/gh-token-monitor.sh, its user systemd unit and, on macOS, its LaunchAgent. On laptops and on runners. This step goes first; it is not negotiable.
  • Roll back to safe versions with --ignore-scripts. Reinstalling without that flag on a machine whose lockfile is still poisoned runs the loader all over again.
  • Rotate from a clean machine, never from the infected one: npm tokens, GitHub PATs and SSH keys, cloud credentials, Kubernetes service accounts, Vault tokens. Plus the line missing from almost every list: the API keys for your AI tools.
  • Audit your GitHub organisation. Workflows nobody remembers creating — StepSecurity cites one named "Run Copilot" — commits attributed to [email protected], new public repositories, and changes to .claude/settings.json or .vscode/tasks.json, which is where the worm hooked itself to start again.
  • Cross-reference your cloud audit logs against those credentials, from 4 August to today, looking for use from addresses that are not yours. Rotating without checking this tells you the door is shut, not what happened while it was open.
  • And the permanent ones: --ignore-scripts by default with a short list of justified exceptions; a soak period before adopting a freshly published version, which in this case would have been enough; two-factor on npm; and ephemeral tokens in CI instead of long-lived PATs sitting on disk.

What this is not

This is not an argument against npm or against open source, and anyone using it as one this week is cheating. Your proprietary application drags in hundreds of dependencies too; the difference is that you cannot see them and that, when this happens to a closed vendor, you find out six months later in a letter. Here the whole chain is public: which is why five independent forensic analyses were published within days, and why you can go looking for the indicators yourself this afternoon.

And "audit every dependency" is not a plan either. It is what gets said in the meeting and what nobody does the following Tuesday: nobody reads the dependency tree of a mid-sized project, not this month and not next. What holds up over time is two thoroughly unglamorous routines: credentials that expire on their own and credentials that reach very little. An npm token that expires in an hour and can only publish one package is worth very little stolen. And if the laptop that builds can deploy to production with a key stored on disk, the blast radius of one preinstall line is your entire infrastructure — a sentence that is frightening precisely because it is literal.

But what we will take away from this episode is the thing at the start. On 4 August the control worked exactly as designed: the attestation was generated, it was verified, and it was correct. And the package was poisoned anyway. That does not get fixed by adding another box to the checklist. It gets fixed by knowing precisely which question each control you have in place answers — provenance answers "was this built by who it says?", and there its job ends — and which of your risks is left with nobody to answer it. Who wrote the commit remains a matter of accounts, passwords and second factors on somebody else's computer. That is the boring part, and it is the part that decided 4 August.

We do not sell supply chain security tooling and we have no product that solves this, and anyone telling you they do is selling you an alarm for a door that is not the one that opened. What we do is the work around it: knowing which applications a company has, who builds them, what credentials live in its pipeline, how long they last and how far they reach. That is what sits behind our data and applications projects and the consulting we do without selling anyone's licences. If you cannot answer today which machines in your company ran npm install last week, get in touch and we will look at it with you.

A note on sources. The timeline ([email protected] published at 09:35 UTC on 4 August 2026 via trusted publishing with OIDC and valid provenance; second wave from 09:38 to 13:20 UTC), the count of 444 packages and 2,212 versions, the sizes of setup.mjs and Math_Symbol.js (727,680 bytes), the Bun v1.3.13 download, the ~140 credential paths, the /proc/<pid>/mem read looking for "isSecret":true, the Ethereum contract 0xE1f2395ee43e45A1556EC6438a88c31B83493103 with 75 RPC endpoints, the gh-token-monitor.sh watcher polling every 60 seconds for 24 hours, and the remediation order, from StepSecurity's analysis. The description of the worm, the destructive handler on revocation of the monitored token, the AES-256-GCM encryption of exfiltrated data and the fallback public repository, from Microsoft Security's analysis of 4 August 2026. The compromise of the maintainer's GitHub account, the alternative count of 1,381 versions and the 2 billion-plus monthly installs, from Aikido. The line about provenance attesting build integrity rather than source integrity, from Socket. Additional indicators and the 300-plus credential patterns, from Elastic Security Labs. Where the figures disagree between sources we say so in the text rather than picking the highest. The reading of provenance as a seal rather than a seatbelt, the ordering of the tasks and the conclusion about credential lifetime and reach are our own. Cover image: "Eimskip container terminal at Port of Reykjavík" by Quintin Soloviev, via Wikimedia Commons, under a CC BY 4.0 licence (cropped).

Share:

Subscribe to our newsletter

To receive IT stories, everyWAN news and exclusive subscriber offers, sign up to our mailing list

Minorisa de Sistemas Informaticos y Gestión S.L. © 2026
everyWAN
everyWAN