Poisoned Pipeline / Incidents / AS338

AS338 · 2026-10

The earliest PolinRider sample yet found was a May 2025 npm account takeover

npmGit / GitHubWindows softwareLinux distro / packagesmacOS software

OpenSourceMalware published this on 30 October 2026. It started with a
package that had gone quiet: itsa-utils last published in August 2018.
Then, at 20:15 UTC on Sunday 4 May 2025, someone published version
1.4.2. Over the following 90 minutes, new versions of eight more npm
packages from the same maintainer appeared — nine in total, from an
account that holds 59 packages. Three went out in the first eleven
minutes, then a 56-minute pause, then six more in 23 minutes. The next
morning, between 10:07 and 11:18 UTC, the maintainer published seven
clean replacements, including itsa-utils 1.4.3. Those moved the
latest tag forward but left the infected versions installable, so
anyone pinning a version still pulled the malicious code.

The researchers recovered four of the nine tarballs — the three
versions still live and itsa-utils 1.4.2 from a mirror, matched against
the registry's original checksums — and the payload line is
byte-identical in all four. Five more were removed before they could
be read and are counted as infected on the strength of the burst.
Six of the nine have since disappeared from the registry: two in 2025
and four in October 2026, judging by last-modified times. Nobody has
said who removed them.

What they found inside is the reason this is a story rather than
another account takeover. The malicious code is obfuscated JavaScript
appended to the end of an existing file and pushed off screen by long
runs of whitespace — the pattern OpenSourceMalware has documented for
PolinRider, the DPRK-linked campaign with no known victims before
January 2026. Eight of the nine traits the researchers use to
fingerprint that campaign are present here: the marker, the XOR key,
the command server, the server paths, the blockchain delivery chain,
and the habit of appending to a config-like file. What is missing is
the behaviour that defines PolinRider in 2026 — force-pushing the
change into a maintainer's repositories as them. That makes this
sample a building block of the campaign rather than an early run of
it, the way OpenSourceMalware folded TasksJacker in: the shared
decryption key, command server and paths are the strongest links.

There are no install hooks. The trigger is the entry point. In
itsa-utils 1.4.2 the first eleven lines of index.js export the
package's utility functions and line 12 is the obfuscated program, so
the payload runs when an application imports the package — the rest of
the file is identical to the clean 1.4.3. The chain is EtherHiding,
executable content stored in the calldata of blockchain transactions
and read out through public explorer APIs, so the attacker never has to
host a stage. The loader asks Binplorer for the latest mint on the
attacker wallet, decodes a Base64 blob from the transaction input and
XORs it with the campaign key to recover JADESNOW, a second-stage
loader that moves to Ethereum and reads one transaction through
Ethplorer, BlockCypher and Blockchair for redundancy. The on-chain
record is dated 7 February 2025 — 86 days before the npm publish — and
GTIG has already documented the same BNB Smart Chain contract, owner
wallet and transaction lineage in UNC5342's use of JADESNOW to deliver
INVISIBLEFERRET.

The decoded bootstrapper sets up a command server, installs missing
Node dependencies, makes Python 3.12.7 available and fetches two more
payloads from Ethereum. One is a remote-access component: shells,
directory changes, single files and whole directories uploaded,
reconnecting every five seconds, with a Russia kill switch. The other
is a 65,500-byte Python credential stealer that runs on Windows, Linux
and macOS. It recovers browser encryption material through DPAPI, the
macOS Keychain, Linux Secret Service or KDE Wallet, then exports
passwords, cookies, cards, profiles and extension storage across nine
Chromium browsers and Firefox; it enumerates more than 80 wallet and
password-manager extensions including MetaMask, Phantom, Coinbase,
TronLink, Keplr, Rabby, Exodus, 1Password and Dashlane; it collects Git
credentials, GitHub CLI tokens, Solana key files, Electrum and Bitcoin
wallets, macOS login keychains, WinAuth and Proxifier profiles, and
looks for Dropbox, Google Drive, OneDrive, iCloud, Box, MEGA and pCloud
data. Archives go out as AES-encrypted ZIPs, with Telegram as the
fallback for anything under 50 MB. The stealer carries a Telegram bot
token and chat ID that were still valid when the researchers checked in
October 2026. Nothing creates a run key, scheduled task, launch agent
or service, so the implant does not survive a reboot — it lives in
detached Node processes, a portable Python install and silent error
handling instead.

The publishing environment is the one clue to who was at the keyboard.
The four burst versions with a registry record were published with npm
10.9.2 and Node 22.14.0; every other recorded publish from that account
between May 2025 and April 2026 used npm 5.5.1 and Node 8.9.1. npm
client versions are self-reported, so this is an indication rather than
proof, but it points at someone with the maintainer's publishing rights
working from a machine the maintainer does not normally use.

The same maintainer's GitHub repositories carry newer variants of the
payload — labelled B, B2 and C — in four windows: 22 January, 23 March,
29 May and 9 July 2026, across itsa-react-table, itsa-react-select,
itsa-react-docviewer and itsa-react-fileuploadarea. Only variant A, the
chain above, was decoded. Every variant is a long appended line after
padding, and the marker changed from one naming the maintainer's
package prefix to values that name nothing. The commits carry the
metadata tells: six 2026 commits have committer dates 31 to 66 days
after the author date, in +09:00 or -08:00 against the maintainer's
+01:00 or +02:00, with a shortened committer name, and four July
commits have committer dates set 34 to 152 days before the push. The
2026 payloads never reached npm, because PolinRider as it is operated
now does not publish for the maintainer — the maintainer has to ship a
version themselves for hidden code to ride along.

Mapped techniques

supply-chain A dormant maintainer account publishes nine packages in 90 minutes

T0113 · Initial Access · Compromised user account

Nine packages from one account with 59 of them, in a single
90-minute burst, after seven years without a publish from
itsa-utils. The npm and Node versions used for the burst do not
match the account's other publishes across the following year,
which points at an operator working with the maintainer's
credentials from a different machine.

T0109 · Resource Development · Publish malicious artifact

itsa-utils@1.4.2 plus eight more versions of the maintainer's
packages. Four tarballs were recovered and the payload line is
byte-identical in all four; seven clean replacements the next
morning left the infected versions installable by version pin.

post-supply-chain Importing the package runs a blockchain-delivered RAT and stealer

T0197 · Initial Access · Use code from untrusted source

Any application that depends on the package pulls the burst
versions by range or by pin; nothing about the version numbers
marks them as hostile.

T0138 · Persistence · Backdoor in code

Obfuscated JavaScript appended to index.js after long runs of
whitespace, reached at line 12 when the module is imported. The
file is otherwise identical to the clean 1.4.3, and there are no
install hooks to trip a script scanner.

T0159 · Execution · Malicious artifact execution

The import starts the chain. A loader reads an encrypted stage
from a BNB Smart Chain transaction, recovers JADESNOW, moves to
Ethereum, and installs a remote-access component and a Python
credential stealer.

T0184 · Credential Access · Dumping credentials from files

The stealer reads browser profile stores across nine Chromium
browsers and Firefox using DPAPI, Keychain, Secret Service or
KDE Wallet material, and also takes Git credentials, GitHub CLI
tokens, Solana key files, wallet files and password-manager data
before packing it into AES-encrypted ZIPs.

supply-chain The same maintainer's repositories carry undecoded variants in 2026

T0169 · Lateral Movement · Push implants across repositories

Variants B, B2 and C landed in four of the maintainer's React
repositories in four windows between January and July 2026. None
of them reached npm.

T0195 · Defense Evasion · Spoofed Commits

Committer dates sit 31 to 66 days after the author date with
+09:00 or -08:00 offsets against the maintainer's European
offset and a shortened committer name; four July commits carry
dates 34 to 152 days before the push. The researchers' rule is
to trust GitHub's push times over commit dates.

T0111 · Resource Development · Malicious code contribution to an open-source repository

Every infected 2026 commit changed only the appended payload
line, in repositories the maintainer already owned.

Framework gaps noted

Sources