Request change

axios npm Supply Chain Attack - Malicious 1.14.1 & 0.30.4 Drop RAT via plain-crypto-js

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.

axios npm Supply Chain Attack - Malicious 1.14.1 & 0.30.4 Drop RAT via plain-crypto-js

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:
npm1.png

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
npm3.png

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 list after 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

npm4.png

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

npm5.png

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

  1. Run npm list axios in every project and environment. If it shows 1.14.1 or 0.30.4, treat the system as compromised.
  2. 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.
  3. Check for filesystem IOC simmediately: /tmp/ld.py (Linux), /Library/Caches com.apple.act.mond (macOS), %PROGRAMDATA%\wt.exe and system.bat (Windows). Kill and delete any found.
  4. Windows: check and delete the registry persistence key: HKCU\Software\Microsoft\Windows\CurrentVersion\Run\MicrosoftUpdate.
  5. Pin to safe versions:axios@1.14.0 (1.x) or axios@0.30.3 (0.x). Add explicit lockfile commits. Run npm install to regenerate clean lockfiles.
  6. Block C2 at the firewall: add rules to deny all outbound traffic to sfrclak.com and
    142.11.206.73:8000.
  7. Add npm install --ignore-scripts to all CI pipelines as a hardening measure. This would not have prevented the dependency install but blocks postinstall execution for future attacks.
  8. 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.

Share
Like this post?

Request a change or update

Suggest a correction or content update. The post author or an admin will be notified and can resolve or respond.

Comments (0)

No comments yet. Be the first to share your thoughts.

Leave a comment