| name | security-axios |
| description | Scan ALL projects on this Mac for the axios supply-chain attack (2026-03-31). Detects compromised versions (axios@1.14.1, axios@0.30.4), malicious dependency (plain-crypto-js@4.2.1), related campaign packages (@shadanai/openclaw, @qqbrowser/openclaw-qbot), and macOS RAT IOC (/Library/Caches/com.apple.act.mond) with SHA256 verification. Scans package.json, package-lock.json, yarn.lock, and pnpm-lock.yaml. Checks launch persistence and dropper anti-forensics. Generates an HTML report and opens it in the default browser. Use when the user says "scan for axios attack", "check axios supply chain", "axios malware scan", "am I affected by axios compromise", or "security-axios". |
Security: Axios Supply-Chain Attack Scanner
Mission
Scan the entire Mac for the axios npm supply-chain compromise (2026-03-31).
Check package.json and all lock files (package-lock.json, yarn.lock, pnpm-lock.yaml).
Detect anti-forensics (dropper self-cleanup), related campaign packages, and verify RAT SHA256.
Produce a full HTML threat report and open it automatically.
Background
On 2026-03-31, axios versions 1.14.1 and 0.30.4 were published via a compromised maintainer
account (jasonsaayman, email changed to ifstap@proton.me) with an injected dependency
plain-crypto-js@4.2.1. Its postinstall hook (setup.js) deployed a cross-platform RAT that
beacons to sfrclak[.]com:8000 every 60 seconds. On macOS the RAT binary is
/Library/Caches/com.apple.act.mond. The dropper self-destructs after execution: setup.js is
deleted and package.json is replaced with a clean stub reporting version 4.2.0 — so
post-infection node_modules inspection will NOT reveal the malicious manifest. The directory
presence of node_modules/plain-crypto-js/ alone confirms the dropper ran.
The malicious dependency was pre-staged ~18 hours earlier by attacker account nrwise
(nrwise@proton.me). A clean decoy version (4.2.0) was published first to build registry history.
Both compromised axios versions were published manually with a stolen npm token — no OIDC Trusted
Publisher binding, no gitHead, no corresponding GitHub commit or tag.
References
Threat Intelligence
| Category | Value |
|---|
| Compromised axios versions | 1.14.1, 0.30.4 |
| Malicious dependency | plain-crypto-js@4.2.1 |
| Related campaign packages | @shadanai/openclaw (v2026.3.28-2, 2026.3.28-3, 2026.3.31-1, 2026.3.31-2), @qqbrowser/openclaw-qbot (v0.0.130) |
| macOS RAT binary | /Library/Caches/com.apple.act.mond |
| macOS RAT SHA256 | 92ff08773995ebc8d55ec4b8e1a225d0d1e51efa4ef88b8849d0071230c9645a |
| Windows RAT | %PROGRAMDATA%\wt.exe (PowerShell copy), %TEMP%\6202033.ps1, %TEMP%\6202033.vbs |
| Windows RAT SHA256 | 617b67a8e1210e4fc87c92d1d1da45a2f311c08d26e89b12307cf583c900d101 (PS1) |
| Linux RAT | /tmp/ld.py |
| Linux RAT SHA256 | fcb81618bb15edfdedfb638b4c08a2af9cac9ecfa551af135a8402bf980375cf |
| C2 domain | sfrclak[.]com |
| C2 IP | 142.11.206.73 |
| C2 port | 8000 |
| Campaign path | /6202033 |
| C2 POST body (macOS) | packages.npm.org/product0 |
| C2 POST body (Windows) | packages.npm.org/product1 |
| C2 POST body (Linux) | packages.npm.org/product2 |
| Infection vector | npm install / yarn install / pnpm install postinstall hook |
| Compromised account | jasonsaayman (email changed to ifstap@proton.me) |
| Attacker account | nrwise (nrwise@proton.me) — published plain-crypto-js |
| GitHub Advisory | GHSA-fw8c-xr5c-95f9 |
| OSV Advisory | MAL-2026-2306 |
| Attack window | 2026-03-31 00:21–03:15 UTC (~3 hours) |
| Windows persistence | Registry Run key MicrosoftUpdate, re-download batch file |
| macOS dropper method | AppleScript via osascript writing to /tmp/6202033 |
Package SHA256 Hashes
| Package | SHA256 |
|---|
axios-1.14.1.tgz | 5bb67e88846096f1f8d42a0f0350c9c46260591567612ff9af46f98d1b7571cd |
axios-0.30.4.tgz | 59336a964f110c25c112bcc5adca7090296b54ab33fa95c0744b94f8a0d80c0f |
plain-crypto-js-4.2.1.tgz | 58401c195fe0a6204b42f5f90995ece5fab74ce7c69c67a24c61a057325af668 |
com.apple.act.mond (macOS) | 92ff08773995ebc8d55ec4b8e1a225d0d1e51efa4ef88b8849d0071230c9645a |
stage2.ps1 (Windows) | 617b67a8e1210e4fc87c92d1d1da45a2f311c08d26e89b12307cf583c900d101 |
ld.py (Linux) | fcb81618bb15edfdedfb638b4c08a2af9cac9ecfa551af135a8402bf980375cf |
Package SHA1 Shasums (npm)
| Package | SHA1 |
|---|
axios@1.14.1 | 2553649f232204966871cea80a5d0d6adc700ca |
axios@0.30.4 | d6f3f62fd3b9f5432f5782b62d8cfd5247d5ee71 |
plain-crypto-js@4.2.1 | 07d889e2dadce6f3910dcbc253317d28ca61c766 |
Anti-Forensics Awareness
The dropper (setup.js) performs three cleanup actions after execution:
- Deletes
setup.js — fs.unlink(__filename) removes itself
- Deletes
package.json — removes the manifest with "postinstall": "node setup.js"
- Renames
package.md to package.json — a pre-staged clean stub reporting version 4.2.0
Post-infection, npm list shows plain-crypto-js@4.2.0 (not 4.2.1). Incident responders
checking for version 4.2.1 may incorrectly conclude the system is clean. The reliable check is
directory presence: if node_modules/plain-crypto-js/ exists at all, the dropper ran — this
package is not a dependency of ANY legitimate axios version.
Our scanner checks:
node_modules/plain-crypto-js/ directory existence (confirms dropper ran)
node_modules/plain-crypto-js/setup.js presence (dropper not yet cleaned up)
Execution Flow
GREP PRE-FILTER → PARSE → LOCK FILES → RELATED PKGS → IOC CHECK → SHA256 VERIFY → LAUNCH PERSIST → REPORT → OPEN
What Is Scanned
| File / Check | What We Check |
|---|
package.json | declared axios version, plain-crypto-js in all dep sections, related campaign packages |
package-lock.json | resolved axios version, plain-crypto-js in packages tree, related campaign packages |
yarn.lock | resolved axios version, any mention of plain-crypto-js |
pnpm-lock.yaml | resolved axios version, any mention of plain-crypto-js |
node_modules/plain-crypto-js/ | directory presence (confirms dropper ran even after self-cleanup) |
node_modules/plain-crypto-js/setup.js | dropper file still present (not yet cleaned up) |
/Library/Caches/com.apple.act.mond | RAT binary present + SHA256 hash verification against known IOC |
running processes via pgrep | RAT process (FP-safe — pgrep excludes itself) |
lsof -i | active C2 connection to sfrclak.com or 142.11.206.73 |
~/Library/LaunchAgents/, /Library/LaunchAgents/, /Library/LaunchDaemons/ | launch persistence referencing act.mond, sfrclak, or C2 IP |
| global npm/yarn/pnpm | plain-crypto-js installed globally |
False-Positive Notes
Process check FP risk: NEVER use ps aux | grep act.mond — a bash heredoc containing that string
would match itself. Use pgrep -f "/Library/Caches/com.apple.act.mond" instead — pgrep excludes
its own PID, and the script runs as python3 /tmp/file.py (no script content in command line).
Anti-forensics version mismatch: After dropper cleanup, npm list plain-crypto-js reports
4.2.0 (not 4.2.1). The scanner checks directory existence rather than version number.
Scripts
The scanner is split into focused modules under scripts/:
skills/security-axios/scripts/
├── axios_scan.py # Entry point — orchestrates scan, IOC check, report
├── constants.py # Threat intel: IOC hashes, versions, references, skip dirs
├── scanners.py # Manifest/lock file parsers + find_and_scan walker
├── ioc_check.py # System IOC checks: RAT, C2, globals, launch persistence
└── report.py # HTML report generation: CSS, tables, badges, template
To run:
python3 /Users/guybary/Documents/skills/skills/security-axios/scripts/axios_scan.py
Module Reference
constants.py — Threat Intelligence Data
All IOC constants, SHA256 hashes, related campaign packages, skip directories, and reference URLs.
scanners.py — Manifest & Lock File Parsers
| Function | Purpose |
|---|
scan_package_json(path) | Scan for axios, plain-crypto-js, and related campaign packages in all dep sections |
scan_package_lock(path) | Scan npm lockfile v1/v2/v7+ packages tree, including related packages |
scan_yarn_lock(path) | Regex-based scan for compromised resolved versions |
scan_pnpm_lock(path) | Regex-based scan for pnpm lockfile v5/v6/v9 formats |
find_and_scan() | Walk ~/, merge findings per repo, track dropper evidence (plain_installed, setup_js_present) |
quick_hit(path, *needles) | Bytes-level grep pre-filter before full parse |
check_related_pkgs(all_deps) | Check dependency map for @shadanai/openclaw, @qqbrowser/openclaw-qbot |
ioc_check.py — System IOC Detection
| Function | Purpose |
|---|
check_iocs() | Returns (rat_found, rat_hash_match, rat_running, c2_active, global_crypto, launch_persist) |
sha256_file(path) | Compute SHA256 for IOC hash verification |
Checks performed:
- RAT binary existence + SHA256 verification against known hash
pgrep -f for running RAT process (FP-safe)
lsof -i for active C2 connections
- npm/yarn/pnpm global list for
plain-crypto-js
- LaunchAgents/LaunchDaemons scan for persistence references
report.py — HTML Report Generation
Generates dark-themed HTML report with:
- Threat intel section (SHA256 hashes, campaign path, advisory IDs)
- Project tables with dropper evidence and related packages columns
- Source badges (📦 package.json · 🔒 lockfile · 🧶 yarn · ⚡ pnpm)
- References & Advisories section with all 8 source links
- Remediation steps including version pinning and CI/CD
--ignore-scripts
- Footer with advisory links (GHSA, OSV, Snyk, Wiz, StepSecurity, Aikido)
Execution Instructions
When this skill is invoked:
-
Announce — scanning for the axios supply-chain attack (2026-03-31).
-
Run the scanner script directly — do NOT copy/paste inline:
python3 /Users/guybary/Documents/skills/skills/security-axios/scripts/axios_scan.py
-
What the script does:
- Walk
~/ skipping node_modules, Library, caches, etc.
- Use bytes-level grep pre-filter before full JSON/YAML parsing (fast)
- Scan
package.json, package-lock.json, yarn.lock, pnpm-lock.yaml per repo
- Check for related campaign packages (
@shadanai/openclaw, @qqbrowser/openclaw-qbot)
- Detect dropper anti-forensics (directory presence vs
setup.js presence)
- Check macOS IOC with
pgrep -f RAT_PATH (NOT ps aux | grep — avoids FP)
- SHA256 verify RAT binary against known hash
92ff0877...c9645a
- Check C2 connections via
lsof -i
- Check global npm/yarn/pnpm for
plain-crypto-js
- Scan LaunchAgents/LaunchDaemons for persistence referencing attack IOCs
- Generate dark-themed HTML with source badges (📦 🔒 🧶 ⚡) and all references
- Open in browser automatically
-
Print the terminal summary.
-
Verdict:
🚨 SYSTEM COMPROMISED — RAT binary / C2 active / launch persistence
⚠ VULNERABLE DEPS FOUND — compromised axios in lock files / package.json
✅ CLEAN — no compromised versions, no IOCs
-
List each compromised repo with path, version, which file(s) confirmed it, and related packages.
Constraints
- NEVER output credential values from any scanned file.
- NEVER modify any files. Read-only scan.
- Skip
node_modules/ when walking for manifests.
- FP rule: use
pgrep -f RAT_PATH — never ps aux | grep act.mond.
Success Criteria