IronWorm npm: when Trusted Publishing becomes the attack surface
JFrog disclosed IronWorm on 3 June: an npm worm with a Rust binary, eBPF rootkit, Tor C2, and self-propagation via npm Trusted Publishing OIDC tokens.
For two years the supply-chain answer has been: rotate your npm tokens, enable 2FA, and publish through GitHub Actions with npm Trusted Publishing, which exchanges an OIDC identity token from your CI run for a short-lived, package-scoped publish credential. No long-lived token on disk, no token in env vars, nothing for an attacker to steal.
On 3 June 2026 JFrog Security Research disclosed IronWorm, an npm supply-chain worm that uses Trusted Publishing's OIDC flow as its self-propagation mechanism. The token-rotation guidance still holds. It just no longer touches the path the attacker actually takes.
IronWorm by the numbers
- 36 trojanized npm packages across 9 GitHub organizations, including
ocrybit,asteroid-dao,alisista,warashibe,kakedashi-hacker,weavedb,ArweaveOasis,arthursimao, andmlebjerg(JFrog). - 57 back-dated malicious commits authored as the generic identity
claude— chosen, per JFrog, to blend in with dependabot, renovate, and github-actions commit noise. - ~32,177 monthly downloads / 148,724 lifetime downloads combined across the trojanized packages, per OX Security's tracker.
- The entry point was the
asteroiddaomaintainer account; the Web3/Arweave ecosystem (WeaveDB, AO, Arweave Oasis) was the visible target, with SlowMist issuing the Web3-side warning. - Within roughly 24 hours of JFrog's discovery the malicious versions were deprecated by the operator — silent removal, not a registry takedown.
The mainstream-press hit landed in BleepingComputer on 4 June. Concurrently, Dark Reading flagged that StepSecurity and Endor Labs were tracking an unrelated but parallel npm campaign (binding.gyp), which means two separate actors were running ecosystem-wide worms in the same week.
The host-side gap: a Rust ELF with an eBPF rootkit
Most JavaScript supply-chain payloads are JavaScript: an index.js post-install script, a tiny Node downloader, maybe a base64 blob. They are detectable by static analysis on the package tarball.
IronWorm is not that. The malicious package's preinstall hook drops and executes a 976 KB Rust ELF — Linux-only — wrapped in a custom UPX stub with the UPX magic overwritten to defeat signature-based unpackers. Once running, it loads an eBPF kernel module that hides its own process from ps / top, its files from ls, and its sockets from netstat by filtering netlink responses. Anti-debug is built in: any ptrace against the protected process is answered with SIGKILL. Every string in the binary is encrypted with per-call-site parameters, so a memory dump does not give you a plaintext IoC list.
Two design choices matter for defenders.
First: preinstall runs before npm resolves dependencies. There is no opportunity for a sandbox-on-install policy to inspect the package tree before code executes — the code executes as the first thing npm does after fetch.
Second: an eBPF kernel rootkit is invisible to the userland processes EDR agents are made of. The hook list the EDR enumerates is not the hook list the kernel is honouring. Standard endpoint visibility — process tree, file open events, socket creation — is exactly what the rootkit is engineered to hide.
This is not theoretical. Once the rootkit is loaded, the appliance running the EDR sees a clean host.
Tor C2 and the Trusted Publishing pivot
The implant beacons over Tor to an onion service /api/agent endpoint. The defender that looks at the wire sees: a host that historically only talks to registry.npmjs.org, github.com, and pypi.org is suddenly maintaining a long-lived encrypted session to a Tor entry guard. That is the signal.
But the more important pivot is how IronWorm propagates to the next victim. The implant scrapes the CI environment for an OIDC identity token and submits it to:
https://registry.npmjs.org/-/npm/v1/oidc/token/exchange/package/<pkg>
The token exchange returns a short-lived, package-scoped automation token. The malware then publishes the trojanized next version of any package the compromised CI runner has rights to publish. From npm's point of view, the publish is fully authentic: it came from the configured Trusted Publisher, signed with a token npm itself just minted.
"We rotated all our npm tokens last quarter." — fine. The IronWorm publish event did not use a stored token. It used a short-lived OIDC exchange that ran for ~30 seconds inside your own GitHub Actions runner, against the endpoint npm tells you to use, with an identity npm trusts because your repo configured it as a trusted publisher.
This is the structural shift Microsoft's Red Hat Miasma writeup on 2 June flagged in parallel: the OIDC-exchange path is now an active propagation surface in the npm ecosystem, not a hypothetical risk model.
What it steals: AI keys and coding-agent configs
IronWorm exfiltrates 86 environment variables and 20 credential file paths. The env-var list is the interesting part — it includes 14 LLM provider keys: Anthropic, OpenAI, Gemini, Cohere, Mistral, Groq, Perplexity, xAI, alongside the usual AWS / GCP / Azure / cloud-storage / Vault / Kubernetes / npm / SCM / messaging blocks.
The file paths include the standard ones (~/.aws/credentials, ~/.ssh/id_*, ~/.docker/config.json, ~/.kube/config, ~/.vault/, Exodus wallet seed locations) and a less-standard set: the config directories of Claude, Codex, Cursor, and Gemini. AI-assisted coding tools store API keys, MCP server configurations, and in some cases project context locally. IronWorm is the first widely-tracked npm worm to put those files in its target list — and it is unlikely to be the last.
For an engineering organisation that pushed AI coding tools into developer workstations in 2025, the IronWorm exfil set is also the realistic post-breach blast radius: every LLM provider you use, every CI token, every cloud credential.
Why host-only defence cannot catch this
Pull the threads together:
| Defensive layer | What it sees during an IronWorm infection |
|---|---|
| Static package analysis on install | A preinstall that runs a Linux ELF. Suspicious, but the ELF body is UPX-mangled and string-encrypted; signature/yara is weak. |
| Endpoint EDR | A clean host. The eBPF rootkit hides processes, files, sockets. The agent sees what the rootkit lets it see. |
| Vulnerability scanner | Nothing — the affected packages are not CVE-tracked vulnerabilities, they are published, trusted, signed legitimate versions of packages you depend on. |
| Registry signature verification | Pass. The publish came through Trusted Publishing OIDC. The signature is valid. |
| Network egress monitoring | A new long-lived Tor session from a host that historically only talked to package registries and SCM. |
| DNS lineage | The preinstall script triggered the first-ever DNS lookups to Tor entry nodes from this build node. |
The only row that detects an active IronWorm infection in flight is the wire. Static analysis catches the package if you got lucky on the unpacker. Trust-chain verification is structurally defeated. EDR sees nothing. The exfil happens through an encrypted tunnel from a build runner to an onion service.
This is the operational case for treating network-egress behaviour as a first-class detection surface — not a SIEM enrichment field, not a log line that gets reviewed next morning. The signal exists at wire speed; the question is whether something is looking at wire speed.
What Zero Hunt does about this
Zero Hunt's AI Traffic Analysis pillar is a proprietary deep-learning model trained on billions of PCAP sequences, with four parallel inference heads — suspicious-traffic classification, malware classification, attack-type identification, and application fingerprinting — running locally on the appliance GPU at 2.7+ Gbit/s baseline throughput. For the IronWorm class of attack the relevant outputs are the first and the fourth: identifying that a build host is now sustaining an outbound encrypted session whose timing and packet-size profile match Tor, and recognising that the application initiating the session is not one of the categories that host has ever spoken before.
That detection happens while the exfil is in flight, not after a SIEM digest. It works whether or not the host EDR can see the process — and in the IronWorm case the EDR cannot see the process at all.
The complementary work happens on the Pillar 1 side. The AI Gym corpus that backstops Zero Hunt's generative pentest swarm includes self-evolving offensive skills for supply-chain compromise paths — the 10-agent engine can compose a pentest campaign that exercises a customer's CI pipeline against preinstall hook abuse, Trusted Publishing OIDC misuse, and developer-workstation credential paths, validated against the AI Gym backtest corpus (142+ skills, Vulhub-Bench's 314 CVE-based black-box tasks, NYU CTF Bench's 200 CSAW tasks) before any new skill ever touches a production environment. Every finding is ECDSA-signed at write-time and mapped against the 32 compliance frameworks Zero Hunt tracks, so the audit trail of "what did we test against IronWorm-class threats this quarter, and what did we find" exists by construction.
And one footnote from the IronWorm operator, because the human ending is too good to leave out: per JFrog, the implant's exfil routine carried a hardcoded BIP-39 recovery phrase in its skip-list — "bench crane defense corn wheel trial…" — so that during testing the malware would not steal the operator's own crypto wallet. It is, of course, the only place in the binary where a BIP-39 phrase appears in plaintext. JFrog's dry summary: "the operator shipped his recovery phrase to everyone."
The lesson for defenders is more sober. The supply chain answer — Trusted Publishing — works as designed. It is the design's blast radius that just got rewritten.