Skip to content

CyberDracula/shai-hulud-2-scanner

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

56 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Shai-Hulud 1.0/2.0 CanisterWorm and other npm Supply Chain Attack IOCs Malware Scanner

Version Node.js License GitHub Stars Zero Dependencies Security Hardened No Vulnerabilities Auditable Last Commit

A forensic auditing tool designed to detect the Shai-Hulud 1.0/2.0 (and related) npm supply chain attacks. It scans local caches, global installations, and project directories against the IOCs (Indicators of Compromise) provided by Wiz Research.

πŸ›‘οΈ Security Hardening (v2.0.0)

Shai-Hulud Scanner now includes advanced security protections:

  • Symlink & Path Traversal Protection: Prevents malicious symlinks and directory escapes during scan.
  • Resource Limits: Enforces max memory, file count, and scan duration to avoid resource exhaustion.
  • CSV Injection Prevention: All CSV reports are sanitized to prevent formula/code injection.
  • Sensitive Data Redaction: API keys, user info, and secrets are never included in reports.
  • Graceful Shutdown: If interrupted, generates a partial report with all findings up to that point.

READ THIS FOR MORE INFO: https://www.wiz.io/blog/shai-hulud-2-0-ongoing-supply-chain-attack

πŸš€ Features

  • Zero Dependencies: Runs on standard Node.js (v12+). No 'npm install' required. Audit the code in 1 minute.
  • Triple Threat Intelligence: Automatically syncs with three IOC sources:
  • Custom Local IOC Feed: Add your own package IOCs in fallback/custom-iocs.txt when you cannot fetch them from an online source.
  • Smart Caching: IOC data is cached for 30 minutes to reduce network requests. Automatic fallback to offline data if network is unavailable.
  • Deep NVM Support: Automatically detects NVM installations (Windows/macOS/Linux) and scans inside every installed Node version.
  • Forensic Scan: Checks for physical malware files (setup_bun.js, bun_environment.js) regardless of version numbers.
  • Metadata Scan: Validates installed packages against live threat intelligence feeds.
  • Ghost Detection: Alerts on empty/broken directories that match target package names (potential failed malware installs).
  • Enterprise Reporting: Generates a sanitized CSV report with optional centralized upload capability for organizations.
  • Case-Insensitive Detection: Flags malware regardless of filename casing.
  • Scan Statistics: Reports total files scanned, scan duration, and summary stats.
  • Built-in Help System: Run with --help for usage instructions.
  • Environment Variable Configuration: API keys and upload URLs can be set via environment variables for secure automation.

πŸ“‹ Prerequisites

  • Node.js: Installed and available in your PATH.
  • Internet Connection: Required to fetch the latest IOC lists from:
    • Wiz Research IOC repository
    • Hemachandsai malicious packages database

πŸ› οΈ Installation

  1. Download scan.js to a centralized folder.
  2. Run it immediately β€” no installation needed!

πŸƒUsage

You can run the script directly with Node, or use the provided helper scripts.

Full System Scan (Default)

Scans all system caches (npm, Yarn, pnpm, NVM) and the current directory. Generates a local CSV report only.

node scan.js

Project-Only Scan

Pass a path to scan only that specific project directory (skips system caches for faster scanning):

node scan.js C:\Projects\MyApp
or
node scan.js /home/user/projects/myapp

Full System Scan with Custom Path

To scan both system caches AND a specific directory, use the --full-scan flag:

node scan.js C:\Projects\MyApp --full-scan
or
node scan.js /home/user/projects/myapp --full-scan

Advanced Options

Bypass Cache (Force Fresh Download)

IOC data is cached for 30 minutes. To force a fresh download:

node scan.js --no-cache

Custom IOC File (Local Overrides/Additions)

The scanner always tries to load your local custom IOC file from:

fallback/custom-iocs.txt

Supported formats (one entry per line):

package-name
package-name@1.2.3
@scope/package-name
@scope/package-name@1.2.3
package-name,1.2.3
@scope/package-name,*

Notes:

  • Empty version or * means wildcard (all versions).

  • Lines starting with # or ; are comments.

  • If you want a different location, set:

    SHAI_HULUD_CUSTOM_IOC_FILE=/absolute/or/relative/path/to/custom-iocs.txt

Set API Keys/Upload URLs via Environment Variables

For secure automation, you can set the following environment variables:

export SHAI_HULUD_API_KEY="your-secure-api-key"
export SHAI_HULUD_UPLOAD_URL="https://your-company-api.example.com/upload"

These override the defaults in scan.js and are recommended for CI/CD.

Limit Directory Traversal Depth

Control how deep the scanner traverses directories (default: 5). Useful to speed up scans or focus locally:

node scan.js --depth=3
node scan.js C:\Projects\MyApp --full-scan --depth 7

Notes:

  • Accepts --depth=<n> or --depth <n> formats
  • Applies to both project-only and full-system scan modes

CI/CD Integration (Exit Codes)

Control when the scanner fails builds based on finding severity. By default, the scanner always exits with code 0. Use --fail-on to enable CI/CD mode:

node scan.js /path/to/project --fail-on=critical

node scan.js /path/to/project --fail-on=warning

node scan.js /path/to/project --fail-on=off

Modes:

  • --fail-on=critical: Exit code 1 only on CRITICAL findings (FORENSIC_MATCH, FORENSIC_ARTIFACT, CRITICAL_SCRIPT, VERSION_MATCH, WILDCARD_MATCH, LOCKFILE_HIT, WILDCARD_LOCK_HIT)
  • --fail-on=warning: Exit code 1 on CRITICAL or WARNING findings (includes SCRIPT_WARNING, GHOST_PACKAGE, CORRUPT_PACKAGE)
  • --fail-on=off: Always exit code 0 (report only, never fail builds)

Jenkins Example:

stage('Security Scan') {
    steps {
        sh 'npx @cyberdracula/shai-hulud-2-scanner /path/to/project --fail-on=critical'
    }
}

GitHub Actions Example:

- name: Scan for Supply Chain Attacks
  run: npx @cyberdracula/shai-hulud-2-scanner /path/to/project --fail-on=critical

GitLab CI Example:

security_scan:
  script:
    - npx @cyberdracula/shai-hulud-2-scanner /path/to/project --fail-on=critical

Note: Without the --fail-on flag, the scanner maintains backwards-compatible behavior (always exits 0). This ensures existing workflows are not disrupted.

Optional: Organization Reporting

For companies/organizations only: If you want to centrally aggregate scan results across multiple machines, you can configure automatic report uploads:

  1. Edit the configuration at the top of scan.js:

    const UPLOAD_API_URL = 'https://your-company-api.example.com/upload';
    const API_KEY = 'your-secure-api-key';
  2. Run the scan normally β€” reports will be uploaded automatically.

  3. To disable uploads and only generate local CSV:

    node scan.js --no-upload

Note: By default, reports are saved locally as shai-hulud-report.csv. No data is uploaded unless you explicitly configure an API endpoint.

βœ… Known False Positives (Safe to Ignore) React Native projects: contents.json files in the ios/ folder are standard iOS asset catalog files (Xcode resources), NOT malware indicators. These are safe to ignore.

πŸͺ± Keeping the CanisterWorm IOC List Current

The CanisterWorm (fallback/canisterworm-packages.csv) list is bundled with this tool and is not auto-downloaded at scan time, because the live feed at socket.dev/supply-chain-attacks/canisterworm is protected by Cloudflare and cannot be fetched programmatically.

To update it, choose one of:

  1. Pull this repo β€” the bundled CSV is kept up to date with each release:

    git pull
  2. Download manually β€” go to socket.dev/supply-chain-attacks/canisterworm, click the Download CSV button, and replace fallback/canisterworm-packages.csv with the downloaded file.

Running node update-fallbacks.js automatically updates the Wiz and Hemachandsai feeds and reminds you of this step for CanisterWorm.


Interpreting the Report (shai-hulud-report.csv)

The CSV report includes one row per finding. Each row contains the severity level, the specific finding type, and a Campaign column that identifies the malware campaign the IOC belongs to (e.g. SHAI_HULUD_2, CANISTERWORM, or empty when unknown). Use the Campaign column to filter or group findings by campaign in spreadsheets, SIEMs, or other analysis tools.

Finding Type Severity Description Action Required
FORENSIC_MATCH πŸ”΄ CRITICAL High-confidence malware files (setup_bun.js, bun_environment.js, truffleSecrets.json, actionsSecrets.json, .github/workflows/discussion.yml) found with no content verification needed ⚠️ SYSTEM COMPROMISED. See emergency response steps below.
FORENSIC_ARTIFACT 🟠 HIGH Suspicious file detected (bundle.js, contents.json, cloud.json, environment.json) that matches malicious content signatures after deep inspection Investigate file origin. If legitimate (e.g., React Native contents.json), mark as safe. Otherwise, remove immediately.
WILDCARD_MATCH πŸ”΄ CRITICAL Package matches a strict denylist where ALL versions are malicious. ⚠️ DELETE IMMEDIATELY. Follow remediation steps below.
CRITICAL_SCRIPT πŸ”΄ CRITICAL Install/preinstall/postinstall script contains high-confidence malicious behavior (e.g., piping remote code to shell, base64β†’sh chains, privileged Docker flags, workflow backdoor files) ⚠️ ACTION NEEDED Treat as incident: isolate host, remove package, rotate credentials, investigate lateral movement.
VERSION_MATCH 🟠 HIGH Package name and version match the known infected list Uninstall package. Check lockfiles. Clear caches.
LOCKFILE_HIT 🟠 HIGH Malicious version is locked in package-lock.json/npm-shrinkwrap.json/yarn.lock/pnpm-lock.yaml/bun.lock - will auto-install on every install ⚠️ CRITICAL FOR CI/CD. Delete lockfile, remove package, regenerate.
WILDCARD_LOCK_HIT 🟠 HIGH Lockfile contains a dependency that is known malware (any version). Delete lockfile, remove dependency, regenerate with safe versions.
GHOST_PACKAGE 🟑 WARNING Folder exists with a targeted name, but is empty/broken Investigate manually. Likely a failed install or cleanup artifact.
SCRIPT_WARNING 🟑 WARNING Install/preinstall/postinstall script has suspicious indicators (e.g., obfuscation via Buffer/Base64, dynamic Function(), GitHub API/artifact usage, nc/socat) Review and validate script intent. If not business-critical, remove or pin safe version; open a security ticket.
SAFE_MATCH πŸ”΅ INFO Package name matches a target, but the version is safe No action needed. Logged for audit purposes.

Side Note for FORENSIC_MATCH: This finding can sometimes generate false positives (except setup_bun.js, bun_environment.js). Please verify if the found file is expected to be present (e.g., part of your own code or a legitimate tool). If unsure, investigate the file's origin and contents before taking action.

CSV Report Security: All CSV reports are sanitized to prevent formula/code injection. Sensitive data (API keys, user info) is never included in the report.

Partial Reports: If the scan is interrupted, a partial report is generated with all findings up to that point.

Behavioral Heuristics (Install Script Scanner)

The scanner analyzes common lifecycle hooks (preinstall, install, postinstall, prepublish, prepare) and flags:

  • High-confidence malicious patterns: piping remote content to shell, base64β†’shell chains, Docker --privileged and host mounts, workflow backdoor files
  • Suspicious indicators: obfuscation via Buffer.from(..., 'hex'|'base64'), dynamic Function(...), GitHub API/artifact usage, backdoor primitives (nc, socat)
  • Whitelisted safe scripts: typical tooling like tsc, node-gyp, prebuild-install, opencollective-postinstall, electron-builder install-app-deps, lerna bootstrap, nx/turbo run, esbuild

Indicators (What you may see in Issue_Type and Details)

The scanner emits normalized indicator tags to help triage:

  • REMOTE_CODE_EXEC: Downloading or piping remote content into shell; subshell/backtick curl/wget; bash -c with network fetch
  • OBFUSCATION: Base64/hex decoding, String.fromCharCode, escape sequences, packed or encoded payloads
  • CODE_INJECTION: eval, Function(...), direct child_process execution, sync exec/spawn usage
  • SHAI_HULUD: References to loader or payload artifacts (setup_bun, bun_environment, signature tokens)
  • PERSISTENCE: CI/CD workflow backdoor files (e.g., .github/workflows/discussion.yml)
  • PRIV_ESC: Docker --privileged runs or host filesystem mounts (-v /:/host)
  • INSECURE_NETWORK: Plain http:// network calls in install-time scripts
  • EXFIL_ATTEMPT: GitHub API interactions or Actions artifact uploads from lifecycle scripts
  • BACKDOOR_PRIMITIVE: Use of nc or socat for reverse shells or listeners

🚨 Emergency Response Steps (If FORENSIC_MATCH or WILDCARD_MATCH Found)

Your system has been compromised. The malware may have already stolen credentials.

1. Immediate Actions

Windows (PowerShell):

# Stop all package managers and builds
Stop-Process -Name node, npm, yarn, pnpm -Force -ErrorAction SilentlyContinue

# Clear all caches
npm cache clean --force
yarn cache clean --all
pnpm store prune
Remove-Item node_modules -Recurse -Force -ErrorAction SilentlyContinue

macOS/Linux (Bash):

# Stop all package managers and builds
killall -9 node npm yarn pnpm 2>/dev/null

# Clear all caches
npm cache clean --force
yarn cache clean --all
pnpm store prune
rm -rf node_modules

2. Check for Backdoors

Windows (PowerShell):

# Look for malicious GitHub workflows
Get-ChildItem -Path .github\workflows -Filter "discussion.yaml" -Recurse -ErrorAction SilentlyContinue
Get-ChildItem -Path .github\workflows -Filter "formatter_*.yml" -Recurse -ErrorAction SilentlyContinue

macOS/Linux (Bash):

# Look for malicious GitHub workflows
find .github/workflows -name "discussion.yaml" 2>/dev/null
find .github/workflows -name "formatter_*.yml" 2>/dev/null

Check for self-hosted runners (named "SHA1HULUD"):

Visit: https://github.com/<your-org>/<repo>/settings/actions/runners

3. Rotate ALL Credentials (Assume Breach)

The malware targets these secrets:

  • GitHub: Personal Access Tokens (PATs), deploy keys, Actions secrets
  • Cloud Providers: AWS credentials (AKIA keys), GCP service accounts, Azure tokens
  • Package Managers: npm tokens, registry credentials
  • Environment Variables: All API keys, database passwords, service tokens
  • SSH Keys: Regenerate all SSH keys that were on the infected machine

4. Search for Exposed Secrets

Your credentials may have been published to public GitHub repositories:

# Search GitHub for your organization's exposed data
# Visit: https://github.com/search?q="Shai-Hulud"+OR+"SHA1-HULUD"+"<your-org-name>"&type=repositories

⚠️ Cross-Victim Exposure: Your secrets may appear in repositories owned by OTHER victims. You must search broadly.

5. Clean and Rebuild

Windows (PowerShell):

# Remove lockfiles
Remove-Item package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lock, npm-shrinkwrap.json -ErrorAction SilentlyContinue

# Remove the malicious package from package.json (manually edit)

# Reinstall with safe versions
npm install

# Verify no malware files remain
Get-ChildItem -Recurse -Include setup_bun.js, bun_environment.js, truffleSecrets.json, cloud.json, contents.json, environment.json, actionsSecrets.json

macOS/Linux (Bash):

# Remove lockfiles
rm -f package-lock.json yarn.lock pnpm-lock.yaml bun.lock npm-shrinkwrap.json

# Remove the malicious package from package.json (manually edit)

# Reinstall with safe versions
npm install

# Verify no malware files remain
find . -type f \( -name "setup_bun.js" -o -name "bun_environment.js" -o -name "truffleSecrets.json" -o -name "cloud.json" -o -name "contents.json" -o -name "environment.json" -o -name "actionsSecrets.json" \)

6. CI/CD Pipeline Review

LOCKFILE_HIT is especially dangerous in CI/CD environments:

  • Malware executes during preinstall (before your code runs)
  • Runs synchronously in CI (blocks until complete exfiltration)
  • May attempt Docker privilege escalation
  • Can modify sudoers for persistent root access

Action: Review all CI/CD logs from the past 30 days for:

  • Unexpected network connections
  • Failed privilege escalation attempts
  • New GitHub runners registered
  • Unusual workflow executions

Understanding LOCKFILE_HIT Risk

When found in both project and cache, this means:

  1. Automatic Reinfection: Every npm install will reinstall the malware
  2. Team-Wide Exposure: All developers who clone the repo will be infected
  3. CI/CD Compromise: Every pipeline run will execute the malware
  4. Container Builds: Every Docker build will be compromised

The lockfile acts as a time bomb - the malware will keep coming back until you remove it from the lockfile AND rotate all credentials.

πŸ”„ Updating Offline Fallback Files

The scanner includes offline fallback IOC files in the fallback/ directory. To keep them current:

node update-fallbacks.js

This utility:

  • Downloads the latest threat intelligence from both sources
  • Shows current file status (age and size)
  • Updates the fallback files used when offline
  • Run this periodically to maintain up-to-date offline data

Disclaimer

This tool is provided "as is" to assist in detection. It relies on public IOCs from Wiz Research. False negatives are possible if the malware authors change file names or package versions. Always perform manual verification on critical systems.

Contributing

Contributions are welcome! Please submit issues or pull requests on the GitHub repository.

Contributors

Special thanks to everyone who has contributed to the Shai-Hulud 2.0 Scanner project:

  • MaSMas0 (https://github.com/MaSMas0) β€” Security hardening in 2.0
  • Hemachandsai (malicious package IOC feed)
  • Wiz Research (threat intelligence)

About

A forensic auditing tool designed to detect the Shai-Hulud 2.0 (and related) npm supply chain attacks. It scans local caches, global installations, and project directories against the IOCs (Indicators of Compromise) provided by Wiz Research.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages