Back to Intelligence

npm Supply Chain Attacks: Detecting Shai Hulud-style Malware and CI/CD Persistence

SA
Security Arsenal Team
April 26, 2026
7 min read

The JavaScript ecosystem is currently the battleground for one of the most sophisticated supply chain campaigns we've seen in years. Following Unit 42's in-depth analysis of the post-"Shai Hulud" threat landscape, it is clear that the era of simple typosquatting has evolved into wormable, multi-stage attacks designed to persist within CI/CD environments.

For security practitioners, this means that the traditional perimeter defense model is obsolete. If your organization uses Node.js, your build pipelines and developer workstations are actively targeted by adversaries looking to steal credentials, inject malicious code, and propagate laterally across your software supply chain. The urgency to implement runtime controls and audit your dependency tree cannot be overstated.

Technical Analysis

Unit 42's research highlights a disturbing evolution in npm-focused malware. We are no longer dealing solely with benign packages that merely log data; we are facing self-propagating malware capable of evading static analysis and establishing persistence.

Affected Platforms & Products:

  • Platform: Node.js (all versions), npm registry.
  • Environment: CI/CD pipelines (GitHub Actions, Jenkins, CircleCI), developer workstations (Windows, Linux, macOS).

Attack Vector & Mechanics: The attack chain typically initiates when a developer or build system installs a malicious package. The threat actors use two primary methods to gain initial access:

  1. Typosquatting: Registering packages with names that are deliberate misspellings of popular libraries (e.g., express vs expresss).
  2. Dependency Confusion: Publishing malicious packages with higher version numbers than private internal packages.

Post-Exploitation & The "Shai Hulud" Evolution: Once installed, the malicious package executes code via preinstall, postinstall, or prepublish scripts defined in package..

  • Wormable Behavior: Unlike standard malware, these variants can modify package. files in the parent directory or sibling projects, effectively "infecting" other legitimate projects on the same filesystem or network share.
  • Obfuscation: Attackers are using heavy obfuscation (Base64, hex encoding) within index.js or bin files to hide malicious payloads, often utilizing child_process to spawn reverse shells.
  • Data Exfiltration: The malware actively scrapes environment variables (.env, .npmrc, AWS credentials, GitHub tokens) and exfiltrates them via DNS tunneling or HTTPS POST requests to actor-controlled C2 servers.
  • CI/CD Persistence: In a pipeline context, the malware seeks to overwrite build scripts or compromise pipeline secrets, ensuring malicious code is shipped to production.

Exploitation Status: Active exploitation is confirmed in the wild. These packages are not theoretical; they are being downloaded thousands of times by unsuspecting developers and automated build bots.

Detection & Response

Sigma Rules

YAML
---
title: Potential Malicious NPM Package Execution via Shell Spawning
id: 8a4f2c1d-5e6b-4a3c-9d1e-0f2a3b4c5d6e
status: experimental
description: Detects Node.js processes spawning suspicious shells (cmd, powershell, bash), a common TTP in malicious npm packages like Shai Hulud to execute post-exploitation payloads.
references:
  - https://unit42.paloaltonetworks.com/monitoring-npm-supply-chain-attacks/
author: Security Arsenal
date: 2024/05/22
tags:
  - attack.execution
  - attack.t1059
  - attack.supply_chain
logsource:
  category: process_creation
  product: windows
detection:
  selection_parent:
    ParentImage|endswith:
      - '\node.exe'
      - '\npm.cmd'
  selection_child:
    Image|endswith:
      - '\cmd.exe'
      - '\powershell.exe'
      - '\pwsh.exe'
  filter_legit:
    # Filter out common developer build tools that might spawn shells legitimately
    CommandLine|contains:
      - 'webpack'
      - 'gulp'
      - 'grunt'
      - 'vite'
  condition: selection_parent and selection_child and not filter_legit
falsepositives:
  - Legitimate build scripts utilizing shell commands for automation
level: high
---
title: NPM Process Modifying Git Configuration
id: 9b5e3d2f-6f7c-5b4d-0e2f-1a3b4c5d6e7f
status: experimental
description: Detects npm or node processes attempting to modify .git/config or creating hooks, indicative of persistence mechanisms or supply chain tampering.
references:
  - https://unit42.paloaltonetworks.com/monitoring-npm-supply-chain-attacks/
author: Security Arsenal
date: 2024/05/22
tags:
  - attack.persistence
  - attack.t1136
logsource:
  category: file_change
  product: windows
detection:
  selection:
    Image|endswith:
      - '\node.exe'
      - '\npm.exe'
    TargetFilename|contains:
      - '\.git\config'
      - '\.git\hooks\'
  condition: selection
falsepositives:
  - Legitimate setup scripts configuring git for the first time
level: medium
---
title: Suspicious NPM Install with Network Connection
id: 1c2d3e4f-5a6b-7c8d-9e0f-1a2b3c4d5e6f
status: experimental
description: Detects npm install events followed closely by a network connection from the node process to a non-standard public IP, suggesting C2 beacons or data exfiltration.
references:
  - https://unit42.paloaltonetworks.com/monitoring-npm-supply-chain-attacks/
author: Security Arsenal
date: 2024/05/22
tags:
  - attack.exfiltration
  - attack.t1041
logsource:
  category: network_connection
  product: windows
detection:
  selection:
    Image|endswith:
      - '\node.exe'
    Initiated: 'true'
    DestinationPort|notin:
      - '80'
      - '443'
      - '8080'
  filter:
    DestinationIp|cidr:
      - '10.0.0.0/8'
      - '172.16.0.0/12'
      - '192.168.0.0/16'
      - '127.0.0.0/8'
  condition: selection and not filter
falsepositives:
  - Local development servers connecting to backend DBs or APIs
level: medium

KQL (Microsoft Sentinel / Defender)

KQL — Microsoft Sentinel / Defender
// Hunt for NPM processes spawning shells or making suspicious network connections
let SuspiciousParents = dynamic(["node.exe", "npm.cmd"]);
let SuspiciousChildren = dynamic(["cmd.exe", "powershell.exe", "pwsh.exe", "bash.exe"]);
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ SuspiciousParents
| where FileName in~ SuspiciousChildren
| project Timestamp, DeviceName, AccountName, InitiatingProcessCommandLine, ProcessCommandLine, FileName, FolderPath
| distinct *
// Join with network events to see if Node connected to external IPs shortly after
| join kind=inner (
    DeviceNetworkEvents
    | where Timestamp > ago(7d)
    | where InitiatingProcessFileName in~ SuspiciousParents
    | where RemoteIP !startswith "10." and RemoteIP !startswith "192.168." and RemoteIP !startswith "127."
    | project Timestamp, DeviceName, RemoteIP, RemotePort, RemoteUrl, InitiatingProcessFileName
) on DeviceName, Timestamp

Velociraptor VQL

VQL — Velociraptor
-- Hunt for package. files with suspicious scripts (postinstall, preinstall)
-- and identify node processes connecting to the network
LET package_files = SELECT FullPath, Mtime
FROM glob(globs="/*/package.")
WHERE read_file(filename=FullPath)
   =~ "(?i)(preinstall|postinstall|prepublish)" AND
   read_file(filename=FullPath) =~ "(?i)(curl|wget|base64|eval|child_process)"

SELECT package_files.FullPath, package_files.Mtime,
       Pid, Name, CommandLine, Exe
FROM pslist()
WHERE Name = "node" AND CommandLine =~ "(http|https)"

Remediation Script (PowerShell)

PowerShell
# PowerShell Script to Audit NPM Environment for Malicious Packages
# Usage: Run as Administrator in the root of your project or CI agent workspace

Write-Host "[*] Starting NPM Security Audit..." -ForegroundColor Cyan

# 1. Check for globally installed packages that might be malicious
Write-Host "[+] Auditing Global NPM Packages..." -ForegroundColor Yellow
$globalPackages = npm list -g --depth=0 2>&1
Write-Host $globalPackages

# 2. Check for packages with known suspicious scripts (heuristic)
Write-Host "[+] Scanning package. for suspicious scripts..." -ForegroundColor Yellow
if (Test-Path "package.") {
    $content = Get-Content "package." -Raw
    $suspiciousStrings = @("curl", "wget", "base64", "child_process", "eval(", "powershell", "cmd.exe")
    
    foreach ($str in $suspiciousStrings) {
        if ($content -match $str) {
            Write-Host "[!] WARNING: Found '$str' in package.. Please review manually." -ForegroundColor Red
        }
    }
} else {
    Write-Host "[!] No package. found in current directory." -ForegroundColor Yellow
}

# 3. Run NPM Audit
Write-Host "[+] Running 'npm audit' for known vulnerabilities..." -ForegroundColor Yellow
npm audit --audit-level=moderate

# 4. Check Environment Variables for exposed secrets (heuristic)
Write-Host "[+] Checking Process Environment for leaked secrets..." -ForegroundColor Yellow
$envVars = Get-ChildItem Env:
$keywords = @("API_KEY", "TOKEN", "SECRET", "PASSWORD", "AWS_")
foreach ($var in $envVars) {
    foreach ($key in $keywords) {
        if ($var.Name -like "*$key*") {
            Write-Host ("[!] Potential Secret found in env: " + $var.Name) -ForegroundColor Red
        }
    }
}

Write-Host "[*] Audit Complete." -ForegroundColor Green

Remediation

Immediate and sustained action is required to secure the software supply chain against these sophisticated npm threats.

  1. Patch and Update:

    • Run npm audit fix immediately across all projects to automatically update vulnerable dependencies.
    • Ensure Node.js and npm are running on the latest Long Term Support (LTS) versions.
  2. Lock Down Dependency Installation:

    • Review package-lock. files. Ensure they are committed to version control to prevent developers from accidentally installing new, potentially malicious minor versions.
    • Enforce the use of the --ignore-scripts flag in CI/CD pipelines (npm install --ignore-scripts) if build scripts are not strictly required, or review all scripts in package. manually.
  3. Network Egress Controls:

    • Block outbound internet access from build agents except to specific, necessary endpoints (e.g., registry.npmjs.org, github.com). This prevents C2 beacons and data exfiltration.
  4. Developer Hygiene:

    • Educate developers on typosquatting. Encourage the use of browser extensions or IDE plugins that verify package popularity and author integrity before installation.
    • Never run npm install with elevated privileges (sudo/root) unless absolutely necessary.
  5. Supply Chain Verification:

    • Implement tools like npm audit, snyk, or GitHub Dependabot for automated scanning.
    • Consider adopting the Sigstore ecosystem for signing and verifying packages.

Related Resources

Security Arsenal Incident Response Services AlertMonitor Platform Book a SOC Assessment incident-response Intel Hub

incident-responseransomwarebreach-responseforensicsdfirnpmsupply-chainnodejs

Is your security operations ready?

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