On March 31, 2026, one of the most operationally sophisticated supply chain attacks ever documented hit axios — an npm package with 83 million weekly downloads and adoption across virtually every JavaScript ecosystem.
An attacker compromised the npm credentials of jasonsaayman, the lead maintainer of axios. The attacker:
- Obtained a long-lived npm access token (not the ephemeral OIDC tokens used by the CI/CD pipeline)
- Changed the account's registered email to
ifstap@proton.me(an anonymous ProtonMail address) - Published two poisoned versions directly via the npm CLI, bypassing GitHub Actions entirely
- Targeted both release branches within 39 minutes to maximize blast radius
| Time | Event |
|---|---|
| Mar 30, 05:57 | plain-crypto-js@4.2.0 published — clean decoy to establish the package |
| Mar 30, 23:59 | plain-crypto-js@4.2.1 published — malicious postinstall payload added |
| Mar 31, 00:05 | Socket automated detection flags plain-crypto-js as anomalous |
| Mar 31, 00:21 | axios@1.14.1 published via compromised account — targets 1.x users |
| Mar 31, 01:00 | axios@0.30.4 published — targets legacy 0.x users (39 min later) |
| Mar 31, 01:30 | StepSecurity Harden-Runner captures live C2 callbacks during npm install |
| Mar 31, ~03:00 | GitHub issue filed, community mobilizes |
| Mar 31, 03:06 | Co-maintainer @DigitalBrainJS confirms jasonsaayman's credentials are compromised but cannot revoke access (collaborator, not admin) |
| Mar 31, 03:20 | @DigitalBrainJS contacts npm administration |
| Mar 31, 03:29 | plain-crypto-js removed from npm |
| Mar 31, 03:40 | npm administration revokes all compromised tokens and removes poisoned versions |
The attacker timed the release for UTC midnight to maximize the window before humans could respond. The compromised versions were live for approximately 3 hours and 40 minutes.
During incident response, @DigitalBrainJS confirmed the account takeover by pinning a GitHub issue and watching it get unpinned by the attacker using jasonsaayman's stolen credentials. The original issue (#10590) was deleted by the attacker. From the follow-up thread:
"Since access to git and the npm repository is compromised, and his git permissions are higher than mine. I'm a collaborator, not an admin. I can't revoke his access. Whatever I fix, he [can undo]."
Both axios@1.14.1 and axios@0.30.4 add a single dependency: plain-crypto-js@4.2.1. This package is never imported by axios — its only purpose is to run a postinstall script (setup.js) that deploys a cross-platform RAT.
setup.js uses two-layer string obfuscation:
_trans_1(x, r): XOR cipher with key derived from"OrDeR_7077"(only digits survive:[0,0,0,0,0,0,7,0,7,7])_trans_2(x, r): Reverses encoded strings, replaces underscores with equals signs, base64-decodes, then applies_trans_1
All sensitive data (module names, URLs, shell commands) are stored in an obfuscated array stq[].
macOS (darwin):
osascript → AppleScript writes to temp
→ curl downloads RAT to /Library/Caches/com.apple.act.mond
→ /bin/zsh launches with nohup in background
Windows (win32):
where powershell → locates PowerShell binary
→ copies to %PROGRAMDATA%\wt.exe (disguised as Windows Terminal)
→ writes VBScript to %TEMP%\6202033.vbs
→ cscript executes VBScript
→ hidden cmd.exe runs curl to fetch PowerShell RAT
→ wt.exe (PowerShell) executes with -ExecutionPolicy Bypass -WindowStyle Hidden
Linux:
curl → downloads Python script to /tmp/ld.py
→ nohup python3 launches in background
→ detaches from process tree (ppid: 1)
After payload delivery, setup.js:
- Deletes itself via
fs.unlink(__filename) - Deletes the incriminating
package.json(which contains the postinstall hook) - Renames a pre-staged clean
package.mdtopackage.json(shows version 4.2.0, no postinstall)
Result: Inspecting node_modules/plain-crypto-js/ after infection shows no evidence of compromise.
Server: sfrclak.com:8000 (142.11.206.73)
Beacon URL: http://sfrclak.com:8000/6202033
POST body identifiers (designed to look like npm registry traffic in network logs):
- macOS:
packages.npm.org/product0 - Windows:
packages.npm.org/product1 - Linux:
packages.npm.org/product2
Fake User-Agent: mozilla/4.0 (compatible; msie 8.0; windows nt 5.1; trident/4.0) — IE8 on Windows XP, an obvious tell but effective at bypassing naive user-agent filters.
On first contact, the RAT sends a FirstInfo beacon containing:
- Unique victim ID
- OS and architecture
- Current user and home directory
- Full directory enumeration targeting credentials:
.ssh/(SSH keys).aws/,.s3cfg(AWS credentials).bashrc,.profile(shell config with potential secrets).envfiles
The C2 responds with second-stage payloads for persistent access.
| Package | Version | SHA-1 |
|---|---|---|
| axios | 1.14.1 | 2553649f2322049666871cea80a5d0d6adc700ca |
| axios | 0.30.4 | d6f3f62fd3b9f5432f5782b62d8cfd5247d5ee71 |
| plain-crypto-js | 4.2.1 | 07d889e2dadce6f3910dcbc253317d28ca61c766 |
| Hash | Platform |
|---|---|
f7d335205b8d7b20208fb3ef93ee6dc817905dc3ae0c10a0b164f4e7d07121cd |
Windows Stage 1 (PowerShell stager) |
617b67a8e1210e4fc87c92d1d1da45a2f311c08d26e89b12307cf583c900d101 |
Windows Stage 2 (PowerShell RAT) |
92ff08773995ebc8d55ec4b8e1a225d0d1e51efa4ef88b8849d0071230c9645a |
macOS (Mach-O universal binary) |
fcb81618bb15edfdedfb638b4c08a2af9cac9ecfa551af135a8402bf980375cf |
Linux (Python script) |
| Type | Value |
|---|---|
| C2 Domain | sfrclak.com |
| C2 IP | 142.11.206.73 |
| C2 Port | 8000 |
| C2 URL | http://sfrclak.com:8000/6202033 |
| Attacker email | ifstap@proton.me |
| Platform | Path | Description |
|---|---|---|
| Windows | %PROGRAMDATA%\wt.exe |
PowerShell binary disguised as Windows Terminal |
| Windows | %TEMP%\6202033.vbs |
VBScript dropper (self-deletes) |
| Windows | %TEMP%\6202033.ps1 |
PowerShell RAT script (self-deletes) |
| macOS | /Library/Caches/com.apple.act.mond |
RAT binary disguised as Apple system cache |
| Linux | /tmp/ld.py |
Python RAT payload |
| All | node_modules/plain-crypto-js/ |
Presence indicates dropper executed |
| Pattern | Context |
|---|---|
FirstInfo / FirstReqPath in JSON |
RAT beacon payload |
packages.npm.org/product in POST body |
C2 beacon disguised as npm traffic |
msie 8.0; windows nt 5.1; trident/4.0 |
Fake IE8 User-Agent used by RAT |
Process: nohup python3 /tmp/ld.py |
Linux RAT execution |
Process tree: node setup.js -> sh -> curl |
Postinstall dropper chain |
| Branch | Safe version |
|---|---|
| 1.x | axios@1.14.0 (shasum: 7c29f4cf2ea91ef05018d5aa5399bf23ed3120eb) |
| 0.x | axios@0.30.3 (shasum: ab1be887a2d37dd9ebc219657704180faf2c4920) |
Pin exact versions — remove ^ and ~ prefixes from package.json.
This gist includes two scanners:
12-point check covering registry persistence, RAT file artifacts, C2 connections, DNS resolution, beacon patterns in logs, SHA256 hash matching, lockfile scanning, and compromised axios version detection. Returns exit code 1 if infected.
By @silascutler (original gist). Scans package manifests, lockfiles, node_modules metadata, JavaScript/TypeScript source files, and performs SHA256 hash matching against all known payloads. Generates a detailed report file.
- Disconnect from network immediately
- Do NOT attempt to clean in place — rebuild from a known-good state
- Rotate ALL credentials accessible from the compromised machine:
- npm tokens
- AWS/GCP/Azure keys (the RAT specifically targets
.s3cfgand.aws/) - SSH keys (the RAT enumerates
.ssh/) - CI/CD secrets
.envvalues- Database passwords
- Block C2 at firewall:
sfrclak.com/142.11.206.73 - Audit network logs for POST requests to port 8000 with the fake IE8 User-Agent
- Check for data exfiltration — the RAT sends full directory listings on first contact
- Pin exact dependency versions —
"axios": "1.14.0"not"^1.13.5" - Use lockfiles and commit them
- npm long-lived tokens are dangerous — use OIDC Trusted Publishing where possible
- Postinstall scripts are an attack vector — consider
--ignore-scriptsand explicitly allow-listing - The self-cleanup is real —
node_modulesinspection after the fact shows nothing. You need to catch it at install time or scan for the dropped artifacts - Late-night timing is intentional — attackers choose moments when human response is slowest
- StepSecurity — axios Compromised on npm
- Socket.dev — Supply Chain Attack on Axios
- SecurityOnline — Axios Under Siege
- Cyber Kendra — Axios Hack Alert
- CyberSecurityNews — Axios NPM Packages Compromised
- GitHub Issue #10604 — axios@1.14.1 and axios@0.30.4 are compromised
- GitHub Issue #10590 — Original report (deleted by attacker)
- @feross on X — Active supply chain attack alert
- @silascutler's Scanner
- Hacker News Discussion
- Hacker News — axios Compromised on npm