axios 1.14.1 & 0.30.4 Are Malicious. Revert to 1.14.0 Now.
A compromised axios maintainer account published two poisoned npm versions that inject plain-crypto-js - a fake dependency that silently drops a cross-platform RAT targeting macOS, Windows, and Linux. Both versions are now unpublished. npm security has responded.
On March 31, 2026, StepSecurity and Socket identified two malicious releases of axios - the HTTP client with over 100 million weekly npm downloads - published using the compromised credentials of a lead maintainer. The attacker changed the account’s email to a ProtonMail address, bypassed the project’s GitHub Actions CI/CD pipeline, and manually pushed the poisoned packages via the npm CLI. Neither malicious version contains a single line of malicious code inside axios itself. Instead, they inject a hidden dependency - plain-crypto-js@4.2.1 - whose only purpose is to run a postinstall script that deploys a multi-stage, cross-platform Remote Access Trojan.
What Is axios - and Why This Is High-Stakes
axios is the most widely used HTTP client library in the JavaScript ecosystem. With over 100 million weekly downloads on npm and billions of total downloads, it sits as a dependency in virtually every Node.js backend, React and Vue frontend, and JavaScript automation script. It is the default HTTP library for tens of thousands of enterprise applications.
This is precisely what makes it such a high-value supply chain target. Compromising axios does not require targeting one organisation - it delivers malware into every environment that installs or updates the package. Any developer running npm install, any CI/CD pipeline executing a build, any Docker image rebuild that pulls a fresh npm install during the attack window becomes an unwitting dropper of the RAT payload.
💡 axios Dependency Footprint axios ships as a direct dependency of countless packages. Transitive exposure is the real scale - not just developers who directly depend on axios, but every project that depends on a package that depends on axios. A single npm install can pull in dozens of packages that themselves depend on axios. The safe versions to pin to are `axios@1.14.0` (for 1.x users) and `axios@0.30.3` (for legacy 0.x users).
The Attack Timeline - Pre-Staged and Precise
The attack was not improvised. The attacker pre-staged the malicious dependency on npm 18 hours before the axios releases, deliberately avoiding “brand-new package” alerts from security scanners that flag packages published within minutes of being depended on.
Mar 27 · 19:01 UTC
Legitimate axios@1.14.0 published via GitHub Actions OIDC
Clean release. Published by the official GitHub Actions CI pipeline with full SLSA provenance. Publisher identity: "GitHub Actions <npm-oidc-no-reply@github.com>"
Mar 30 · 05:57 UTC
plain-crypto-js@4.2.0 published by nrwise@proton.me - decoy version
A clean decoy package published to age the account and avoid new-package scanner alerts. Contains a full copy of a legitimate crypto library but no malicious code - purely to establish credibility on npm.
Mar 30 · 23:59 UTC
plain-crypto-js@4.2.1 published - the malicious payload version
The real weapon. Contains the postinstall RAT dropper. Socket’s automated malware detection flagged it at 00:05:41 UTC - just 6 minutes after publication. Attacker’s email: nrwise@proton.me.
Mar 31 · 00:21 UTC
Malicious axios@1.14.1 published - 22 minutes after malware dependency
Published by compromised maintainer account jasonsaayman. Email changed to ifstap@proton.me. No SLSA provenance. Manually published via npm CLI, bypassing GitHub Actions. Adds plain-crypto-js@4.2.1 as a new dependency never present in any prior axios version.
Mar 31 · 01:00 UTC
Malicious axios@0.30.4 published - 40 minutes after 1.14.1
Same compromised account. Targets the 0.x legacy branch. Same email change pattern. Same hidden plain-crypto-js dependency. Covers both major version branches simultaneously.
Mar 31 · ~hours later
StepSecurity and Socket identify the incident - npm security responds
Both malicious axios versions unpublished by npm (404). plain-crypto-js@4.2.0 and 4.2.1 remain live at time of writing. GitHub issue #10590 filed on axios/axios repo. Community incident response begins.
Account Compromise + SLSA Bypass
The attacker did not exploit a vulnerability in npm or axios’s code. They compromised a lead maintainer’s npm account credentials, changed the associated email to an anonymous ProtonMail address, and manually published the poisoned packages - bypassing the project’s security-hardened GitHub Actions CI pipeline entirely.
The SLSA Provenance Signal
Modern axios 1.x releases are published via npm’s OIDC Trusted Publisher mechanism - the publish is cryptographically tied to a verified GitHub Actions workflow, producing SLSA provenance that proves where and how the package was built. This is a critical forensic signal:

Any security tool that verifies npm provenance attestations would have immediately flagged axios@1.14.1 - legitimate 1.x releases always carry GitHub Actions OIDC provenance. Its absence is a definitive indicator of a compromised or out-of-band publish.
⚠ The Pre-Staging Tactic - 18 Hours of Patience The attacker published the clean decoy `plain-crypto-js@4.2.0` on March 30 at 05:57 UTC - 18 hours before publishing the malicious axios versions. This is deliberate evasion: many npm security scanners flag packages published within minutes of being added as a dependency. By waiting 18+ hours between the dependency seed and the axios publish, the attacker reduced the likelihood of automated "new suspicious dependency" alerts firing.
What plain-crypto-js Actually Does
The malicious plain-crypto-js@4.2.1 package is never imported anywhere in axios’s source code. Its sole purpose is to execute a postinstall script that drops a platform-specific second-stage Remote Access Trojan, contacts a live C2 server, and then self-destructs - replacing its own package.json with a clean decoy to erase forensic evidence.
Platform-Specific Stage 2 Payloads

Self-Destruction After Execution
After dropping the platform payload and contacting the C2, the malicious package replaces its own package.json with a clean stub. This means:
- A
npm listafter infection may not show anything obviously malicious - Post-incident forensic inspection of
node_modules/plain-crypto-js/may find only a clean-looking package - The RAT is already running as a persistent process before any inspection begins
- The only reliable detection is network IOCs (C2 traffic) or filesystem IOCs (dropped binaries)
C2 Communication
The dropper contacts sfrclak.com:8000 (IP: 142.11.206.73, ASN: AS54290 Hostwinds LLC) via POST requests to endpoint /6202033. Beacons run every 60 seconds carrying base64-encoded JSON. The POST body uses OS-specific npm registry URLs as a disguise: packages.npm.org/product0 (macOS), product1 (Windows), product2 (Linux).
json - malicious package.json (plain-crypto-js@4.2.1)
// plain-crypto-js@4.2.1 - the only real content is the postinstall hook
{
"name": "plain-crypto-js",
"version": "4.2.1",
"scripts": {
"postinstall": "node install.js" // ← runs on npm install, no user interaction
}
// install.js: detects OS, drops platform payload,
// contacts sfrclak.com:8000, then replaces this package.json
// with a clean stub to erase forensic evidence
}
Indicators of Compromise - Complete List

How to Detect If You Were Hit
bash - detection commands
# 1. Check if malicious versions are installed
npm list axios 2>/dev/null | grep -E "1\.14\.1|0\.30\.4"
# OR check lock file
grep -E "axios.*1\.14\.1|axios.*0\.30\.4" package-lock.json yarn.lock 2>/dev/null
# 2. Check if plain-crypto-js was ever installed
npm list plain-crypto-js 2>/dev/null
ls node_modules/plain-crypto-js 2>/dev/null
# 3. Linux - check for Python RAT
ls -la /tmp/ld.py 2>/dev/null && echo "INFECTED: RAT found"
# 4. macOS - check for native payload
ls -la /Library/Caches/com.apple.act.mond 2>/dev/null && echo "INFECTED"
# 5. Windows - check registry persistence
# reg query HKCU\Software\Microsoft\Windows\CurrentVersion\Run /v MicrosoftUpdate
# 6. Check network logs for C2 connections
grep -r "sfrclak.com\|142\.11\.206\.73\|/6202033" /var/log/ 2>/dev/null
# 7. Check npm audit (may flag if npm adds the advisory)
npm audit
# 8. Verify axios provenance - should show GitHub Actions publisher
npm view axios@1.14.0 _npmUser
# Legitimate: {"name":"npm-oidc-no-reply@github.com"...}
Who Is Affected

The Self-Destruction Makes Confirmation Harder The RAT dropper replaces its own package.json after execution. If you installed the malicious version, checking node_modules/plain-crypto-js/ now may show a clean-looking package - this does not mean you were not compromised. It means the payload already ran and erased its tracks. Check for filesystem IOCs and network logs, not just the package itself.
What to Do Right Now
- Run
npm list axiosin every project and environment. If it shows 1.14.1 or 0.30.4, treat the system as compromised. - If compromised: rotate all secrets, API keys, and credentials accessible on or from that machine. The RAT beacons every 60 seconds to the C2 - assume everything it could reach has been exfiltrated.
- Check for filesystem IOC simmediately:
/tmp/ld.py(Linux),/Library/Caches com.apple.act.mond(macOS),%PROGRAMDATA%\wt.exeandsystem.bat (Windows). Kill and delete any found. - Windows: check and delete the registry persistence key:
HKCU\Software\Microsoft\Windows\CurrentVersion\Run\MicrosoftUpdate. - Pin to safe versions:
axios@1.14.0(1.x) oraxios@0.30.3 (0.x). Add explicit lockfile commits. Runnpm installto regenerate clean lockfiles. - Block C2 at the firewall: add rules to deny all outbound traffic to
sfrclak.comand
142.11.206.73:8000. - Add
npm install --ignore-scriptsto all CI pipelines as a hardening measure. This would not have prevented the dependency install but blocks postinstall execution for future attacks. - Audit all Docker images built during the attack window. Rebuild from scratch with pinned, verified versions.
bash - pin to safe versions
# Downgrade to last known clean axios 1.x
npm install axios@1.14.0
# Downgrade legacy 0.x users
npm install axios@0.30.3
# Explicitly exclude both malicious versions forever
# Add to .npmrc or package.json overrides:
"overrides": {
"axios": "1.14.0"
}
# CI hardening - ignore postinstall scripts
npm install --ignore-scripts
# Verify no plain-crypto-js in your dependency tree
npm ls plain-crypto-js 2>/dev/null
# Should return: npm ls plain-crypto-js - (empty - not found)
# Block C2 on Linux
sudo iptables -A OUTPUT -d 142.11.206.73 -j DROP
echo "142.11.206.73 sfrclak.com" | sudo tee -a /etc/hosts
Runtime Detection
falco-axios-rat.yaml
- rule: axios RAT C2 Beacon
condition: |
evt.type = connect and fd.typechar = 4
and (fd.sip.name = "sfrclak.com" or
fd.sip = "142.11.206.73")
priority: CRITICAL
tags: [network, supply_chain, axios_rat]
- rule: axios RAT Linux Dropper
condition: |
evt.type in (open, openat, creat)
and fd.name = "/tmp/ld.py"
priority: CRITICAL
tags: [filesystem, supply_chain, axios_rat]
- rule: npm postinstall Spawns Suspicious Child
condition: |
proc.name = "node" and
proc.cmdline contains "postinstall" and
spawned_process.name in (curl, wget, python, python3)
priority: CRITICAL
tags: [process, supply_chain, npm_postinstall]
The Bigger Picture
The axios attack follows the same playbook we have seen across the TeamPCP campaign (Trivy, KICS, LiteLLM, telnyx) - but with a key difference: this is a direct maintainer account compromise, not a cascade from stolen CI credentials. The attacker targeted the maintainer account directly, changed the email to an anonymous ProtonMail address, bypassed OIDC-based provenance, and pre-staged the malicious dependency 18 hours early to beat scanner timing heuristics.
npm security responded quickly - both malicious axios versions are now unpublished. But the window of exposure, combined with axios’s 100M weekly download footprint, means the blast radius could be significant. Any npm install that resolved to these versions during the window executed the RAT dropper silently.
The forensic lesson: the absence of SLSA provenance on a package that previously always had it is the single most reliable detection signal. Legitimate axios 1.x releases are always published via GitHub Actions OIDC. The moment that provenance disappears, the package is suspect. Tools that verify npm provenance attestations would have caught this immediately.
Pin Your Deps. Verify Provenance. Run --ignore-scripts.
A lockfile with pinned versions would have stopped this entirely. npm provenance verification would have caught it before install. postinstall blocking would have contained it even if installed. Three controls. All available today.
Comments (0)
No comments yet. Be the first to share your thoughts.
Leave a comment