IronWorm: The Rust eBPF Rootkit Worm That Rewrote npm Supply Chain Attacks

IronWorm is not an incremental improvement on the Shai-Hulud family of npm worms. It is a deliberate engineering escalation that makes several defenses: token rotation, 2FA-enforced publish workflows, provenance signatures, and even kernel-level attestation.

Share

Author: Glenn Fryklund / Ollama Homelab
Date: July 2026
Target Audience: DevSecOps engineers, self-hosted CI/CD operators, package maintainers, security researchers
Word count: ~3,700 words


Executive Summary: A Qualitative Leap in Supply Chain Malware

IronWorm is not an incremental improvement on the Shai-Hulud family of npm worms. It is a deliberate engineering escalation that makes several defenses: token rotation, 2FA-enforced publish workflows, provenance signatures, and even kernel-level attestation. All of them are structurally irrelevant to the threat model it exploits.

Discovered by JFrog Security Research on June 3, 2026, and confirmed across The Hacker News, Phoenix Security, SafeDep (43 packages catalogued), CSA Research Note, Dark Reading, OX Security, and Zero Hunt analyses, IronWorm is a ~976 KB Rust-built Linux ELF binary distributed via 37 npm packages republished from the compromised asteroiddao account. It combines three capabilities that had never before appeared in the same supply chain implant:

  1. A kernel-level eBPF rootkit for hiding processes, sockets, and anti-debugging trips, compiled with debug metadata left in place by clang 22.1.5.
  2. OIDC-based self-propagation that mints fresh publish tokens from CI runner identity, bypassing every stored credential in scope.
  3. Tor-based C2 with a custom torrc and /api/agent endpoint for operator command delivery.

No CVE has been assigned. No vulnerability scanner at the point of compromise had a signal to match against. IronWorm is not a vulnerable package; it is malicious code shipped intentionally inside what otherwise appears as legitimate SDK releases. The qualitative leap is this: supply chain malware just jumped from "tamper with source" to "live in your kernel, own your CI pipeline, and publish itself before you notice."


The Technical Breakdown: eBPF Kernel Rootkit Mechanisms

The implant ships an embedded ELF object: a BPF program compiled with clang 22.1.5 that lives alongside the Rust userland code. Three hiding mechanisms make up the rootkit layer:

Process hiding. Rather than using LD_PRELOAD or userspace signal injection, IronWorm rewrites /proc directory entries in place. When a process ID is added to a hidden-set watchlist via an execve hook that matches against its name, subsequent reads of /proc/*/fd, ps, top, or ls /proc see nothing. New processes matching the watchlist are added automatically; no manual list management is required for defenders to enumerate via standard tooling.

TCP connection hiding. The same pattern extends to network visibility. The rootkit intercepts reads of /proc/net/tcp and strips rows belonging to hidden sockets before returning control to userland tools like ss, netstat, or any forensic audit looking at socket tables. Netlink filtering applies to the interface used by those same tools, so Tor connections become invisible to standard defense tooling on a running host.

Anti-debugging via ptrace interception. Any attempt to trace an IronWorm process using ptrace (the foundation of strace, debuggers, and many sandbox/analysis frameworks) receives not an error but SIGKILL. In practice, running strace -p <ironworm_pid> against the implant kills the shell that issued the command.

The vulnerability: kernel lockdown. Phoenix Security's analysis, building on JFrog Security Research's source reconstruction from BTF debug metadata, identified that two of the three strongest tricks (process hiding and TCP connection hiding) depend on a BPF helper that modifies caller-process memory. That helper is blocked under kernel lockdown mode. On systems with lockdown enabled, hidden processes and sockets become visible to standard tooling. Anti-debugging still works; netlink filtering survives. On stock Ubuntu or Debian servers running as root without lockdown, the operating assumption for most developer workstations, all three mechanisms operate as designed.

The compiler left .BTF.ext debug metadata intact with 214 verbatim source lines mapped to original filenames and line numbers, full type information for every BPF map, and every struct field preserved. JFrog reconstructed q2.bpf.c closely enough that the rootkit's exact logic is fully understood by security researchers. Something the operator clearly did not anticipate.


OIDC-Based Self-Propagation: The Worm Without a Token

The most significant operational innovation IronWorm introduces over Shai-Hulud and Mini Shai-Hulud (the TanStack campaign of May 2026) is its credential-free self-propagation via npm's Trusted Publishing OIDC flow. Here is the full mechanism:

  1. OIDC identity token request. The implant running on a CI runner with active npm OIDC trust federation requests an identity token from the CI environment, using the audience parameter registered for npm Trusted Publishing. No stored npm token ever appears in memory; only the runner's native OIDC bearer exists.
  2. Token exchange. The identity token is submitted to registry.npmjs.org/-/npm/v1/oidc/token/exchange/package/, a documented API endpoint that returns a short-lived, package-scoped automation publish token tied directly to the CI runner's provenance.
  3. Self-publishing. Using that freshly minted token, the implant publishes trojanized versions of any accessible packages under whichever maintainer namespace the runner can reach. No stored npm credential is required at any point.
  4. GitHub repository poisoning. Simultaneously, for GitHub repositories the stolen credentials have write access to (9 organizations confirmed: ocrybit, asteroid-dao, alisista, warashibe, kakedashi-hacker, weavedb, ArweaveOasis, arthursimao, mlebjerg), the implant commits either a payload dropper, under the spoofed author [email protected], or replaces an existing GitHub Actions workflow with a secret-exfiltration job under rotating bot identities (dependabot[bot], renovate[bot], github-actions[bot]).

This design is devastating for defenders because it makes every traditional credential hygiene measure irrelevant. Rotating npm tokens does nothing, because no token exists in the environment at the time of propagation. 2FA enforcement on publish workflows cannot block an OIDC-minted automation token; that path bypasses the UI entirely. Provenance signatures verify authenticity but not intent; a package published with valid signed provenance from a trusted CI runner is still malicious if the runner was infected upstream.

The cleanup completed within ~24 hours of JFrog's discovery. Most malicious npm versions were marked deprecated and most malicious GitHub commits silently removed, though some remained visible. The compromised account had approximately 4,500 contributions to private projects in the same month, meaning the public footprint is a lower bound on real impact.


Miasma Lineage: IronWorm's Position in the Evolutionary Tree

The Shai-Hulud family, spanning TeamPCP supply chain operations from late 2025 through mid-2026, has a documented genealogy that places IronWorm as a deliberate next-generation variant. Here is where it sits operationally:

Wave Date Operator / Variant Language Key Innovation
Shai-Hulud origin Sept 2025 Unknown (TeamPCP lineage) Python/JS First known self-propagating npm worm
Mini Shai-Hulud open-source release May 2026 TeamPCP-affiliated, leaked source JavaScript TanStack attack; demonstrated OIDC-based publish via CI runner identity without stored tokens
Miasma Wave 1 "Red Hat" June 1, 2026 Miasma operator JavaScript/Node.js Credential-stealing RAT targeting AI coding tool configs on npm
Miasma Wave 2 "Phantom Gyp" June 3, 2026 Same Miasma operator JavaScript with binding.gyp Bun runtime evasion for credential theft; LeoPlatform 23-package compromise
IronWorm June 4, 2026 Unknown (operational adjacency to TeamPCP) Rust compiled ELF eBPF kernel rootkit; Tor C2; OIDC self-propagation from CI runner without any stored credential
Miasma Wave 3 "AsyncAPI" July 14, 2026 Miasma operator (confirmed) JavaScript + Pwn Request 37 decoy PRs to steal PAT; AsyncAPI release-branch push delivering RAT to 3M weekly downloads under valid SLSA provenance

IronWorm is operationally adjacent but technically distinct from the Miasma family. Both share:

  • The same propagation model (compromise maintainer → self-replicate via trusted CI workflows)
  • The same target profile (developers with privileged access to registries, clouds, and signing keys)
  • Use of spoofed automation identities for commit forgery ([email protected])
  • Attribution leads pointing toward TeamPCP-affiliated operational playbook

But IronWorm raises the engineering bar on every axis: Rust compiled binary vs. JavaScript; eBPF kernel rootkit vs. userspace obfuscation; Tor C2 with custom torrc vs. centralized HTTP callbacks; credential-free OIDC self-propagation vs. stolen npm tokens used directly.

Phoenix Security's documentation places Mini Shai-Hulud (the May 2026 TanStack attack) as the direct technical predecessor to IronWorm's OIDC propagation mechanism. Both use the same endpoint and defense recommendation (disable OIDC trust federation on affected namespaces until rotation is verified). The four evolutionary leaps from that predecessor confirm this was a deliberate upgrade, not a parallel experiment.

SafeDep catalogued 43 packages across the Miasma + IronWorm combined footprint as of mid-July 2026. CSA Research Note #202 reports confirmed that JFrog's analysis matched none of these against known infostealers or eBPF rootkits. Each represented a new custom build for this campaign.


The Credential Sweep: Why Every CI Runner Is in Scope

The implant's environmental variable sweep covers 86 named variables across five categories that directly affect self-hosted DevSecOps operators:

Cloud providers. AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN, GOOGLE_APPLICATION_CREDENTIALS, GCP_SERVICE_ACCOUNT_KEY, AZURE_CLIENT_SECRET, AZURE_TENANT_ID. Any CI runner with cloud credentials configured is a potential leak source.

Source control and registries. GITHUB_TOKEN, GH_TOKEN, GITLAB_TOKEN, NPM_TOKEN, NPM_AUTH_TOKEN, PYPI_TOKEN, CARGO_REGISTRY_TOKEN. The sweep captures both traditional long-lived tokens (directly stolen) and OIDC-refreshable automation state.

CI/CD platform state. JENKINS_TOKEN, CIRCLECI_TOKEN, GitHub Actions runner environment variables including pre-existing OIDC identity tokens, which are the exact values IronWorm needs to self-propagate further, not merely steal.

Secrets management infrastructure. VAULT_TOKEN, VAULT_ADDR, Kubernetes service account tokens at /var/run/secrets/kubernetes.io/serviceaccount/token. The implant includes a dedicated K8s module that walks every accessible namespace and enumerates Secrets. Any team running builds from pods with cluster access is in scope.

AI/ML API providers (the 2026-specific expansion). ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, COHERE_API_KEY, MISTRAL_API_KEY, GROQ_API_KEY, PERPLEXITY_API_KEY, XAI_API_KEY. Plus companion credential files the sweep targets that did not exist a year ago: ~/.claude/.credentials.json, ~/.codex/auth.json, ~/Cursor/auth.json, ~/.gemini/settings.json.

The file-path sweep extends beyond environment variables. The implant reads over 20 credential paths including ~/.aws/credentials, ~/.kube/config, ~/.docker/config.json, ~/.npmrc, ~/.gitconfig, ~/.ssh/, browser keystores, and the new-generation AI tool configs listed above.


The Operator's Operational Security Failure: Hardcoded Recovery Phrase

A deliberately-placed error in the implant, the kind that tells you an attacker is testing in production without realizing it, exposes their own infrastructure. The credential file scanner includes a skip list of 74 bytes containing a complete 12-word BIP-39 recovery phrase, hardcoded in plaintext inside every copy of the implant so the wallet stealer would not rob them during testing.

Recovering the Ethereum address from this phrase takes three lines of Python with bip_utils. The resulting address is 0x7e28D9889f414B06c19a22A9Bd316f0AC279a4d6, a near-empty test wallet holding dust. This is not a treasure to seize; it is an attribution lead and evidence of the operator's operational methodology:

  • The phrase was written into every implant binary because testing against their own production wallet would have triggered alerts before analysis
  • Wallet detection happened during development, suggesting this variant may not be the final form. The full production release likely includes stronger anti-analysis measures for exactly this failure mode
  • The Exodus desktop wallet hook ( Electron sandbox relaxation to capture seed mnemonics at unlock time ) is designed to harvest seeds from any developer who unlocks their wallet on a compromised machine

Glenn's Angle: Detection and Prevention for Self-Hosted CI/CD, npm Mirrors, and Homelab DevSecOps

For operators running self-hosted CI runners (GitHub Actions Runners, Gitea/Forgejo-runners, Drone, or equivalent), the IronWorm threat model has direct operational implications. You do not need to be a corporate enterprise to be in scope; any Linux-based developer machine or runner with npm access is vulnerable during the attack window.

1. Treat every npm install on a CI runner as a privileged action. The preinstall hook fires before dependency resolution begins. Even npm ci (which skips lifecycle scripts by default) should be reviewed. Confirm that your runtime actually does skip them. JFrog notes that in certain configurations, lifecycle execution can be triggered regardless of lockfile-based installs.

2. Audit OIDC trust federation across all namespaces you control. If any CI runner with npm access has active Trusted Publishing federation configured, the worm's propagation path is available. Disable federation on any namespace touched by an affected version until rotation is complete and provenance verified. Re-enable only after confirming no persistence remains.

3. Enable kernel lockdown where possible. IronWorm's eBPF rootkit process hiding and TCP connection hiding both depend on a BPF helper blocked under lockdown mode. On production Linux systems, especially those running as root or in CI runners, enabling lockdown makes the implant detectable by standard tooling (ps, ss, netstat). This does not fix a compromised host, but it restores defender visibility.

4. Block egress to Tor entry guards from developer and CI subnets. The implant downloads its own Tor expert bundle and writes its own torrc configuration, meaning any outbound connection to known Tor exit/entry guard addresses is suspicious on any system that does not normally use the Tor browser or Tor-based anonymization as part of expected tooling. Firewall egress at this layer disables IronWorm's C2 without addressing prior credential theft already completed.

5. Audit every git repository you have write access to for backdated commits authored under spoofed automation identities. Search GitHub Actions activity logs, not the commit timestamps, which are easily forged, for pushes that appeared in the last few days but are attributed as occurring years earlier under author [email protected], dependabot[bot], renovate[bot], or github-actions[bot] when those identities did not previously make changes of this type.

6. Pin all GitHub Actions to specific SHAs, not tags. Payload B's workflow replacement replaces real workflows with one that uses legitimate pinned action SHAs. This makes the diff look security-conscious rather than suspicious. Without SHA pinning you cannot distinguish legitimate updates from malicious replacements by inspection alone.

7. Treat every credential reachable from any host that ran npm install of an affected version as compromised. The full 86-variable sweep means AWS, GCP, Azure, Vault, Kubernetes service account tokens, npm/GitHub tokens, and every AI provider API key configured on those hosts are in scope. Rotation must include long-lived API keys, signing keys stored in environment variables, and OIDC trust relationships where the attacker could have re-federated.

8. Reimage compromised developer machines. The eBPF rootkit's kernel-level mechanism makes point-fix removal unsafe. Even with lockdown enabled to restore visibility, a system that had full-features-rootkit running as root should be considered irrecoverable by tooling alone and reimaged from known-clean media.


Key Takeaways for Package Maintainers and DevSecOps Engineers

For package maintainers:

  • Enable npm's official Signed Commits on every repository you own, including forks. This does not prevent IronWorm (it uses valid credentials), but it raises the bar for any variant that tries to impersonate your commit identity.
  • Rotate maintainer credentials immediately if any affected version was installed in your environment during the attack window. The compromised account had ~4,500 private contributions in one month. Scope is a lower bound.
  • Audit npm Trusted Publishing federation on every namespace you maintain. Disable until rotation is verified; this is the same defense that stopped Mini Shai-Hulud from hitting TanStack the previous month.

For DevSecOps engineers running CI runners:

  • Treat preinstall scripts as untrusted code executed by an attacker-controlled process. Any binary at tools/setup or similar innocuous paths inside a package should be flagged and blocked immediately, especially when it is Rust-compiled, UPX-packed, and ships with embedded BPF objects.
  • Deploy network-level egress controls for CI runners: block Tor entry guards, block connections to temp.sh, block outbound HTTPS to npm's OIDC token exchange endpoints from runners that should not be publishing packages.
  • Use lockfile-only installs (npm ci --ignore-scripts) as a temporary mitigation. This prevents new infections but does not clean existing ones. Reimage affected machines regardless of script-blocking status, because the eBPF rootkit may already have executed during an earlier install session without --ignore-scripts.

For security tooling operators:

  • CVE-based scanning is structurally incapable of detecting IronWorm-class threats. There is no CVE here. No NVD entry. No OSV record at time of compromise. Detection signals live in package behavior: preinstall lifecycle scripts that execute unfamiliar binaries, per-call-site string encryption signatures, modified UPX stubs, embedded BPF objects, hardcoded BIP-39 phrases, not in vulnerability advisories.
  • Deploy behavioral heuristics: flag any npm package that ships a binary with no source-tree justification (PHX-Neural uses 77 signals for this purpose).

For homelab operators running self-hosted DevSecOps toolchains:

  • Ollama on your homelab doesn't protect you from supply chain attacks in the CI runner tier, but it does mean every machine that runs your build pipeline is a potential compromise surface.
  • The same homelab hardening principles that apply to Docker Compose CIS benchmarks and nftables rules should be applied to CI runners: isolated networks, mandatory egress filtering, no root-by-default execution, kernel lockdown where the distribution supports it.
  • If you mirror npm packages locally (Verdaccio, Artifactory OSS, or equivalent), audit installed versions against the affected list regardless of whether they were pulled from the public registry. Local mirrors do not protect against malicious content in published packages.

Conclusion: What This Means for Supply Chain Security Posture

IronWorm represents a phase change in supply chain attack methodology. It demonstrated that a single compromised maintainer account, when paired with OIDC trust federation and CI runner identity, can be converted into an unbounded publishing engine, one that requires no stored credential at any point in the propagation loop. The combination of Rust compilation (resistant to dynamic analysis), eBPF kernel rootkit (invisible to standard detection tooling on unprotected hosts), and Tor-based C2 (anonymous, decentralized command delivery) means this implant was built for persistent, undetected operation over weeks or months. Not a single-shot credential grab.

The operator's mistake, hardcoding their own wallet recovery phrase in the skip list so testing wouldn't rob them, is a human-level operational failure that exposed an otherwise sophisticated toolchain to reverse engineering. It also signals this may be a rehearsal variant, with the production release likely designed to anticipate exactly this kind of cryptographic attribution analysis.

For defenders: the IronWorm threat model forces us to stop thinking about "secure packages" and start thinking about "trusted runtime execution paths." The attack chain runs through CI runners, OIDC federation configurations, preinstall lifecycle scripts, commit authorship patterns, and kernel-level visibility, none of which are currently covered by standard vulnerability management programs.


Sources used in this analysis:

  • JFrog Security Research: "IronWorm: Shai-Hulud's Rustier Cousin" (June 3, 2026)
  • Phoenix Security: "IronWorm (No CVE): Rust-Built npm Worm Ships an eBPF Rootkit, Tor C2, and a Self-Propagating Supply Chain Implant Across 37 Packages" (June 4, 2026)
  • SafeDep: IronWorm campaign catalog, 43 packages tracked
  • CSA Research Note #202 (Cloud Security Alliance): "Miasma and IronWorm: Self-Replicating Worms Targeting AI Coding Supply Chain"
  • The Hacker News: "IronWorm and New Miasma Worm Variant Hit npm in Supply Chain Attacks" (June 5, 2026)
  • Dark Reading: "Rust-Written IronWorm Hits NPM Supply Chain" (June 4, 2026)
  • OX Security: "IronWorm Supply Chain Malware Hits npm"
Topics: