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 TimestampEvent
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:15npm unpublishes both axios versions
Mar 31, 03:25npm 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:

PlatformRAT LocationExecution Method
macOS/Library/Caches/com.apple.act.mondAppleScript via /bin/zsh
Windows%PROGRAMDATA%\wt.exe (PowerShell copy disguised as Windows Terminal)VBScript dropper → hidden PowerShell stage-2
Linux/tmp/ld.pynohup 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 list will show [email protected] (the clean stub), not 4.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:

  1. Rebuild the system from a known-good state — reimage, redeploy from clean base
  2. 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 .env files
  3. Audit CI/CD pipeline logs for any npm install that 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

  1. 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.

  2. 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.

  3. postinstall scripts are a dangerous attack surface. They execute arbitrary code at install time with zero user confirmation. Use --ignore-scripts in CI/CD and audit postinstall hooks in your dependency tree.

  4. 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.

  5. 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