Yaamlabs
Threat intel

PolinRider npm loader reads Ethereum to find its C2

A fresh PolinRider campaign hijacked 10 npm packages. The loader reads an Ethereum transaction to locate its server, and it has no install hooks to disable.

By Yaali. November 11, 2026, 6 min read, Threat intel, Vulnerabilities.

Cover illustration on a plain navy gradient, with the Yaamlabs logo and the text: PolinRider npm loader reads Ethereum to find its C2, 10 packages, 12 malicious releases, no install hooks

On November 4, OX Security reported the return of PolinRider, a malware family now hiding concealed JavaScript loaders in 10 hijacked npm packages across 12 malicious releases. npm is the Node.js package registry, the default source of third-party code for JavaScript projects. Sonatype published the same package set the same week, and GBHackers covered the technique that makes this wave worth your attention: instead of carrying a fixed server address, the loader reads a transaction on the Ethereum blockchain to discover where its command and control (C2) server lives.

You are affected if any project installed one of the named versions below, whether you listed the package directly or pulled it in as a dependency of a dependency. The part that breaks a common habit: unlike most npm attacks, these releases contain no install or postinstall script. The usual advice to run npm install --ignore-scripts does nothing here, because the malicious code runs when your application imports and uses the package, not when you install it.

Attack flow showing a hijacked npm package loaded at runtime, the loader reading the attacker wallet's latest Ethereum transaction over a public RPC endpoint, the recipient address decoded into a payload server IP address, and XOR-encoded JavaScript fetched and run in the Node process

How a blockchain dead-drop resolver works

A dead drop is a hiding place where one party leaves a message for another without the two ever meeting. In this campaign the hiding place is the Ethereum blockchain, which anyone can read but no one can quietly take down. The server address never appears in the package source, so grepping the code for a domain or an IP finds nothing.

The loader itself is obfuscated. Six of the releases wrap it in Base64 and run it with eval(atob(...)); the other six rebuild it from a character-permutation bootstrap and execute it through the JavaScript Function constructor. Once it runs, it queries a public Ethereum RPC endpoint, with a Blockscout explorer API as a fallback. RPC, remote procedure call, is the standard request a client sends to a blockchain node to read chain data. The loader asks for the latest transaction sent from one hard-coded wallet, 0x1eef0849eda15d87f64e51c73c8442cf3c8bcef3.

The trick is in how it reads that transaction. It takes the 20-byte recipient address and treats the first four bytes as the four numbers of an IPv4 address, the primary payload server, and the next four bytes as a backup. It then fetches XOR-encoded JavaScript over plain HTTP on TCP port 443 from the paths /0x/cls and /0x/ls on that server. The /0x/cls stage runs through eval inside your application's own process and is also piped to a separate, detached node -i process; /0x/ls goes only to the detached process. Those child processes are unreferenced, so they keep running on their own after the parent moves on.

This is why domain and IP blocking does not contain it. Nothing in the package points at a server, so there is no address to add to a blocklist up front. To move infrastructure, the operator sends one new transaction from the wallet, and every running loader picks up the new address the next time it checks. Greenflagged observed the advertised server at 95.135.181.151 on November 3, but blocking that one address does not stop the loader from reading the next. The parts worth watching are the resolver wallet and the blockchain lookups themselves, not any single server.

Why "no install hooks" breaks a common defence

Most npm supply-chain attacks fire from a postinstall script, so the standard defence has become installing with scripts turned off. Sonatype confirmed that the manifests for these releases declare no consumer install or postinstall hooks at all. The malicious code lives inside an ordinary JavaScript file the package loads during normal use. Requiring cdeops/client runs it. For server-hemera-mongo, it sits in dist/main.js, which runs when the service starts.

Two things follow. First, npm install --ignore-scripts changes nothing, because there is no install script to skip. Second, a clean install log proves nothing about whether the code ran. A lockfile entry alone does not confirm execution, and an import that threw an error does not confirm the code never reached its network calls.

The hijacked packages

Sonatype listed the confirmed packages and malicious versions. Most sit in the @adminide-stack scope, which is how a single compromised publisher account can taint a cluster of related packages at once.

PackageMalicious versions
@adminide-stack/client-redux0.0.16-18
@adminide-stack/health-api-server0.0.18-alpha.465
@adminide-stack/user-activity-browser1.1.3-alpha.114
@adminide-stack/utils0.0.16-129, 0.0.16-130
@adminide-stack/forms-core5.1.9-alpha.3
@adminide-stack/yantra-help-browser12.0.194
@cdmbase/wiki-browser12.0.18-alpha.53
@pubngo-stack/help-browser12.0.23-alpha.3
cdeops13.5.31
server-hemera-mongo0.0.15, 0.0.16

Useful indicators beyond the versions: the resolver wallet 0x1eef0849eda15d87f64e51c73c8442cf3c8bcef3; requests to /0x/cls and /0x/ls; a request header named Sec-V carrying A10-npm_inject, A10-II or A10-E; and detached node -i processes started by another Node process with input piped in. Socket's earlier reporting ties the wider PolinRider activity to a North Korea linked cluster, but that attribution is not needed to act on this wave.

What to do

Defence checklist in four steps: inventory dependencies and lockfiles for the named versions, block and remove them, watch runtime egress for blockchain RPC and payload paths, and rotate secrets if the code ran

  1. Inventory your dependencies. Run npm ls <package> --all (or pnpm why <package>) per project to find every resolved copy, including ones pulled in through other packages. Grep your lockfiles for the exact versions: grep -rn '0.0.16-130\|13.5.31\|12.0.194' package-lock.json pnpm-lock.yaml yarn.lock. Check internal mirrors, package caches, CI build records and container images built in the last week, not just the repositories you install from.
  2. Block and remove. Block the affected versions in your repository manager or dependency firewall, and review download logs to see whether any were already delivered. Remove them and pin to a version you have independently verified as clean. Keep a copy of the bad release for investigation before you delete it.
  3. Do not treat --ignore-scripts as cover. It blocks postinstall hooks, which these packages do not use. It is worth keeping on, but it is not the control that stops this.
  4. Watch runtime egress. Alert on outbound connections to public Ethereum RPC endpoints or Blockscout from build servers and application hosts that have no business talking to a blockchain. Alert on connections to addresses that were resolved at runtime rather than configured, on requests to /0x/cls or /0x/ls, on the Sec-V header values above, and on detached node -i processes spawned by another Node process.
  5. If the code could have run, treat the host as compromised. Correlate when the entrypoint loaded with process and network telemetry to decide. If it ran, rotate every secret those processes could reach: npm publishing tokens, source repository access, cloud keys, CI secrets, and any wallet keys reachable from the build or application host.

The deeper fix is where your code comes from. A vetted or mirrored registry with integrity pinning, so that only reviewed versions can resolve, removes the window in which a hijacked release reaches your builds in the first place. Pair that with egress rules that treat an unexpected blockchain lookup from a build agent as the alarm it is.

We review dependency and build pipelines for exactly this kind of exposure during red team code review, and platform and cloud engineering covers locking down registries, CI egress and runtime monitoring. If you are not sure whether a build of yours resolved one of these versions, open the chat and Yaali, our AI agent, will pass the question to the engineer who would do the work.


Sources: OX Security: PolinRider returns, Sonatype: PolinRider loader reappears in hijacked npm packages, GBHackers: ChainDrop and PolinRider use blockchain C2, Infosecurity Magazine: npm packages read C2 addresses from Ethereum wallet.

Read next

Back to the blog, or tell us about your system in the chat. Yaali, our AI agent, answers first and brings in an engineer.