On March 31, 2026, malicious versions of axios — a package with 70M+ weekly downloads — were published to npm after the maintainer's account was hijacked via social engineering. Versions 1.14.1 and 0.30.4 were pushed back-to-back, both carrying a plain-crypto-js@^4.2.1 dependency that deploys a cross-platform RAT through a postinstall hook.
The malicious releases sat on the registry for roughly 3 hours. In that window, an estimated 600,000 installs occurred.
If you use axios, check your lock file. Now.
TL;DR
- Malicious:
axios@1.14.1,axios@0.30.4- Safe:
axios@1.14.0,axios@0.30.3(pre-incident),1.15.0+/0.30.5+(post-incident)- Attribution: North Korea — Sapphire Sleet (Microsoft) / UNC1069 (Google)
- Action: wipe
node_modules, reinstall, rotate all credentials
How to Check Right Now
# Installed axios version
npm list axios
# Check lock file for malicious versions
grep -E "axios@(1\.14\.1|0\.30\.4)|plain-crypto-js" package-lock.json
# Monorepo-wide scan
find . -name "package-lock.json" -not -path "*/node_modules/*" \
| xargs grep -l "plain-crypto-js\|1.14.1\|0.30.4"
If grep returns a match, remediate immediately. No output means you're probably fine — but also check git history. If the malicious version was ever installed in the past, the postinstall hook has already run.
# Did the malicious version ever land in lock file history?
git log -p -- package-lock.json | grep -E "1\.14\.1|0\.30\.4|plain-crypto-js"
With pnpm, use pnpm list axios; with yarn, yarn list --pattern axios. The lock-file grep pattern applies regardless of package manager.
The 3-Hour Timeline
Independent reconstructions from Aikido Security, Arctic Wolf, and Elastic Security Labs largely agree:
| Time (UTC) | Event |
|---|---|
| 2026-03-31 00:21 |
axios@1.14.1 published — targets 1.x line, adds plain-crypto-js@^4.2.1
|
| +39 min | Attacker stages the 0.x legacy release |
| 01:00 |
axios@0.30.4 published — 0.x branch compromised |
| ~03:00 | Socket.dev / Aikido detect anomalous postinstall hook, community alerts begin |
| ~04:00 | npm force-unpublishes both versions, exposure totals ~3 hours |
"Only 3 hours" is a dangerous framing. Vercel, GitHub Actions, CircleCI, and similar CI environments pull fresh versions on cache misses every 10~30 seconds. Globally, tens of thousands of builds ran in that window. Several regions also reported CDN cache serving the malicious version briefly after the unpublish.
How the Malicious Code Works
plain-crypto-js disguises itself as a crypto utility. It is never imported anywhere in axios source — it exists solely to execute its postinstall hook.
During install, npm runs postinstall automatically. That hook contacts the attacker's C2 server and pulls a second-stage payload. The payload detects the host OS (macOS / Windows / Linux) and drops a matching RAT (Remote Access Trojan).
Per Elastic Security Labs, the C2 protocol rides on HTTPS with a custom command set designed to blend into normal API traffic, making network-level detection difficult.
Attack Vector — Maintainer Account Hijack
Per SANS Institute and The Hacker News, the axios maintainer account was hijacked through a targeted social engineering campaign. The attacker changed the account email to ifstap@proton.me, then abused publish permissions to push the two malicious releases.
Attribution
- Microsoft Threat Intelligence: Sapphire Sleet — North Korea state actor
- Google GTIG: UNC1069 — same actor, tracked independently
- Joint attribution confirmed
UNC1069 / Sapphire Sleet has a track record of targeting developers through:
- Fake job offers with malicious coding-test files
- Fake recruiter outreach via LinkedIn or Telegram
- Phishing open-source maintainers directly
This axios case appears to fall into the third pattern.
Remediation
Don't just upgrade — wipe and rebuild.
# 1. Wipe node_modules + lock file
rm -rf node_modules package-lock.json
# 2. Clean cache
npm cache clean --force
# 3. Reinstall latest safe version
npm install axios@latest
# 4. Verify
grep "plain-crypto-js" package-lock.json
# → No output = clean
Apply the same to deployment environments (Vercel / Netlify / GitHub Actions caches). A stale cache can still serve the compromised artifact.
Rotate All Credentials — Not Just Env Vars
If a malicious version ever reached your machines, the RAT may still be resident. The attacker has system-level access, not just process.env.
Rotation checklist:
- [ ] AWS / GCP / Azure access keys
- [ ] AI API keys — OpenAI / Anthropic / Gemini
- [ ] Database passwords — PostgreSQL, MySQL, MongoDB
- [ ] Payment API keys — Stripe, LemonSqueezy, Paddle
- [ ] GitHub Personal Access Token + SSH keys
- [ ] App secrets —
NEXTAUTH_SECRET,SESSION_SECRET - [ ] Webhook secrets for external services
- [ ] Infected-machine SSH public keys — remove from
~/.ssh/authorized_keyson any servers they reached
Revoke old keys immediately after issuing new ones. Keeping the old key alive defeats the rotation.
For machines with high suspicion of compromise, an OS reinstall is the safest option. CI runner images should be rebuilt clean. Local dev machines should at minimum clear browser sessions, SSH keys, and saved AWS CLI profiles, then reconfigure.
Prevention Routines
1. Commit lock files. Without a lock file, every build can pull a different version. If package-lock.json is in .gitignore, remove it now.
2. Put npm audit in CI. Run it on every PR. npm audit --audit-level=high catches high-severity issues at minimum. Caveat: audit only sees what's public in the CVE database.
3. Tighten version range specifiers.
// ❌ Too loose — opens the door to auto-updates
"axios": "^1.13.0"
// ✅ Exact pin
"axios": "1.14.0"
// ✅ Patch-only
"axios": "~1.14.0"
4. Monitor beyond CVE.
| Tool | Strength | Note |
|---|---|---|
| Dependabot | Built into GitHub | CVE-based, limited against fresh attacks |
| Socket.dev | Behavioral analysis | Flagged this axios incident early |
| Aikido Security | Real-time behavioral | Published first public analysis |
| Snyk | Scan + remediation | Free tier available |
| npm audit | Built-in | CVE-based limits |
Realistic combo: Dependabot + Socket.dev. Single-tool reliance leaves blind spots.
Why This Keeps Happening
The npm ecosystem has a low publishing bar. A single account compromise can poison a package used by hundreds of millions of developers. That structural fact isn't changing fast.
- XZ Utils (2024-03) — compromised Linux distribution backdoor
- event-stream (2018) — crypto wallet stealer hidden in dependency
- ua-parser-js (2021) — malicious versions with credential stealer
- axios (2026-03) — this incident
axios isn't the first and won't be the last.
Following this incident, npm is reportedly considering mandatory 2FA expansion and a 24-hour cooldown on maintainer email changes. GitHub already required 2FA for top npm maintainers since 2024, but this hijack went through the email recovery flow. Security chains only hold as strong as the weakest link.
Key Takeaways
- Check your lock file right now — don't assume you're fine.
- Wipe, don't just upgrade — stale caches and remnant RATs are real risks.
- Rotate credentials broadly — system-level access means everything is suspect.
- Put behavioral analysis in your CI — CVE-based tools can't catch fresh attacks.
- Pin exact versions for critical packages — range specifiers are attack surface.
Trusting a popular package and verifying it are different things. If you use axios, put a version check in your routine starting today.
Sources:
- axios Official Post-Mortem (GitHub #10636)
- Microsoft Security Blog — Mitigating the Axios npm supply chain compromise
- Google Cloud Threat Intelligence — UNC1069 analysis
- Aikido Security — first public analysis
- Elastic Security Labs — RAT technical analysis
- Socket.dev — plain-crypto-js analysis
- Snyk Security Blog
Originally published on GoCodeLab — April 2026.


Top comments (0)