All Posts
16 min read

Axios Supply Chain Attack Explained: npm's Most Popular HTTP Client Compromised with Cross-Platform RAT

On March 31, 2026, an attacker hijacked the lead axios maintainer's npm account and published two malicious versions — axios@1.14.1 and axios@0.30.4 — injecting a cross-platform remote access trojan via a fake dependency. Here is the full timeline, technical analysis, IOCs, and what to do if you are affected.

On March 31, 2026, an attacker published two malicious versions of axios — the most widely used HTTP client in the JavaScript ecosystem — to the npm registry. Versions 1.14.1 and 0.30.4 were poisoned with a hidden dependency that deploys a cross-platform remote access trojan. Axios has over 100 million weekly downloads on npm. Every project using a caret range (^1.14.0 or ^0.30.0) that ran npm install during the exposure window pulled in the compromised version automatically.

The attacker did not modify a single line of axios source code. Instead, they added plain-crypto-js@^4.2.1 as a runtime dependency — a package that had never appeared in any legitimate axios release and is never imported anywhere in the axios codebase. Its sole purpose is to run a postinstall hook that deploys a multi-stage RAT dropper. The dropper self-destructs after execution, meaning post-infection inspection of node_modules reveals nothing.

This is the most operationally sophisticated supply chain attack ever documented against a top-10 npm package. Socket flagged the malicious dependency within six minutes of publication. npm security has since removed all affected versions.

Full timeline

Time (UTC)Event
March 30, 05:57Clean decoy plain-crypto-js@4.2.0 published by nrwise@proton.me — a typosquat of the legitimate crypto-js library with no malicious payload
March 30, 23:59Malicious plain-crypto-js@4.2.1 published with obfuscated RAT dropper
March 31, 00:05Socket’s automated detection flags plain-crypto-js@4.2.1 as malware — six minutes after publication
March 31, 00:21axios@1.14.1 published via the compromised jasonsaayman npm account
March 31, 01:00axios@0.30.4 published via the same compromised account — 39 minutes after the 1.x release
March 31npm security removes all malicious versions and publishes security-holder stubs

The attacker poisoned both the 1.x and 0.x branches to maximize blast radius across projects pinned to either major version line.

How the maintainer account was hijacked

The root cause is credential compromise. The attacker gained access to the npm account of jasonsaayman, the lead axios maintainer, and changed the account’s registered email to ifstap@proton.me — an attacker-controlled ProtonMail address.

Both malicious versions were published manually via the npm CLI, completely bypassing the project’s normal release pipeline. Legitimate axios releases are published through GitHub Actions with OIDC Trusted Publisher binding — a mechanism that ties npm publishes to specific CI/CD workflows and prevents manual publishing. Neither compromised version has a corresponding commit, tag, or release in the axios GitHub repository. At the time of discovery, v1.14.0 was the most recent visible tag.

Evidence suggests the attacker also had access to the maintainer’s GitHub account. Early reports noted that GitHub issues documenting the compromise were being deleted, indicating the attacker was actively suppressing disclosure while the malicious versions remained live.

The compromise highlights a critical gap: even projects that adopt OIDC Trusted Publishing can remain vulnerable if long-lived npm access tokens are not revoked after transitioning to the new pipeline. Early remediation discussion confirmed that the axios project continued to use a long-lived npm token alongside trusted publishing.

Pre-staging the attack

The malicious dependency was not created on the fly. The attacker used a deliberate two-phase staging strategy designed to evade automated new-package alerts:

Phase 1 — Establish the package (March 30, 05:57 UTC). A separate account (nrwise, registered to nrwise@proton.me) published plain-crypto-js@4.2.0 to npm. This version was a clean copy of the legitimate crypto-js library with no malicious code. Its purpose was to age the package name, establish download history, and avoid triggering security scanners that flag brand-new packages with immediate downstream adoption.

Phase 2 — Deploy the payload (March 30, 23:59 UTC). Approximately 18 hours later, plain-crypto-js@4.2.1 was published with the full RAT dropper payload. By this point, the package name had an existing version history, making the update appear routine.

The 18-hour gap between the clean decoy and the malicious version is a deliberate operational security measure. Many package registry scanners apply heightened scrutiny to packages published for the first time. By separating the package creation from the payload delivery, the attacker reduced the chance of immediate detection.

What the malware does

The payload in plain-crypto-js@4.2.1 is a heavily obfuscated dropper that deploys a cross-platform remote access trojan. StepSecurity, Socket, and Aikido have published detailed technical analyses.

The obfuscation layer

The dropper uses a custom two-layer deobfuscation scheme to hide all operational strings — module names, the C2 URL, shell commands, and file paths:

  1. Layer 1: Base64 encoding with reversed characters and underscore substitution
  2. Layer 2: Per-character XOR cipher with a rotating key derived from the string OrDeR_7077

All 18 obfuscated strings in the stq[] array are hidden behind this encoding. At runtime, the dropper deobfuscates each string only when needed, then dynamically loads sensitive modules (fs, os, child_process.execSync) to avoid static analysis detection. As Socket noted, this is “not minification or legitimate IP protection; it is purpose-built to hide malware indicators from scanners.”

The postinstall hook

The malware executes via npm’s postinstall lifecycle script. The moment npm install processes the plain-crypto-js dependency, the dropper runs automatically — no explicit require() or import needed. The script:

  1. Detects the host operating system
  2. Contacts the C2 server at sfrclak[.]com:8000 (IP: 142.11.206.73)
  3. Downloads a platform-specific second-stage payload
  4. Deploys and executes the RAT
  5. Erases its own artifacts and replaces its package.json with a clean version

macOS payload

On macOS, the dropper uses AppleScript to fetch a compiled C++ RAT binary from the C2 server. The binary is saved to:

/Library/Caches/com.apple.act.mond

This path deliberately mimics Apple’s system daemon naming convention (com.apple.*), making it difficult to distinguish from legitimate system processes in Activity Monitor or ps output. The binary:

  • Generates a 16-character unique victim ID
  • Fingerprints the system: hostname, username, OS version, timezone, CPU type, running processes, directory listings
  • Beacons to the C2 server via HTTP POST every 60 seconds
  • Supports remote commands: execute shell commands, run additional payloads, enumerate the file system, and self-terminate
  • Uses a fake Internet Explorer 8 on Windows XP User-Agent string for C2 communication

Windows payload

On Windows, the dropper:

  1. Copies the PowerShell binary to %PROGRAMDATA%\wt.exe — disguising it as Windows Terminal
  2. Writes a VBScript to the temp directory that launches PowerShell with execution policy bypass
  3. Downloads a PowerShell RAT script from the C2 server
  4. Executes the RAT hidden from the user

The execution policy bypass (-ExecutionPolicy Bypass) is a standard technique for running unsigned scripts on systems with restricted PowerShell policies.

Linux payload

On Linux, the dropper:

  1. Downloads a Python RAT script from the C2 server
  2. Saves it to /tmp/ld.py
  3. Executes it in the background using nohup to survive terminal closure

All three platform payloads communicate with the same C2 infrastructure at sfrclak[.]com:8000.

Anti-forensics

The dropper replaces its own package.json after execution and deletes or renames payload artifacts. If you installed the malicious version and later inspect node_modules/plain-crypto-js/, you may see a clean-looking package. This does not mean you were not compromised — it means the payload already ran and erased its tracks. Check your lockfiles, not your disk.

Additional affected packages

Socket identified two additional npm packages distributing the same malware payload through vendored dependencies:

@shadanai/openclaw — Versions 2026.3.28-2, 2026.3.28-3, 2026.3.31-1, and 2026.3.31-2 vendor the malicious plain-crypto-js payload directly in a deep vendored path within the package. The malware is not listed as a dependency — it is embedded in the package’s file tree, bypassing dependency-level scanning.

@qqbrowser/openclaw-qbot — Version 0.0.130 ships a tampered copy of axios@1.14.1 pre-installed in its own node_modules/ folder, with plain-crypto-js injected as a dependency. As Socket noted, “the real axios has only three dependencies (follow-redirects, form-data, proxy-from-env)” — the presence of plain-crypto-js is the immediate indicator of tampering.

Both packages use the same C2 server (sfrclak[.]com:8000), the same obfuscation scheme, and the same platform-specific RAT payloads. Their existence suggests the attacker was distributing the malware through multiple vectors simultaneously to maximize reach.

PackageMalicious versionsMethod
axios1.14.1, 0.30.4Dependency injection (plain-crypto-js)
plain-crypto-js4.2.1Direct payload (postinstall hook)
@shadanai/openclaw2026.3.28-2, 2026.3.28-3, 2026.3.31-1, 2026.3.31-2Vendored payload in file tree
@qqbrowser/openclaw-qbot0.0.130Ships tampered axios with injected dependency

Attribution

At the time of writing, no security vendor has attributed this attack to a known threat group. Socket has confirmed they have “not observed any evidence linking this activity to the recently reported TeamPCP campaigns” that compromised Trivy, LiteLLM, and Telnyx earlier this month.

However, the operational profile differs from TeamPCP in several important ways:

  • No credential harvesting focus. TeamPCP’s payloads were designed to steal CI/CD credentials and cascade to new targets. The axios RAT is designed for persistent remote access and system control.
  • No self-identification. TeamPCP publicly claimed responsibility by renaming repositories and maintaining social media accounts. The axios attacker left no calling card.
  • No cryptocurrency miners or ransomware. The absence of financially motivated payloads, combined with extensive system reconnaissance (.ssh and .aws directory enumeration, process monitoring), led Open Source Malware to suggest the attacker may be an advanced persistent threat (APT) actor conducting intelligence gathering.

The use of two separate ProtonMail addresses (ifstap@proton.me for the hijacked maintainer account, nrwise@proton.me for the staging account) and the deliberate 18-hour staging gap indicate an operationally disciplined actor.

Am I affected?

Step 1: Check your lockfiles

The most reliable indicator is your package-lock.json, yarn.lock, or pnpm-lock.yaml. Search for the compromised versions:

# Check for compromised axios versions
grep -r "axios" package-lock.json | grep -E '"(1\.14\.1|0\.30\.4)"'

# Check for the malicious dependency
grep -r "plain-crypto-js" package-lock.json

# Check for the additional affected packages
grep -rE "(@shadanai/openclaw|@qqbrowser/openclaw-qbot)" package-lock.json

If any of these return results, you are affected.

Step 2: Check for persistence artifacts

macOS:

# Check for the RAT binary
ls -la /Library/Caches/com.apple.act.mond 2>/dev/null

# Check for active connections to the C2
lsof -i -n | grep "142.11.206.73"

Windows:

# Check for the disguised PowerShell copy
Test-Path "$env:PROGRAMDATA\wt.exe"

# Check for active connections to the C2
netstat -an | findstr "142.11.206.73"

Linux:

# Check for the Python RAT
ls -la /tmp/ld.py 2>/dev/null

# Check for active connections to the C2
ss -tnp | grep "142.11.206.73"

All platforms:

# Check for the staging directory
ls -la "$TMPDIR/6202033" 2>/dev/null

Step 3: Check network logs

Search firewall logs, DNS query logs, and proxy logs for any connections to:

Domain: sfrclak[.]com
IP:     142.11.206.73
Port:   8000

Any outbound connection to this infrastructure after March 31, 2026 00:21 UTC is an indicator of compromise.

What should you do right now

If you installed axios@1.14.1 or axios@0.30.4, treat the environment as fully compromised.

  1. Downgrade immediately. Roll back to the last known safe versions: axios@1.14.0 (for 1.x users) or axios@0.30.3 (for 0.x users). Remove plain-crypto-js from node_modules if present.

  2. Rotate all credentials. Every secret accessible to the compromised environment should be considered exposed: API keys, database passwords, cloud provider credentials (AWS, GCP, Azure), SSH keys, npm/PyPI tokens, .env variables, and any application secrets.

  3. Block the C2 infrastructure. Add firewall rules to block outbound traffic to sfrclak[.]com and 142.11.206.73:

# iptables
iptables -A OUTPUT -d 142.11.206.73 -j DROP

# ufw
ufw deny out to 142.11.206.73

# macOS pf
echo "block drop out quick on en0 to 142.11.206.73" | sudo pfctl -ef -
  1. Audit CI/CD pipelines. Check build logs for any npm install runs after March 31, 2026 00:21 UTC that may have pulled the compromised versions. CI runners are high-value targets because they typically hold publishing tokens and deployment credentials.

  2. Remove persistence artifacts. Delete the RAT binaries and scripts identified in the IOC section. On macOS, also check for launch agents or daemons referencing com.apple.act.mond.

  3. Consider full system rebuilds. For production infrastructure and CI/CD runners, rebuilding from clean images is the safest remediation. The RAT’s anti-forensic capabilities mean you cannot be certain of the system’s integrity through file inspection alone.

Complete IOC reference

Malicious package versions

PackageMalicious versionsSafe version
axios1.14.1, 0.30.41.14.0 / 0.30.3
plain-crypto-js4.2.1None (entire package is malicious)
@shadanai/openclaw2026.3.28-2, 2026.3.28-3, 2026.3.31-1, 2026.3.31-2None
@qqbrowser/openclaw-qbot0.0.130None

Network indicators

IndicatorContext
sfrclak[.]com:8000C2 server domain
142.11.206.73:8000C2 server IP address
HTTP POST to C2 every 60 secondsRAT beaconing interval
User-Agent: Internet Explorer 8 on Windows XPFake UA string in C2 communication

File system indicators

PathPlatformDescription
/Library/Caches/com.apple.act.mondmacOSRAT binary disguised as Apple system daemon
%PROGRAMDATA%\wt.exeWindowsPowerShell copy disguised as Windows Terminal
/tmp/ld.pyLinuxPython RAT script
$TMPDIR/6202033AllStaging directory

Attacker accounts

AccountPlatformEmail
jasonsaayman (hijacked)npmChanged to ifstap@proton.me
nrwisenpmnrwise@proton.me

Obfuscation indicators

IndicatorDescription
OrDeR_7077XOR key used in the two-layer deobfuscation scheme
stq[] array with 18 entriesObfuscated string table in the dropper

Lessons for every JavaScript project

Pin your dependencies

The single most effective defense against this class of attack is version pinning. Projects using "axios": "^1.14.0" in their package.json automatically pulled in 1.14.1 on the next install. Projects using "axios": "1.14.0" (exact version) were not affected.

Use npm ci instead of npm install in CI/CD pipelines. npm ci installs from the lockfile exactly, without resolving newer versions. Combined with a reviewed lockfile, this eliminates automatic version drift.

Disable postinstall scripts in CI

The malware executes via npm’s postinstall lifecycle hook. Running npm install --ignore-scripts in CI environments prevents postinstall scripts from executing. This is a blunt instrument — it will also disable legitimate postinstall scripts — but it eliminates this entire class of attack vector.

For more granular control, use tools like Socket or npm audit signatures to validate packages before allowing script execution.

Require OIDC Trusted Publishing — and revoke legacy tokens

The axios project had adopted OIDC Trusted Publishing via GitHub Actions, which ties npm publishes to specific CI/CD workflows. But the attacker bypassed this entirely by using a legacy npm access token that had not been revoked. Adopting trusted publishing is only effective if you also revoke all legacy access tokens. A single surviving long-lived token negates the entire security model.

Monitor for phantom versions

Neither axios@1.14.1 nor axios@0.30.4 had a corresponding GitHub release, tag, or commit. This is a strong signal of compromise. Monitoring tools should flag any npm publish that cannot be traced back to a CI/CD workflow or source control event.

Audit dependency additions

The malicious versions added a single new dependency: plain-crypto-js. Legitimate axios has only three dependencies: follow-redirects, form-data, and proxy-from-env. Any tool that diffs dependency changes between versions would have flagged this immediately. Services like Socket, Snyk, and Aikido provide this capability.

The broader context

This is the fourth major npm supply chain incident in March 2026 alone, following the Trivy compromise (March 19), the CanisterWorm npm worm (March 20), and the Telnyx PyPI compromise (March 27). While the axios attack has not been linked to the TeamPCP campaign behind those incidents, the pattern is unmistakable: attackers are systematically targeting the most trusted, most depended-on packages in every ecosystem.

The npm ecosystem is particularly vulnerable because of the postinstall script mechanism, which grants arbitrary code execution at install time by default, and because of the prevalence of caret ranges (^) that automatically adopt new minor and patch versions.

As Open Source Malware stated: “This will go down in history as one of the most successful software supply chain attacks ever.”

FAQ

What versions of axios are affected?

axios@1.14.1 and axios@0.30.4 are the only compromised versions. All other versions are safe. Both malicious versions have been removed from npm.

Does npm install alone trigger the malware?

Yes. Unlike some supply chain attacks where malware only runs at import time, the axios payload executes via a postinstall hook during npm install. The moment the package manager processes plain-crypto-js, the dropper runs automatically.

I use axios but have version pinning. Am I safe?

If your package.json specifies an exact version (e.g., "axios": "1.14.0") and your lockfile reflects that version, you were not affected. If you use a caret range (e.g., "axios": "^1.14.0"), check your lockfile — you may have pulled in 1.14.1. Run npm ls axios to verify.

The malware self-destructs — how do I know if I was compromised?

Check your lockfile (package-lock.json, yarn.lock, or pnpm-lock.yaml) for references to axios@1.14.1, axios@0.30.4, or plain-crypto-js. Check for persistence artifacts on disk (see IOC section). Check network logs for connections to sfrclak[.]com or 142.11.206.73.

No confirmed connection. Socket has stated they have not observed evidence linking this attack to TeamPCP. The operational profile — persistent RAT vs. credential harvester, no public attribution, APT-like reconnaissance — differs significantly from the TeamPCP campaign.

How do I protect against future supply chain attacks like this?

  1. Pin all dependency versions — use exact versions, not caret or tilde ranges
  2. Use npm ci in CI/CD pipelines to install from the lockfile exactly
  3. Run npm install --ignore-scripts where possible to block postinstall hooks
  4. Revoke legacy npm tokens after adopting OIDC Trusted Publishing
  5. Monitor dependency changes between versions with tools like Socket, Snyk, or Aikido
  6. Flag phantom publishes — npm versions with no corresponding source control event
  7. Scope CI/CD secrets — minimize the credentials available to build pipelines

Further reading


This article will be updated as new information becomes available. Last updated March 31, 2026.

Ready to try autonomous pentesting?

See how Revaizor can transform your security testing.

Request Early Access