Axios Hijacked on npm — A RAT Disguised as Your Favorite HTTP Client
On March 31, 2026, attackers compromised the axios maintainer's npm account and published two malicious versions that dropped a cross-platform remote access trojan via a fake dependency. Here's how the attack worked, how to check if you're affected, and what to do about it.
Table of Contents
What Happened
On March 31, 2026, two malicious versions of axios — the most popular JavaScript HTTP client with over 100 million weekly downloads — were published to npm. The attacker compromised the primary maintainer’s npm account and used it to inject a fake dependency that dropped a cross-platform remote access trojan (RAT) on every machine that ran npm install.
The compromised versions were live for approximately 2.5 to 4.5 hours before npm pulled them. In the npm ecosystem, that’s enough time for significant damage.
This article is based on StepSecurity’s detailed analysis of the incident.
How the Attack Works
Account Takeover
The attacker compromised the jasonsaayman npm account — the primary axios maintainer. The registered email was changed to an attacker-controlled ProtonMail address ([email protected]). This was possible because the account used a long-lived npm access token rather than the more secure OIDC Trusted Publisher mechanism. One stolen token was all it took.
The 18-Hour Pre-Staging
This wasn’t a rushed smash-and-grab. The attacker staged the attack over 18 hours:
| UTC Timestamp | Event |
|---|---|
| Mar 30, 05:57 | [email protected] published by [email protected] — a clean decoy to establish the package |
| Mar 30, 23:59 | [email protected] published — malicious postinstall hook added |
| Mar 31, 00:21 | [email protected] published — injects plain-crypto-js@^4.2.1 as a runtime dependency |
| Mar 31, 01:00 | [email protected] published — same payload, targeting the 0.x branch |
| Mar 31, ~03:15 | npm unpublishes both axios versions |
| Mar 31, 03:25 | npm places security hold on plain-crypto-js |
The key insight: plain-crypto-js is never imported anywhere in the axios source code. Its sole purpose is to execute a postinstall script. A dependency that appears in the manifest but has zero usage in the codebase is a high-confidence indicator of a compromised release.
The RAT Dropper
The postinstall hook runs setup.js — a 4.2 KB obfuscated file using two layers of encoding: an XOR cipher (key: OrDeR_7077) and base64. It fires within ~2 seconds of npm install, makes C2 contact before npm finishes resolving dependencies, then self-destructs — deleting setup.js and replacing the malicious package.json with a clean stub reporting version 4.2.0 (not 4.2.1) to evade forensic inspection.
Platform-Specific Payloads
The dropper detects the OS and delivers a tailored payload:
| Platform | RAT Location | Execution Method |
|---|---|---|
| macOS | /Library/Caches/com.apple.act.mond | AppleScript via /bin/zsh |
| Windows | %PROGRAMDATA%\wt.exe (PowerShell copy disguised as Windows Terminal) | VBScript dropper → hidden PowerShell stage-2 |
| Linux | /tmp/ld.py | nohup python3 in background |
The C2 server sits at sfrclak.com:8000 (IP: 142.11.206.73). POST bodies are crafted to look like benign npm registry traffic — packages.npm.org/product0 for macOS, product1 for Windows, product2 for Linux.
How Do I Know If I’m Affected
Check Your Installed Versions
The compromised versions are [email protected] and [email protected]. Safe versions are 1.14.0 and 0.30.3.
# Check if you have a compromised version installed
npm list axios 2>/dev/null | grep -E "1\.14\.1|0\.30\.4"
# Check your lockfile
grep -A1 '"axios"' package-lock.json | grep -E "1\.14\.1|0\.30\.4"
Check for the Malicious Dependency
This package should never exist in any legitimate axios installation:
# If this directory exists, you were compromised
ls node_modules/plain-crypto-js 2>/dev/null && echo "POTENTIALLY AFFECTED"
Check for RAT Artifacts on Your Machine
# macOS
ls -la /Library/Caches/com.apple.act.mond 2>/dev/null && echo "COMPROMISED"
# Linux
ls -la /tmp/ld.py 2>/dev/null && echo "COMPROMISED"
# Windows (PowerShell)
if (Test-Path "$env:PROGRAMDATA\wt.exe") { Write-Host "COMPROMISED" }
Check Network Logs
Look for connections to the C2 infrastructure:
# Check active connections
ss -tunap | grep -E "sfrclak\.com|142\.11\.206\.73"
# Check DNS logs if available
grep -r "sfrclak.com" /var/log/
Important caveat: The malware self-destructs after execution. If it ran successfully,
npm listwill show[email protected](the clean stub), not4.2.1. The absence of obvious artifacts does not guarantee you weren’t compromised — the RAT may still be running.
Mitigation Steps
1. Downgrade Immediately
# For 1.x users
npm install [email protected]
# For 0.x users
npm install [email protected]
2. Pin Your Version with Overrides
Add these to your package.json to prevent transitive dependencies from pulling a bad version:
{
"dependencies": { "axios": "1.14.0" },
"overrides": { "axios": "1.14.0" },
"resolutions": { "axios": "1.14.0" }
}
3. Remove the Malicious Package
rm -rf node_modules/plain-crypto-js
npm install --ignore-scripts
4. If RAT Artifacts Were Found — Nuke from Orbit
Do not attempt in-place cleanup. If any RAT artifact was present on your machine:
- Rebuild the system from a known-good state — reimage, redeploy from clean base
- Rotate ALL credentials that were accessible from the compromised machine:
- npm tokens
- AWS/GCP/Azure access keys
- SSH private keys
- CI/CD secrets and environment variables
- Everything in
.envfiles
- Audit CI/CD pipeline logs for any
npm installthat ran during the compromise window (March 31, 00:21–03:15 UTC)
5. Block the C2 at the Network Level
# Linux firewall
iptables -A OUTPUT -d 142.11.206.73 -j DROP
# Hosts file (macOS/Linux)
echo "0.0.0.0 sfrclak.com" >> /etc/hosts
6. Harden Your CI/CD Going Forward
# Use --ignore-scripts to prevent postinstall hooks
npm ci --ignore-scripts
This blocks the primary attack vector — postinstall scripts — at the cost of breaking packages that legitimately need build steps. For production CI/CD, the trade-off is almost always worth it.
Key Takeaways
-
A single compromised npm token gave attackers publishing access to a 100M weekly-download package. Long-lived access tokens are the weakest link. Use OIDC Trusted Publishers or short-lived tokens wherever possible.
-
The attack was precision-engineered, not opportunistic. An 18-hour pre-staging phase, cross-platform RAT payloads, anti-forensic self-destruction, and C2 traffic disguised as npm registry requests. This is professional-grade work.
-
postinstallscripts are a dangerous attack surface. They execute arbitrary code at install time with zero user confirmation. Use--ignore-scriptsin CI/CD and auditpostinstallhooks in your dependency tree. -
Phantom dependencies are a red flag. A dependency that appears in the manifest but is never imported in the source code is a high-confidence indicator of compromise. Tools that diff manifest vs actual imports can catch this automatically.
-
The 2.5-hour window was enough. Even with npm’s relatively fast response, the compromise window was long enough to affect builds worldwide. Real-time monitoring of package registries — like StepSecurity’s Harden-Runner — is becoming essential infrastructure.
Future Discussion
The npm Token Problem Is Systemic
This attack exploited a long-lived npm access token. npm supports OIDC Trusted Publishers — a mechanism where publishing happens through verified CI/CD pipelines rather than personal tokens — but adoption is low. Until the ecosystem enforces short-lived, scope-limited tokens by default, every maintainer’s personal machine is a single point of failure for the entire dependency tree.
postinstall Needs Rethinking
The npm postinstall hook has legitimate uses (native module compilation, binary downloads), but it’s also the single most abused vector in supply chain attacks. The community has debated sandboxing or requiring explicit opt-in for install scripts for years. This incident will likely accelerate that conversation. In the meantime, --ignore-scripts is the pragmatic default for CI/CD environments.
Dependency Provenance Is the Missing Layer
Package registries currently verify that a publish came from an authorized account — but they don’t verify that the published content matches any source repository. If npm had required that [email protected] match a tagged commit in the axios GitHub repository, this attack would have failed. Sigstore and npm’s provenance attestations are steps in the right direction, but adoption remains low.
AI-Powered Detection Is Becoming Necessary
StepSecurity detected this compromise using automated analysis that flagged plain-crypto-js as anomalous — a new dependency with no corresponding import in the codebase. As supply chain attacks become more sophisticated, manual code review at install-time isn’t feasible for a dependency tree with hundreds of packages. Automated, AI-assisted monitoring of package behavior is shifting from “nice to have” to critical infrastructure.
References
- StepSecurity: Axios Compromised on npm — Malicious Versions Drop Remote Access Trojan
- GitHub Issue #10604 — axios
- Malicious package shasums:
[email protected](2553649f232204966871cea80a5d0d6adc700ca),[email protected](d6f3f62fd3b9f5432f5782b62d8cfd5247d5ee71) - Dropper SHA-256:
e10b1fa84f1d6481625f741b69892780140d4e0e7769e7491e5f4d894c2e0e09
Related Articles
- Anatomy of the LiteLLM Supply Chain Attack — How a Security Scanner Became the Weapon
- When the Malware Calls the Cloud — A Case Study in AI-Driven Cyberattacks
- Bleeding Llama — How a Single GGUF File Can Bleed Your Ollama Server Dry
- Claude Code vs OpenClaw vs NemoClaw — Which AI Agent Fits Your Workflow?
- Parallelization: Concurrent Execution for Agentic Workflows