Two Separate npm Attacks, One Very Bad Week for Developers

The npm registry has taken two distinct hits in quick succession, each from a different threat actor using a different malware family, but both exploiting the same fundamental vulnerability: developers who install packages without suspecting the source has been corrupted. Supply chain attacks against npm are not new, but the technical depth on display here — kernel rootkits, Tor-based command channels, GitHub Actions hijacking — marks a level of engineering investment that goes well beyond the typical credential-scraping script dropped into a malicious package.

JFrog disclosed the first campaign, centered on malware they’ve codenamed IronWorm. Separately, Endor Labs and StepSecurity identified a second campaign distributing a new variant of the Miasma worm, which had already made an appearance earlier in the week — infecting 32 packages across more than 90 versions under the @redhat-cloud-services npm namespace within 72 seconds.

The two operations appear unrelated. Their timing, however, is not a coincidence worth ignoring.

IronWorm is a Rust-compiled ELF binary that enters developer environments through trojanized npm packages published by a compromised account named asteroiddao. The binary executes via a preinstall hook — a standard npm lifecycle event that fires before a package installs — meaning a developer doesn’t need to import or run anything manually. Downloading the package is enough.

Once running, IronWorm targets 86 environment variables and scans for credential files tied to a specific list of services: OpenAI Codex, Anthropic, Claude, Google Gemini, Cursor, AWS, Docker, Kubernetes, npm, vault configurations, and Exodus cryptocurrency wallet files. The scope is deliberately broad, optimized for developer workstations where API keys and cloud credentials tend to accumulate. It hides its processes using an eBPF payload that acts as a kernel-level rootkit, and communicates with its operator over Tor — a configuration that eliminates fixed infrastructure that could be taken down or blocked.

One detail stands out as oddly cautious: the wallet-theft component contains logic to skip the threat actor’s own cryptocurrency wallet. As of the time of JFrog’s disclosure, that wallet held a zero balance and had recorded no transactions. Whether that wallet address reflects operational security or simply hasn’t been used yet remains unclear.

How IronWorm Moves Laterally Across GitHub

The self-propagation mechanism is where IronWorm earns its name. After stealing credentials from an infected machine, it uses those credentials to push malicious commits into GitHub repositories the victim has access to. JFrog traced this behavior through a specific chain: the asteroiddao npm account links to the asteroid-dao GitHub organization, where a user named ocrybit is a member — and also a member of related Arweave organizations. IronWorm stole ocrybit’s credentials and used them to inject malicious code across repositories that account could reach, then disappeared.

The malicious commits were authored under the name claude with the email claude@users.noreply.github.com, an obvious attempt to impersonate Anthropic’s AI assistant and blend into developer commit histories where AI-generated contributions are increasingly common. Those poisoned commits span nine GitHub organizations. Any developer who pulled updated dependencies from those repositories would then become the next infected host.

To avoid needing a traditional command-and-control server — which can be detected, blocked, or seized — IronWorm replaces existing GitHub Actions workflows with modified versions that harvest repository secrets, write them to a benign-looking file, and upload that file as a build artifact. In CI environments specifically, it abuses npm’s Trusted Publishing flow to obtain short-lived tokens and push poisoned package versions directly to the registry. The attack chain is largely self-contained: infection spreads through code, exfiltration happens through GitHub’s own infrastructure, and no external server needs to stay online.

The eBPF rootkit is effective on most systems but has a documented weakness: when kernel lockdown mode is enabled, the process-hiding functionality fails, and the malware’s processes and sockets become visible.

Miasma Returns, Faster and Wider

While IronWorm was making its way through GitHub organizations, a separate actor was running a different campaign entirely. The Miasma worm — which had already infected 32 packages across more than 90 versions under the @redhat-cloud-services namespace within a 72-second window earlier in the week — resurfaced in a new variant. This time, Endor Labs and StepSecurity identified 57 compromised npm packages spread across more than 286 malicious versions.

The list of affected packages includes names that surface frequently in JavaScript development: ai-sdk-ollama, autotel, awaitly, effect-analyzer, eslint-plugin-awaitly, executable-stories-cypress, http-uploader-dev, mountly, node-env-resolver, node-env-resolver-aws. These aren’t obscure packages. They show up in dependency trees that developers don’t always audit carefully, which is precisely what makes them effective delivery vehicles.

Stolen data from the Miasma variant was exfiltrated to a GitHub account named liuende501, which had 236 repositories staged and ready. Whether GitHub removed the account or the operator deleted it before attribution was complete is not known at the time of writing.

What the Architecture of These Attacks Actually Requires

It is worth being direct about what these two campaigns demonstrate. Both IronWorm and the Miasma variant are not opportunistic scripts. They require understanding of npm’s publishing flow, GitHub Actions internals, eBPF programming, Tor routing, and the credential storage habits of professional developers. The decision to use Rust — which compiles to a single binary with no external runtime dependencies and is harder to reverse-engineer than Python or JavaScript — is an architectural choice, not a default.

The use of preinstall hooks as an execution trigger is particularly difficult to defend against at the individual developer level. npm does not block preinstall scripts by default, and most developers do not audit lifecycle scripts before installation. The --ignore-scripts flag prevents these hooks from running, but it also breaks legitimate packages that depend on them — a practical tradeoff that most development workflows don’t accommodate.

For CI/CD pipelines specifically, the Trusted Publishing abuse is the most corrosive element of IronWorm’s design. Short-lived tokens obtained through legitimate OIDC flows leave minimal audit trails. Once a poisoned version reaches the registry with a trusted signature, downstream consumers have little reason to question it.

The Registry Doesn’t Catch This at the Gate

npm’s package registry is not a curated store. Any account can publish any package, and packages are not reviewed before they become available. The asteroiddao account published trojanized versions of legitimate packages — a poisoning strategy that takes advantage of the gap between when a package is submitted and when security tooling flags it, if it ever does.

The 72-second infection window from the earlier Miasma campaign is the clearest illustration of that gap. Fifty-seven packages compromised across 286 versions is not a narrow attack — it suggests automated publishing pipelines on the attacker’s side, running faster than any manual detection process could respond.

Developers running dependency audits after the fact are working against a malware family that is specifically designed to have already moved on by the time anyone looks.

The cryptocurrency wallet tied to IronWorm still sits at zero.