Shai-Hulud npm worm supply chain attack

Updated:

In Frank Herbert’s Dune, “Shai-Hulud” is the sacred name for the massive sandworms of Arrakis, creatures so immense they reshape entire planetary ecologies from beneath the surface. In 2025, the open-source software world met its own Shai-Hulud not once, but twice.

First in September, then again in November, a self-replicating malware campaign burrowed into the npm registry, hijacking legitimate packages, stealing credentials at scale, and autonomously backdooring hundreds of libraries used by millions of developers. Unlike static typosquatting or phishing-style malware, Shai-Hulud was alive. It propagated, adapted, and persisted using the very infrastructure developers trust.

This article outlines the story of Shai-Hulud V1 and V2: how they worked, why they succeeded, how the community responded, and what this means for the future of software supply chain security.

The First Worm (September 2025)

Discovery and Initial Impact

On September 15, 2025, researchers at ReversingLabs identified a disturbing new pattern in the npm ecosystem. This attack compromised widely used libraries such as @ctrl/tinycolor, ngx-bootstrap, and ng2-file-upload, projects with millions of weekly downloads. Within a few days, over 200 packages and 500 versions had been backdoored, igniting a chain reaction across the npm dependency graph.

The malware was given the name Shai-Hulud, after a suspicious GitHub repository tied to the earliest infections. Its design departed sharply from prior npm threats. This was not a one-off payload, but a self-replicating worm capable of hijacking maintainers’ identities and spreading autonomously.

Infection Mechanism and Credential Theft

Shai-Hulud V1 infected packages by injecting a postinstall script into the package.json file. When a developer or CI/CD system ran npm install, the script would execute bundle.js after the package installation completed. This payload was compact but effective. It scanned the local filesystem for npm tokens in .npmrc, GitHub personal access tokens in environment variables, and cloud credentials in standard configuration files.

Critically, the worm also bundled TruffleHog, the open-source secret scanner, to scour the user’s entire home directory for embedded secrets in source code, config files, and even Git history. All harvested data was then exfiltrated to external webhook endpoints like webhook[.]site.

Autonomous Propagation

What truly distinguished V1 was its ability to self-replicate without human intervention. If it found a valid npm token, it would:

  • Validate the token via https://registry.npmjs.org/-/whoami
  • Query the npm registry for all packages owned by that maintainer
  • Download each tarball, inject the malicious postinstall hook and bundle.js
  • Bump the patch version to mimic a routine update
  • Republish the backdoored package using npm publish

This turned every compromised maintainer into an unwitting distributor, creating a cascading effect through the ecosystem.

Containment

By September 18, 2025, coordinated efforts by npm, ReversingLabs, Zscaler, and others had largely contained the outbreak. Malicious versions were removed from the registry, compromised tokens were revoked, and alerts were issued to affected projects. The attack’s reliance on external webhooks made it relatively easy to disrupt. IP blacklists and rate-limiting quickly cut off its command-and-control channel. V1 was stopped, but its architects were already adapting.

The Second Coming (November 24, 2025)

Resurgence with a Redesigned Architecture

On November 24, 2025, security teams at Datadog and Zscaler detected a far more sophisticated wave: Shai-Hulud 2.0, internally dubbed “The Second Coming” by its operators. Within hours, it had backdoored 796 unique npm packages representing over 20 million weekly downloads, created more than 27,000 malicious GitHub repositories, and exfiltrated an estimated 14,000 secrets across 487 organizations.

Patient Zero was traced to a malicious commit in the asyncapi/cli GitHub repository, a pull request from a now-deleted account with no prior activity. The timing aligned with the tail end of npm’s migration from classic to granular tokens, potentially exploiting overprivileged legacy credentials.

Key Technical Upgrades

Shai-Hulud V2 wasn’t merely an evolution, it was a complete re-architecture informed by V1’s weaknesses.

The worm shifted from postinstall to the preinstall lifecycle hook, ensuring execution even if package installation failed. This subtle change dramatically increased reliability.

It also abandoned Node.js in favor of Bun, the high-performance JavaScript runtime. A lightweight dropper (setup_bun.js) installed Bun from the legitimate bun.sh domain, then launched a 480,000-line obfuscated payload (bun_environment.js) as a detached background process. This move bypassed many Node.js-focused EDR and sandboxing tools.

Perhaps most insidiously, V2 abandoned external C2 servers entirely. Instead, it exfiltrated data by creating public GitHub repositories with random 18-character names and the fixed description “Sha1-Hulud: The Second Coming.” It uploaded four double Base64-encoded files containing GitHub/npm tokens, environment dumps, cloud secrets, and TruffleHog results, all over GitHub’s own API, blending malicious traffic with legitimate developer activity.

If no local GitHub token was available, the worm searched GitHub for other victims’ exfiltration repos, downloaded their contents.json, and validated stolen tokens. This “cross-victim credential recycling” created a decentralized, resilient botnet: even if one token was revoked, the worm could persist using another’s.

V2 also deployed persistent backdoors by installing a self-hosted GitHub Actions runner named SHA1HULUD in ~/.dev-env/. It then created a workflow file (discussion.yaml) containing a command injection vulnerability: echo ${{ github.event.discussion.body }}. Any attacker could trigger arbitrary code execution simply by posting a GitHub Discussion containing shell commands like $(curl attacker.com).

And if both propagation and exfiltration failed, it activated a “Dead Man’s Switch”: on Windows, it deleted and securely wiped the user profile with cipher /W; on Linux and macOS, it shredded all writable files in $HOME and cleaned up empty directories.

Finally, V2 demonstrated deep cloud platform awareness. It used official SDKs to harvest credentials not just from environment variables, but from AWS IMDS, GCP ADC, and Azure DefaultAzureCredential, and even retrieved secrets directly from AWS Secrets Manager, GCP Secret Manager, and Azure Key Vault across multiple regions.

Containment of V2

The last known malicious package was published on November 24 at 6 p.m. UTC, strongly suggesting that npm implemented emergency countermeasures, likely including enhanced token validation, rate-limiting on rapid version bumps, and registry-side static analysis.

GitHub began mass takedowns of exfiltration repositories, though thousands remain accessible as of December 2025. Notably, researchers have found no public evidence that attackers have yet exploited the GitHub runner backdoor. But the infrastructure is in place, and future exploitation remains a serious risk.

The response was swift and collaborative: Datadog, Zscaler, ReversingLabs, and others published cross-vendor IOCs, updated SCA tools, and released detection scripts. The open-source community rallied to audit dependencies and revoke credentials.

Comparative Anatomy: V1 vs. V2

Shai-Hulud V2 was not just an incremental update, it was a strategic overhaul. Where V1 used postinstall hooks, external webhooks, and basic credential harvesting, V2 operated with surgical precision: preinstall execution, GitHub-native exfiltration, cloud-aware secret retrieval, persistent CI/CD backdoors, and a resilient credential-recycling network. Most critically, V2 introduced a destructive failsafe absent in the first wave, signaling a shift from pure data theft to punitive retaliation against containment.

The scale reflected this evolution too: from ~200 packages in V1 to nearly 800 in V2, and from simple webhook exfiltration to a sprawling network of more than 27,000 GitHub repositories. The attackers had learned that stealth, adaptability, and autonomy were the keys to survival in a vigilant ecosystem.

Lessons from the Saga

The Shai-Hulud saga reveals that the primary attack surface in modern software supply chains is no longer the package itself, but the identity of the maintainer. Overprivileged, long-lived tokens remain a systemic vulnerability, especially when tied to automated publishing workflows.

Runtime diversity, such as the adoption of Bun, also expands the threat landscape beyond traditional Node.js monitoring. Meanwhile, native developer platforms like GitHub are increasingly weaponized not just for exfiltration, but for command-and-control and persistence.

Most importantly, automation cuts both ways. The same CI/CD pipelines and npm publish scripts that accelerate development also amplify malware propagation when compromised. Defenders must assume that any compromised credential can trigger an ecosystem-wide cascade.

The Worm Below

Shai-Hulud V1 and V2 represent a paradigm shift in supply chain threats. They are not static payloads waiting to be downloaded, they are living, adaptive entities that exploit trust, automation, and interconnectedness to spread with minimal human involvement.

The community’s rapid containment of both waves shows that collaboration, transparency, and technical vigilance can mitigate even sophisticated attacks. But the blueprint is now public. Future variants will borrow these tactics: preinstall hooks, Bun evasion, GitHub exfiltration, self-hosted runners.

Defending the open-source ecosystem now requires more than dependency scanning. It demands proactive identity hygiene, runtime integrity monitoring, zero-trust CI/CD, and a fundamental rethinking of how we manage access in software development. The sandworm has been spotted. It will return.