Dormant npm Packages Poisoned With a Loader That Hides Its Server on Ethereum
On the evening of November 3, 2026, someone published six new versions of four npm packages whose last regular releases date from 2018 to 2021. Each release carries a hidden loader that looks up its server’s address in Ethereum transactions, then downloads more code and runs it in memory.
The releases were caught by Greenflagged, a private npm registry that only serves packages that pass its review. Its write-up by Sean Madden is, at the time of writing, the only detailed analysis.
Affected Packages and Versions
All six versions went up between 22:00 and 23:21 UTC. Because the “latest” tag of each package points to a malicious version, a plain npm install picks it up.
These are small packages. Together they had fewer than 100 downloads in the week before the attack, so few projects are likely to be affected. When we checked npm’s registry again at 00:05 UTC on November 4, all six versions were still available.
How the Loader Works
The first variant sits in one line added to the end of the package’s main file: a run of more than 100 tab characters, then eval(atob("…")). In a normal code view, the tabs push it far out of sight to the right.
It runs as soon as a project loads the package with require. There is no install script, so turning install scripts off (--ignore-scripts), which blocks install-hook malware like August’s npm worm, doesn’t help here.
According to Greenflagged, which decoded the loader without running it, it:
- asks public Ethereum endpoints for the latest transaction sent by the wallet
0x1eef0849eda15d87f64e51c73c8442cf3c8bcef3; - reads two IPv4 addresses, a main server and a backup, out of that transaction’s recipient address;
- requests the next stage over plain HTTP on port 443, takes it from a response header, decodes it and runs it inside the host process;
- fetches a second stage and runs it in a separate
node -iprocess fed through standard input, so it never touches the disk; - swallows every error, so the package appears to load normally.
The wallet has sent 226 transactions, all of zero value, and Greenflagged says the server address is re-posted roughly every 3 hours 20 minutes. Four server IPs have been used so far. A transaction can’t be deleted, so there is no domain to seize and no hosting provider to call. A ledger built to be permanent is, it turns out, permanent for everybody.
The second variant, first seen in utils 0.0.16-130, does the same lookups with different obfuscation: no eval or atob, but 60 added lines of character swapping and the Function constructor. The file grew from 4,930 to 21,215 bytes in a release that changed nothing else except the version number.
Greenflagged didn’t analyze the second stage or contact the servers, so what the attackers do once the loader runs is not known.
The Same Packages Were Hit in August
This is the second time in three months. In the August 2026 npm worm that spread from compromised keyv and cacheable maintainer accounts, packages in the @adminide-stack namespace were among the hundreds poisoned (see OSV MAL-2026-11551 for @adminide-stack/clock-tik-browser). SafeDep’s list also includes server-hemera-mongo 0.0.12, published on August 4. That version is no longer in npm’s version list.
The new releases came from the npm account clockbook-app, one of 18 maintainers on these packages, most of them with cdmbase.com email addresses. The earlier, clean releases came from the adminide-stack account. Whether clockbook-app was hijacked, and whether August and November are linked, isn’t known. Both hide their servers on Ethereum, but in different ways: August’s worm read a list of server domains from a smart contract, StepSecurity found, while this loader decodes IP addresses from a wallet’s latest transaction. August’s worm also needed an install hook and stole credentials. This loader runs without one, and what it does next is unknown.
What the two incidents show together is simpler. August’s worm stole npm publishing tokens, and these packages have 18 accounts that can publish them. After a break-in, every one of those has to be locked again.
What to Do
- Search your lockfiles (package-lock.json, yarn.lock, pnpm-lock.yaml) for the six versions above, including as dependencies of other packages.
- If one was installed and loaded, treat that machine or CI runner as compromised. Nobody knows yet what the second stage does, so rotate the credentials that environment could reach: npm and GitHub tokens, cloud keys and CI secrets.
- Pin or drop. The last releases before November 3 are utils 0.0.16-128, client-redux 0.0.16-17, server-hemera-mongo 0.0.11 and health-api-server 0.0.18-alpha.464. For packages untouched since 2018, it is a good moment to ask whether you still need them.
- Look for the indicators Greenflagged lists: Ethereum JSON-RPC calls from server processes, plain HTTP on port 443, the request header
Sec-V, the response headerx-payload-b64, the paths/0x/clsand/0x/ls, the IPs 166.88.134.75, 103.179.143.219, 23.27.13.72 and 95.135.181.151, andnode -iprocesses started by another Node process. - Install from a committed lockfile with
npm ci, so a new “latest” can’t slip into a build on its own.
Our Mission
We believe security online security matters and its our mission to make it a safer place.