Blog

The Axios npm Supply Chain Attack: A Complete Technical Analysis of the Maintainer Hijack, Cross-Platform RAT, and Enterprise Impact

Written by Kevin McGahey | March 31, 2026

Executive Summary

On March 31, 2026, an attacker hijacked the npm account of Axios’s primary maintainer and published two malicious versions of the most popular HTTP client library in the JavaScript ecosystem. The backdoored packages—axios@1.14.1 and axios@0.30.4—injected a trojanized dependency that delivered cross-platform remote access trojans to macOS, Windows, and Linux machines within seconds of installation. With approximately 100 million weekly downloads and a presence in 80% of cloud environments, Axios represents one of the highest-value targets in the npm registry. The malicious versions were live for approximately 2.5–3 hours, with observed execution in 3% of affected environments. Security researchers have characterized the operation as espionage-grade APT activity. This article provides a complete technical breakdown of the attack chain, the malware mechanics, and the remediation path.

Attack Timeline

The attack was meticulously staged over approximately 22 hours, demonstrating significant operational planning:

Timestamp (UTC) Event Significance
March 30, 05:57 plain-crypto-js@4.2.0 published by nrwise@proton.me Clean decoy establishing npm publishing history; 56 source files bit-for-bit identical to legitimate crypto-js@4.2.0
March 30, 23:59 plain-crypto-js@4.2.1 published with malicious postinstall hook Three file-level differences from 4.2.0: added postinstall script to package.json, added setup.js dropper (4.2 KB), added clean package.md stub
March 31, 00:21 axios@1.14.1 published via compromised jasonsaayman account Published manually (no trusted publisher, no gitHead, no corresponding GitHub tag); email changed to ifstap@proton.me
March 31, 01:00 axios@0.30.4 published Second release branch poisoned 39 minutes later, ensuring coverage of both 0.x and 1.x dependency ranges
March 31, ~03:15 npm unpublishes both malicious Axios versions Approximately 2 hours 53 minutes (1.14.1) and 2 hours 15 minutes (0.30.4) of exposure
March 31, 03:25 npm initiates security hold on plain-crypto-js Malicious dependency quarantined
March 31, 04:26 npm publishes security-holder stub for plain-crypto-js Prevents reinstallation from cached registries

Key operational detail: The 18-hour gap between publishing the clean decoy (plain-crypto-js@4.2.0) and the malicious version (4.2.1) was deliberate. This established a non-zero publishing history, avoiding automated alerts that flag packages with no prior versions. The attacker also pre-built three platform-specific RAT payloads—macOS, Windows, Linux—before the attack began.

How the Maintainer Account Was Compromised

The attacker gained control of the jasonsaayman npm account—Axios’s lead maintainer, who holds the highest permissions for both the npm package and the GitHub repository. The account’s registered email was changed to ifstap@proton.me, an attacker-controlled address, effectively locking out the legitimate owner from password recovery.

The specific credential compromise method has not been publicly disclosed. However, several forensic indicators confirm the hijack:

  • No OIDC trusted publisher binding: Legitimate Axios releases are published through GitHub Actions using npm’s OIDC Trusted Publisher mechanism, which creates cryptographic proof that the published package was built from a specific verified workflow. The malicious versions had no trusted publisher metadata—they were uploaded directly using a classic npm access token.
  • No gitHead or GitHub tag: The malicious npm packages contained no reference to any Git commit, confirming they were not built from the Axios repository.
  • Email change: The account email was swapped to a ProtonMail address, a tactic commonly used to prevent account recovery notifications from reaching the legitimate owner.

Axios collaborator DigitalBrainJS documented the coordination challenge in the GitHub issue: because the compromised account held the highest permissions, other collaborators could not revoke its access. npm support had to intervene directly.

Technical Analysis: The Malware Architecture

The Injection Vector: Dependency Poisoning

plain-crypto-js@4.2.1 was added as a runtime dependency in the malicious Axios versions. It was never imported anywhere in the Axios source code. The 56 cryptographic source files were bit-for-bit identical to the legitimate crypto-js@4.2.0 library—pure camouflage. The entire purpose of the package was to execute its postinstall hook.

This is a critical distinction: the attack vector was npm’s automatic lifecycle script execution, not any vulnerability in the Axios library code itself.

The Dropper: setup.js

The 4.2 KB setup.js file used a two-layer obfuscation scheme:

  • _trans_1(x, r): XOR cipher using key “OrDeR_7077” (effective key bytes: [0,0,0,0,0,0,7,0,7,7])
  • _trans_2(x, r): String reversal, underscore-to-equals substitution, base64 decode, then XOR decipher

Sensitive strings were stored in an stq[] array and decoded at runtime. The decoded C2 URL was http://sfrclak.com:8000/6202033, where 6202033 served as a campaign identifier.

The entry point function wrapped the entire attack in a top-level try/catch that swallowed all errors silently. Regardless of whether the C2 server was reachable, the dropper completed, npm install exited with code 0, and the build continued normally. This made detection through build failures impossible.

Platform-Specific Payload Delivery

macOS (darwin)

  1. Writes AppleScript to /tmp/6202033
  2. Executes via nohup osascript (detached from parent process)
  3. AppleScript uses curl to download Mach-O universal binary from C2 server
  4. RAT binary written to /Library/Caches/com.apple.act.mond (mimics Apple system cache naming)
  5. Binary is a C++ Mach-O universal with self-signing capabilities via codesign
  6. POST body identifier: packages.npm.org/product0
  7. Temporary script deleted after execution

Windows (win32)

  1. Locates PowerShell via where powershell
  2. Downloads PowerShell stage-2 payload to %TEMP%\6202033.ps1
  3. Executes with -WindowStyle Hidden -ExecutionPolicy Bypass
  4. RAT binary copied to %PROGRAMDATA%\wt.exe (disguised as Windows Terminal)
  5. Persistence established via registry Run key MicrosoftUpdate with batch file re-download mechanism
  6. VBScript launcher at %TEMP%\6202033.vbs executed via cscript //nologo
  7. POST body identifier: packages.npm.org/product1
  8. All temporary files self-deleted; persistent wt.exe remains

Linux / Other

  1. Direct shell command chains curl and nohup
  2. Python RAT downloaded to /tmp/ld.py
  3. Executed via nohup python3 /tmp/ld.py with PID detached to init (ppid: 1)
  4. POST body identifier: packages.npm.org/product2

RAT Capabilities (All Platforms)

Despite platform-specific delivery, all three variants provided consistent capabilities:

  • Remote shell execution—arbitrary command execution on the compromised machine
  • Directory browsing and file enumeration—systematic mapping of the filesystem
  • Process listing and monitoring—inventory of running processes
  • Binary injection—capability to drop additional payloads
  • C2 beaconing—heartbeat to sfrclak.com:8000 every 60 seconds
  • System reconnaissance—collection of OS, hardware, and network information

Anti-Forensic Self-Destruction

After executing the dropper, setup.js performed three sequential cleanup operations:

  1. fs.unlink(__filename)—Deletes the setup.js dropper from node_modules/plain-crypto-js/
  2. fs.unlink("package.json")—Removes the malicious manifest containing the postinstall hook
  3. fs.rename("package.md", "package.json")—Replaces with a pre-staged clean stub reporting version “4.2.0” (not 4.2.1)

This triple-cleanup creates a secondary deception layer: after infection, npm list shows a version mismatch between the resolved version (4.2.1) and the on-disk manifest (4.2.0), potentially confusing incident responders. The key detection signal: if node_modules/plain-crypto-js exists at all, the dropper ran—this package never appeared in any legitimate Axios version.

Blast Radius: Who Is Affected

Scale

  • Weekly downloads: ~100 million (npm registry)
  • Cloud presence: ~80% of cloud and code environments (Wiz research)
  • Exposure window: 2 hours 53 minutes (1.14.1), 2 hours 15 minutes (0.30.4)
  • Observed execution: 3% of affected environments showed confirmed malware execution

Direct Impact

Any system that installed axios@1.14.1 or axios@0.30.4 during the exposure window should be treated as fully compromised. This includes:

  • Developer workstations running npm install or npm update
  • CI/CD pipelines that resolve dependencies at build time without version pinning
  • Docker image builds that pulled latest Axios during the window
  • Automated dependency update tools (Dependabot, Renovate) that may have proposed or merged the malicious version

Transitive Dependency Exposure

Axios is a transitive dependency of thousands of npm packages. Organizations may have pulled the compromised versions without any developer explicitly running npm install axios. Any package that specifies Axios with a caret or tilde version range (^1.14.0 or ~1.14.0) could have resolved to 1.14.1 during the exposure window.

Cached and Archived Exposure

Docker images, npm cache directories, and CI/CD artifact stores may retain the malicious packages even after npm removal. Organizations should audit:

  • Container image layer histories for images built during the window
  • npm cache directories: find ~/.npm/_cacache -name "*.tgz" | xargs tar -tf 2>/dev/null | grep plain-crypto-js
  • CI/CD artifact retention from March 31, 2026

Attribution and Motivation

As of this writing, no formal attribution has been made. The Axios attack has not been linked to TeamPCP, the group behind the Trivy, Checkmarx, LiteLLM, and Telnyx compromises earlier in March 2026.

However, security researchers from multiple firms (Wiz, StepSecurity, Snyk, SANS) have noted characteristics consistent with state-sponsored or espionage-focused operations:

  • No financial motivation: No cryptocurrency miners, ransomware, or financial fraud components
  • Intelligence gathering focus: Extensive system reconnaissance, credential harvesting, and preparation for lateral movement
  • Operational sophistication: 18-hour pre-staging, triple-platform payload preparation, cryptographic obfuscation, forensic self-destruction, and deliberate timing (late Sunday night UTC, when response teams are at minimum staffing)
  • Persistence establishment: Windows registry Run key, macOS system cache mimicry—designed for long-term access, not smash-and-grab

Whether attributed to a known group or independent, the attack represents a new threshold in npm supply chain sophistication.

Indicators of Compromise (IOCs)

Malicious npm Packages

Package SHA1
axios@1.14.1 2553649f232204966871cea80a5d0d6adc700ca
axios@0.30.4 d6f3f62fd3b9f5432f5782b62d8cfd5247d5ee71
plain-crypto-js@4.2.1 07d889e2dadce6f3910dcbc253317d28ca61c766

Network Indicators

  • C2 Domain: sfrclak.com
  • Secondary Domain: callnrwise.com
  • C2 IP: 142.11.206.73
  • C2 Port: 8000
  • Campaign Path: /6202033
  • POST Body Identifiers: packages.npm.org/product0 (macOS), product1 (Windows), product2 (Linux)

Filesystem Artifacts

  • macOS RAT: /Library/Caches/com.apple.act.mond
  • Windows RAT (persistent): %PROGRAMDATA%\wt.exe
  • Windows ephemeral: %TEMP%\6202033.vbs, %TEMP%\6202033.ps1
  • Linux RAT: /tmp/ld.py
  • macOS ephemeral: /tmp/6202033
  • Canary directory: node_modules/plain-crypto-js/ (should never exist in legitimate Axios installs)

Attacker Accounts

  • jasonsaayman (hijacked; email changed to ifstap@proton.me)
  • nrwise (attacker-created; email nrwise@proton.me)

Remediation Playbook

Step 1: Detection

# Check installed Axios version
npm list axios

# Search for malicious dependency (its presence = dropper executed)
ls node_modules/plain-crypto-js 2>/dev/null && echo "COMPROMISED" || echo "Clean"

# Check for RAT artifacts
# macOS:
ls -la /Library/Caches/com.apple.act.mond 2>/dev/null
# Linux:
ls -la /tmp/ld.py 2>/dev/null
# Windows (PowerShell):
# Test-Path "$env:PROGRAMDATA\wt.exe"

# Check network logs for C2 traffic
# grep for sfrclak.com or 142.11.206.73 in proxy/firewall/DNS logs

Step 2: Containment

  1. If RAT artifacts are found: isolate the machine from the network immediately
  2. Do not attempt to clean in place—rebuild from known-good state
  3. Remove the malicious dependency: rm -rf node_modules/plain-crypto-js && npm install --ignore-scripts
  4. Purge npm caches: npm cache clean --force
  5. Block C2 infrastructure at network perimeter: firewall rules for 142.11.206.73, DNS sinkhole for sfrclak.com and callnrwise.com

Step 3: Credential Rotation

If any evidence of compromise is found, rotate all credentials accessible from the affected machine:

  • npm tokens and GitHub personal access tokens
  • AWS access keys and secret keys
  • GCP service account keys
  • Azure credentials and service principals
  • SSH private keys
  • Database passwords
  • CI/CD pipeline secrets
  • Container registry credentials
  • All contents of .env files

Step 4: Prevention

  • Pin Axios: "axios": "1.14.0" (no caret, no tilde)
  • Add overrides: "overrides": { "axios": "1.14.0" } in package.json
  • Disable scripts in CI/CD: npm ci --ignore-scripts
  • Configure minimum release age: Add min-release-age=7 to .npmrc
  • Evaluate native fetch: Node.js native fetch() eliminates the Axios dependency for many use cases
  • Monitor for plain-crypto-js: Add to package blocklist in your registry proxy

What This Attack Signals for the npm Ecosystem

The Axios compromise is a watershed moment for npm security. It demonstrates that:

  • Single-maintainer risk is systemic: One compromised account can poison a package used by 80% of cloud environments
  • OIDC trusted publishing is not mandatory: The attack bypassed GitHub Actions entirely by using a classic npm token. Until trusted publishing is required (not just available), the classic token attack vector remains open
  • Postinstall scripts are an unresolved attack surface: npm’s default behavior of executing lifecycle scripts gives every dependency implicit code execution rights. pnpm v10’s decision to disable postinstall scripts by default points toward the industry direction, but adoption remains early
  • Anti-forensic techniques are maturing: Self-destructing droppers, clean manifest replacement, and version number deception make post-incident detection significantly harder
  • The 2.5-hour window was enough: Even with rapid detection and removal, 3% observed execution across the Axios install base represents a massive number of compromised systems

The JavaScript ecosystem’s reliance on deep dependency trees, automatic script execution, and single-maintainer publishing authority creates an attack surface that no single mitigation can close. Defense in depth—version pinning, script disabling, release age delays, egress filtering, and server-side credential isolation—is the only viable path forward.

Frequently Asked Questions

Is Axios safe to use now?

The compromised versions (1.14.1 and 0.30.4) have been removed from npm. Safe versions include 1.14.0 and 0.30.3. However, you should verify your installed version, purge npm caches, and pin to a specific known-safe version. The underlying Axios library code was never modified—the attack was entirely through an injected dependency.

How was this attack detected?

StepSecurity identified the compromised versions on March 30, 2026, through anomalous network traffic during instrumented package installation. The dropper’s C2 communication (outbound HTTP to sfrclak.com:8000) was flagged by behavioral analysis within their Harden-Runner tool. Community researchers then confirmed the malware’s presence and documented the full attack chain within hours.

Why did the attacker target both the 0.x and 1.x branches?

Many enterprise applications still depend on Axios 0.x due to breaking changes in the 1.x release. By poisoning both branches within 39 minutes, the attacker maximized coverage across the entire Axios user base. Organizations pinned to ^0.30.0 would have resolved to 0.30.4, while those on ^1.14.0 would have received 1.14.1.

Does this attack affect frontend-only applications?

The RAT targets the machine where npm install runs, not the machine where the built application executes. For frontend applications, the primary risk is to developer workstations and CI/CD servers that build the application. The malicious postinstall script runs during installation, not at runtime in the browser. However, if a developer’s machine was compromised, the RAT could access source code, environment files, and deployment credentials.

Should I replace Axios with native fetch?

Modern Node.js (v18+) includes native fetch() support built on Undici. For straightforward HTTP requests, native fetch eliminates the Axios dependency entirely. However, Axios provides features not available in native fetch: request and response interceptors, upload progress events, automatic JSON serialization, timeout configuration, and request cancellation via AbortController integration. Evaluate whether your use case requires these features before migrating.

What is the npm minimum release age setting?

A configuration option that tells your package manager to refuse packages published less than N days ago. Supported across major package managers: npm (min-release-age=7 in .npmrc), pnpm (minimum-release-age=10080 in minutes), bun (minimumReleaseAge = 604800 in seconds), and yarn berry (npmMinimalAgeGate: "3d"). This creates a detection buffer: if a compromised version is published and discovered within the delay window, it never reaches your environment. The Axios malicious versions were live for under 3 hours—a 7-day release age gate would have prevented all exposure.