Back to Intelligence

Shai-Hulud npm Supply Chain Attack: Why SBOMs Fail and How to Detect Self-Propagating Package Worms

SA
Security Arsenal Team
October 27, 2026
10 min read

The Shai-Hulud npm supply chain campaign is a watershed moment for software supply chain defense, and not for the reasons most teams think. The uncomfortable lesson from this self-propagating worm is not just that an attacker compromised maintainer accounts and poisoned dozens of widely downloaded packages — it is that organizations holding a perfectly accurate Software Bill of Materials (SBOM) still could not answer the two questions that actually matter during incident response: is the malicious code running in my environment right now, and which of my services does it touch?

Shai-Hulud spread by injecting credential-stealing payloads into npm package postinstall lifecycle scripts, harvesting developer and CI/CD secrets (npm tokens, GitHub PATs, AWS keys), and then using those stolen credentials to autonomously compromise additional maintainer accounts and publish new malicious versions. It is, in effect, a worm that moves through the dependency graph faster than any manual SBOM review can track. Exfiltrated secrets were staged into public GitHub repositories, giving defenders a noisy but detectable signal — if they knew where to look.

If your supply chain risk program begins and ends with generating a CycloneDX or SPDX document at build time, this incident should force a re-evaluation. This post breaks down how the attack worked, why static inventory failed to scope it, and provides detection rules, hunt queries, and a verification script you can run today.

Technical Analysis

Attack Chain

From a defender's perspective, the Shai-Hulud attack chain has five observable stages:

  1. Initial compromise: Attackers phished or otherwise hijacked npm maintainer accounts with publishing rights to popular packages, including packages maintained under well-known namespaces.
  2. Payload injection: Malicious versions were published containing a modified postinstall script. On npm install, the script executed a bundled payload — the campaign was observed using bun as an alternate JavaScript runtime to execute an obfuscated bundle (bun_environment.js), a deliberate evasion technique against node-only monitoring.
  3. Credential harvesting: The payload ran TruffleHog-style secret scanning against the victim's filesystem and environment variables, targeting ~/.npmrc, ~/.aws/credentials, ~/.config/gcloud, GITHUB_TOKEN, NPM_TOKEN, AWS_ACCESS_KEY_ID, and CI/CD environment variables.
  4. Exfiltration: Stolen secrets were exfiltrated via the GitHub API — the malware created public repositories named Shai-Hulud (and later variants) using the victim's own stolen GitHub token, and committed the harvested credentials to them. This is a distinctive, high-fidelity indicator: legitimate tooling does not create repositories with that name.
  5. Self-propagation: Stolen npm tokens were used to enumerate packages the victim maintained, inject the payload, and publish new versions — often via a malicious GitHub Actions workflow (shai-hulud-workflow.yml) dropped into victim repositories to capture CI secrets as well.

Affected Scope

Any organization that ran npm install against a compromised package version during the exposure window — developer workstations, CI/CD runners, Docker build agents, and production build pipelines — is potentially affected. The blast radius is defined not by which packages are listed in your SBOM, but by which compromised versions were installed and executed during the window, and whether the runner had cloud or source-control credentials in scope.

No CVE has been assigned to this campaign; it is a malware distribution operation through a trusted registry, not a software vulnerability. Do not expect your CVE-driven vulnerability scanner to flag it.

Why SBOMs Failed

An SBOM tells you what should be in your artifact at build time. It does not tell you:

  • Whether a malicious transitive dependency version was briefly installed in a CI job and then version-bumped away before the SBOM was generated.
  • Whether the credential material exposed on a runner grants access to services that never appear in any dependency manifest (AWS accounts, production Kubernetes clusters, downstream registries).
  • Whether the poisoned package executed at all — SBOMs describe components, not runtime behavior.

Scoping a Shai-Hulud-class incident requires correlating package registry telemetry (what was installed, when, on which runner) with runtime telemetry (what processes executed, what network connections they made) and identity telemetry (what did the stolen tokens actually access). That is a runtime visibility problem, not an inventory problem.

Detection & Response

The detections below target the high-fidelity behaviors of this campaign: alternate JavaScript runtimes executing from node_modules, credential file access by build tooling, suspicious repository creation, and malicious workflow files. Tune the false-positive notes to your environment before deploying at high severity.

YAML
---
title: Shai-Hulud - Alternate JS Runtime Executing Payload from node_modules
id: 3f8a1c92-7b4d-4e5a-9c21-8d6f0a1b2c3d
status: experimental
description: Detects the bun runtime or node executing obfuscated bundles from node_modules or temp paths, consistent with Shai-Hulud's postinstall payload execution (bun_environment.js).
references:
  - https://www.darkreading.com/cyber-risk/sboms-alone-scope-supply-chain-attack
  - https://attack.mitre.org/techniques/T1195/002/
author: Security Arsenal
date: 2026/04/06
tags:
  - attack.supply_chain_compromise
  - attack.t1195.002
  - attack.t1059.007
logsource:
  category: process_creation
  product: windows
detection:
  selection_runtime:
    Image|endswith:
      - '\bun.exe'
      - '\bun.exe'
  selection_args:
    CommandLine|contains:
      - 'bun_environment.js'
      - 'node_modules'
  condition: selection_runtime and selection_args
falsepositives:
  - Legitimate Bun-based build pipelines (rare in most Windows environments); baseline build agents first
level: high
---
title: Shai-Hulud - npm Lifecycle Script Spawning Secret Scanner or Shell
id: 9c2e4d71-1a3f-4b68-8e52-5f7c9d0e1a2b
status: experimental
description: Detects npm/npx spawned processes invoking trufflehog, credential scanning, or interactive shells during package install lifecycle scripts, matching Shai-Hulud's credential harvesting stage.
references:
  - https://www.darkreading.com/cyber-risk/sboms-alone-scope-supply-chain-attack
  - https://attack.mitre.org/techniques/T1552/001/
author: Security Arsenal
date: 2026/04/06
tags:
  - attack.credential_access
  - attack.t1552.001
logsource:
  category: process_creation
  product: windows
detection:
  selection_parent:
    ParentImage|endswith:
      - '\npm.exe'
      - '\npm.cmd'
      - '\node.exe'
      - '\npx.cmd'
  selection_child:
    Image|endswith:
      - '\trufflehog.exe'
      - '\cmd.exe'
      - '\powershell.exe'
      - '\pwsh.exe'
    CommandLine|contains:
      - 'trufflehog'
      - 'credentials'
      - '.npmrc'
      - 'id_rsa'
  condition: selection_parent and selection_child
falsepositives:
  - Intentional secret scanning in security CI jobs; allowlist known security pipeline runners by hostname or user
level: high
---
title: Shai-Hulud - GitHub CLI or API Creating Suspicious Exfiltration Repository
id: 5b1d8e63-2f4a-4c79-b1d3-6e8a0c2f3d4e
status: experimental
description: Detects creation of repositories named 'Shai-Hulud' or references to the campaign's exfiltration workflow file, a high-fidelity indicator of compromised credential exfiltration.
references:
  - https://www.darkreading.com/cyber-risk/sboms-alone-scope-supply-chain-attack
  - https://attack.mitre.org/techniques/T1567/
author: Security Arsenal
date: 2026/04/06
tags:
  - attack.exfiltration
  - attack.t1567
logsource:
  category: process_creation
  product: windows
detection:
  selection:
    CommandLine|contains:
      - 'Shai-Hulud'
      - 'shai-hulud-workflow'
      - 'gh repo create'
  filter_legit:
    CommandLine|contains: 'security audit'
  condition: selection and not filter_legit
falsepositives:
  - Security researchers investigating the campaign; confirm context before escalating
level: critical

The Linux equivalents of these behaviors (npm spawning sh/bash, trufflehog binary execution, access to ~/.aws/credentials from a build process) should be ingested via Sysmon for Linux or auditd and searched with the same logic.

KQL — Microsoft Sentinel / Defender
// Hunt: Shai-Hulud indicators across endpoints and CI runners
// Look for alternate JS runtimes, secret scanner execution from package managers,
// and references to the campaign's exfiltration repo / workflow names.
let timeframe = 14d;
let pm_parents = dynamic(["npm", "npm.exe", "npm.cmd", "npx", "node", "node.exe", "yarn", "pnpm"]);
union isfuzzy=true
(DeviceProcessEvents
| where TimeGenerated > ago(timeframe)
| where InitiatingProcessFileName in~ (pm_parents)
| where FileName =~ "bun" or FileName =~ "bun.exe"
   or ProcessCommandLine has_any ("trufflehog", "bun_environment.js", "shai-hulud", "Shai-Hulud")
   or (ProcessCommandLine has "credentials" and ProcessCommandLine has_any (".aws", ".npmrc"))
| project TimeGenerated, DeviceName, InitiatingProcessFileName, FileName, ProcessCommandLine, AccountName),
(Syslog
| where TimeGenerated > ago(timeframe)
| where ProcessName in~ (pm_parents) or SyslogMessage has "npm"
| where SyslogMessage has_any ("trufflehog", "bun_environment.js", "shai-hulud", ".aws/credentials", ".npmrc")
| project TimeGenerated, Computer, ProcessName, SyslogMessage),
(CommonSecurityLog
| where TimeGenerated > ago(timeframe)
| where RequestURL has "api.github.com" and (AdditionalExtensions has "Shai-Hulud" or Message has "Shai-Hulud")
| project TimeGenerated, SourceIP, RequestURL, Message)
VQL — Velociraptor
-- Velociraptor hunt: find Shai-Hulud workflow artifacts and recent npm credential exposure
-- 1) Locate malicious GitHub Actions workflow files dropped by the campaign
SELECT FullPath, Size, Mtime
FROM glob(globs='**/.github/workflows/*.yml', root='C:/')
WHERE FullPath =~ 'shai-hulud' OR FullPath =~ 'Shai-Hulud'

-- 2) Identify processes that accessed npm/AWS credential stores recently
SELECT Pid, Name, CommandLine, Username, CreateTime
FROM pslist()
WHERE CommandLine =~ 'trufflehog|bun_environment|\.npmrc|\.aws/credentials'

The following script verifies exposure: it checks for compromised package versions in lockfiles, scans for the campaign's artifacts, and audits recent repository/workflow creation on developer machines and runners.

Bash / Shell
#!/bin/bash
# Shai-Hulud exposure verification - run on build agents, dev machines, and CI runners
set -u

echo "=== [1/4] Scanning for Shai-Hulud artifacts ==="
find / -name "*shai-hulud*" -o -name "bun_environment.js" 2>/dev/null | head -50
find / -path "*/.github/workflows/*shai*" 2>/dev/null | head -20

echo "=== [2/4] Checking for bun runtime installations outside expected paths ==="
which bun 2>/dev/null; find /root /home /usr/local -name "bun" -type f 2>/dev/null | head -10

echo "=== [3/4] Auditing package-lock files for suspicious postinstall drift ==="
for f in $(find / -name "package-lock.json" -mtime -60 2>/dev/null | head -100); do
  grep -l "postinstall" "$f" 2>/dev/null && echo "  REVIEW: $f contains postinstall entries"
done

echo "=== [4/4] Checking npmrc for token exposure risk (files readable by others) ==="
find /home /root -name ".npmrc" 2>/dev/null -exec ls -l {} \; | awk '$1 ~ /.....r../ {print "WORLD-READABLE TOKEN FILE: " $NF}'

echo ""
echo "=== MANUAL STEPS REQUIRED ==="
echo "1. Audit GitHub orgs for repos named 'Shai-Hulud' or unexpected public repos:"
echo "   gh search repos 'Shai-Hulud' --limit 50"
echo "2. Rotate ALL credentials present on any machine that ran npm install in the window:"
echo "   npm tokens, GitHub PATs/OAuth, AWS keys, GCP SA keys, SSH keys"
echo "3. Review GitHub audit log for repo creation and workflow additions by CI identities"
echo "4. Revoke and re-issue npm automation tokens; enforce 2FA (TOTP/WebAuthn) for all maintainers"

Remediation

There is no patch for a supply chain worm — remediation is about containment, credential rotation, and architectural hardening:

  1. Assume credential compromise, not just code compromise. Any secret that was readable by a process that ran npm install during the exposure window must be treated as stolen. Rotate npm tokens, GitHub PATs, cloud provider keys, and SSH keys. Prioritize CI/CD identities — they typically have the broadest blast radius.
  2. Audit GitHub for exfiltration artifacts. Search your orgs and personal accounts for repositories matching the campaign's naming (Shai-Hulud), unexpected public repos, and newly added workflow files. Use the GitHub audit log API to review repo.create and workflow events by developer and automation identities during the window.
  3. Pin and verify dependencies. Enforce lockfile integrity (npm ci instead of npm install in CI), block lifecycle scripts where possible (npm ci --ignore-scripts for packages that don't legitimately need them), and consider a delay window before adopting newly published package versions — the campaign's malicious versions were live for hours to days before takedown.
  4. Harden maintainer and publisher identities. Require phishing-resistant 2FA (WebAuthn) on all npm and GitHub accounts with publish rights. Audit npm provenance attestation and enable it for your own published packages.
  5. Build the runtime correlation layer the SBOM can't provide. Forward npm registry install events, package manager process telemetry, and egress connections from build runners into your SIEM. The scoping question for the next incident is answered by joining "what was installed" with "what executed" and "what did the exposed identity access" — build that join now, not during the next incident.
  6. Egress control on build runners. Restrict outbound access from CI agents to an allowlist (registry.npmjs.org, your artifact mirror, required APIs). The campaign's GitHub API exfiltration and webhook-based channels are blocked or alerted on by default under egress policy.

For ongoing tracking, monitor CISA advisories on software supply chain compromise and the GitHub/npm security advisories feed, and subscribe maintainer namespaces you depend on to version-drift alerting.

Related Resources

Security Arsenal Penetration Testing Services AlertMonitor Platform Book a SOC Assessment vulnerability-management Intel Hub

Is your security operations ready?

Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.