April 1, 2026
· 9 min read100 Million Downloads. One Compromised Account. Your Machine Is Now a Botnet.
On March 31, 2026, two malicious versions of Axios — axios@1.14.1 and axios@0.30.4 — were discovered containing a precision-engineered, self-erasing supply chain attack. The RAT it dropped could silently steal your AWS credentials, API keys, and more — and npm audit reported a clean bill of health the entire time. Here's exactly how it worked.

TL;DR
- Affected versions:
axios@1.14.1(published 00:21 UTC) andaxios@0.30.4(published 01:00 UTC) — both removed by ~03:15 UTC on March 31, 2026. Total exposure window: ~3 hours. - The attacker hijacked the
jasonsaaymannpm account (primary Axios maintainer) and changed its registered email toifstap@proton.me. - A rogue dependency —
plain-crypto-js@4.2.1— was injected into both releases. It ran a postinstall RAT dropper (node setup.js) that contacted C2 serversfrclak.com:8000. - The dropper used double-layer obfuscation: reversed Base64 with
_→=substitution, then XOR cipher with keyOrDeR_7077. - After execution, the dropper self-erased: deleted
setup.js, replacedpackage.jsonwith a clean decoy, removed thepostinstallhook. npm auditreports clean after compromise. Detection required network-level monitoring.- Early attribution links this to TeamPCP — the same group behind the Trivy, KICS, LiteLLM, and Telnyx supply chain attacks in March 2026.
- C2 indicator of compromise:
sfrclak.com:8000.
Why This Attack Is Different
Most npm malware is blunt. A bad actor hardcodes a crypto miner, someone notices, the package gets yanked. Unsophisticated. Traceable. Over in days.
This was not that.
The Axios attack is a precision-guided, multi-stage, self-erasing supply chain compromise targeting one of the most trusted packages in the JavaScript ecosystem. Axios is present in roughly 80% of cloud and code environments, per Wiz. And after the dropper ran, your system looked perfectly clean.
⚠️ Warning: Huntress observed 135 endpoints across all operating systems contacting the attacker's C2 server during the exposure window. The first infection landed 89 seconds after
axios@1.14.1was published — consistent with automated CI/CD pipelines using caret ranges (^1.x) without locked dependencies.
The Attack Timeline
The attack was staged across 18 hours — not improvised. Every step was designed to evade automated scanners that flag new, zero-history packages.
| Timestamp (UTC) | Event |
|---|---|
| Mar 30 — 05:57 | plain-crypto-js@4.2.0 published by nrwise@proton.me — clean decoy, full copy of legitimate crypto-js, no postinstall hook. Purpose: establish npm publishing history. |
| Mar 30 — 23:59 | plain-crypto-js@4.2.1 published by same account — malicious payload added, postinstall: "node setup.js" hook introduced. |
| Mar 31 — 00:21 | axios@1.14.1 published via compromised jasonsaayman account — injects plain-crypto-js@4.2.1 as a runtime dependency. Tagged latest. |
| Mar 31 — 01:00 | axios@0.30.4 published — identical injection into the legacy 0.x branch, 39 minutes later. Tagged legacy. |
| Mar 31 — ~03:15 | npm unpublishes both versions. latest reverts to axios@1.14.0. axios@1.14.1 was live for ~2h 53m; axios@0.30.4 for ~2h 15m. |
| Mar 31 — 03:25 | npm initiates security hold on plain-crypto-js. |
| Mar 31 — 04:26 | npm publishes security stub plain-crypto-js@0.0.1-security.0. plain-crypto-js@4.2.1 had been live for ~4h 27m total. |
💡 Tip: Note the timing — published just after midnight UTC on a Sunday night going into Monday. This maximized the gap before maintainers and the npm security team would be online to respond.
How the Attack Was Structured
The Axios library itself was never touched. Instead, plain-crypto-js@4.2.1 — a rogue dependency — was slipped into both release branches within 39 minutes of each other, covering both modern (1.x) and legacy (0.x) users simultaneously.
Stage 1 — Maintainer Account Hijack
The attacker compromised jasonsaayman — the primary maintainer of the Axios project — and changed the account's registered email to ifstap@proton.me. Both malicious releases appear in the npm registry as published by jasonsaayman, making them indistinguishable from legitimate releases at a glance.
Neither version appears in the project's official GitHub tags. That's the only visible hint something was wrong — and it requires you to know to look for it.
💡 Tip: One reliable supply chain hygiene check: verify that npm releases have a corresponding GitHub release or tag. If a version exists on npm but not on GitHub, treat it as suspicious.
Stage 2 — The Pre-Staged Rogue Dependency
The plain-crypto-js package was a purpose-built attack tool. Version 4.2.0 was published 18 hours before the malicious Axios releases — a clean copy of the legitimate crypto-js library with zero malicious code. Its only job was to give the npm account a brief publishing history so automated scanners wouldn't flag it as a zero-history suspicious package on the next update.
Then 4.2.1 dropped with the payload:
// plain-crypto-js@4.2.1 — package.json (malicious)
{
"name": "plain-crypto-js",
"version": "4.2.1",
"scripts": {
"postinstall": "node setup.js"
}
}The setup.js dropper was 4,209 bytes and used two layers of obfuscation to defeat static analysis:
| Layer | Technique |
|---|---|
| Layer 1 | Strings reversed, _ substituted for = padding, then Base64-decoded |
| Layer 2 | XOR cipher with key OrDeR_7077 and constant value 333 |
Stage 3 — The RAT Dropper
Once deobfuscated, setup.js executed a precise sequence — and it was fast. Malware was calling home to the attacker's server within two seconds of npm install, before npm had even finished resolving the full dependency tree.
1. Detect OS (macOS / Windows / Linux)
2. HTTP POST to sfrclak.com:8000
3. Receive OS-specific Stage 2 binary
4. Write binary to disk
5. Execute binary → establish persistent remote access
6. Begin immediate credential exfiltrationWhat the RAT harvested on first contact:
- User directory and filesystem enumeration
- Running process list
- Environment variables and
.envfiles ~/.aws/credentialsand cloud token files- SSH private keys
- CI/CD secrets (npm publish tokens, GitHub deploy keys, Kubernetes configs)
Wiz researchers observed TeamPCP validating stolen credentials within hours using TruffleHog, then conducting reconnaissance across AWS IAM, EC2, Lambda, S3, and Secrets Manager.
⚠️ Warning: Three separate Stage 2 binaries — for macOS, Windows, and Linux — were pre-built and waiting on the C2 server before the Axios releases even went live.
Stage 4 — The Cleanup (Why npm audit Sees Nothing)
After establishing remote access, the dropper erased its own footprints:
| What was deleted / replaced | Effect |
|---|---|
setup.js (the dropper) |
No script to inspect post-hoc |
plain-crypto-js/package.json |
Replaced with a clean decoy — no postinstall key |
postinstall hook |
Removed from the installed copy |
Run npm audit after this. You get a clean result. The RAT is already running.
Detection required network-level monitoring: StepSecurity's Harden-Runner flagged anomalous outbound connections to sfrclak.com:8000 in CI runs — a domain that had never appeared in any prior workflow run. Sophos customer telemetry caught the same C2 callback at approximately 00:45 UTC on March 31, within 24 minutes of the first malicious version going live.
The TeamPCP Connection
This was not a one-off opportunistic attack. Early attribution from SANS and Wiz researchers links the Axios compromise to TeamPCP — a threat actor group systematically targeting open-source DevTool supply chains to harvest cloud credentials at scale.
Between March 19 and March 27, 2026 alone, TeamPCP compromised four major open-source projects:
| Date | Package | Registry | Primary Target |
|---|---|---|---|
| March 19 | Trivy (vulnerability scanner) | npm | Cloud credentials |
| March 23 | KICS (IaC scanner) | npm | Cloud credentials, Kubernetes configs |
| March 24 | LiteLLM (AI proxy library) | PyPI | API keys, LLM tokens |
| March 27 | Telnyx (communications library) | PyPI | Communication API keys |
| March 31 | Axios (HTTP client) | npm | AWS, SSH, CI/CD secrets |
Each campaign used the same playbook: compromise a maintainer account, inject a malicious dependency, harvest credentials, validate them within hours, then move laterally through cloud infrastructure.
⚠️ Warning: Two secondary packages were also observed shipping
plain-crypto-js@4.2.1:@qqbrowser/openclaw-qbot@0.0.130and@shadanai/openclaw. Exposure through these does not depend on the March 31 UTC window.
Am I Compromised? Check This Now
Step 1 — Check for the affected Axios versions:
# Check your package.json
cat package.json | grep '"axios"'
# Check the resolved version in lockfile
cat package-lock.json | grep -A2 '"node_modules/axios"'Danger versions: axios@1.14.1 and axios@0.30.4.
Step 2 — Check for the rogue dependency:
ls node_modules | grep plain-cryptoStep 3 — Hunt for C2 traffic in your build logs:
# Indicator of Compromise — look for outbound connections to:
sfrclak.com:8000Step 4 — If evidence of compromise exists, rotate immediately:
# Credentials to treat as fully compromised:
# - AWS IAM access keys and secret keys
# - OpenAI / LLM API keys
# - GitHub personal access tokens and deploy keys
# - npm publish tokens
# - Database passwords exposed via environment variables
# - SSH private keys on the affected machineFull incident response guide: Step Security Advisory
CVE tracking: GHSA-fw8c-xr5c-95f9 and MAL-2026-2306
Snyk ID: SNYK-JS-PLAINCRYPTOJS-15850652
⚠️ Warning: Even if you don't find the RAT binary — remember, it self-deletes. If your install ran between 00:21 and 03:15 UTC on March 31, 2026 and resolved to either affected version, rotate credentials anyway.
Were You Protected? The Lockfile Question
Your exposure depended entirely on how you manage dependencies:
| Setup | Protected? |
|---|---|
package-lock.json committed, npm ci in CI |
✅ Yes — lockfile pins exact versions |
yarn.lock committed before March 31 |
✅ Yes |
Caret range (^1.x) without a committed lockfile |
❌ No — resolved to 1.14.1 immediately |
npm install --ignore-scripts |
✅ Yes — postinstall hook never ran |
| Unpinned, scheduled CI pipeline running overnight UTC | ❌ High risk — squarely in the exposure window |
Production Checklist: Harden Your npm Supply Chain
- Enable 2FA on all npm accounts with publish rights — use hardware keys, not TOTP alone.
- Use scoped CI publish tokens — never personal access tokens. Scope to specific package names.
- Commit and enforce your lockfile — use
npm ciin CI pipelines, notnpm install. - Cross-reference npm releases with GitHub tags — a version on npm without a corresponding GitHub tag is a red flag.
- Use
--ignore-scriptsin sensitive CI environments to block postinstall hooks from running. - Monitor outbound network connections from build servers — C2 callbacks are your earliest warning signal.
- Enable Snyk or Socket.dev scanning — both flagged
plain-crypto-js@4.2.1within the exposure window. - Rotate secrets on a schedule — treat credentials as ephemeral, not permanent.
# Safer CI install — blocks postinstall scripts entirely
npm ci --ignore-scripts⚠️ Warning:
--ignore-scriptswill break packages that legitimately require build steps (native addons, etc.). Audit your dependency list before enabling this globally.
Conclusion
I've been building Node.js applications long enough to remember when postinstall scripts felt like a convenience feature, not an attack surface. This attack reframes that entirely.
What makes Axios uniquely dangerous as a target is its ubiquity — present in 80% of cloud environments, embedded transitively in WordPress modules, Datadog packages, and virtually every JavaScript-adjacent toolchain. The attacker didn't need to be inside for long. Three hours was enough. And with 89 seconds from publish to first infection, there was no manual response fast enough to stop it.
The TeamPCP pattern — compromising DevTool packages to harvest cloud credentials at scale — is going to be the dominant supply chain threat model for the next few years. The tools we use to build software are more valuable targets than the software itself.
Pin your lockfiles. Use npm ci. Monitor your build server's egress. Cross-reference npm releases against GitHub tags.
One npm install really can turn your pipeline into a credential harvester. We have 135 confirmed Huntress-monitored endpoints worth of receipts now.
FAQ
Which versions of Axios were compromised?
axios@1.14.1 and axios@0.30.4 were the two malicious versions published to npm. Both have since been removed. The safe version to pin to is axios@1.14.0 for the 1.x branch.
What is plain-crypto-js and why is it dangerous?
plain-crypto-js@4.2.1 is the rogue dependency injected into both malicious Axios releases. It was purpose-built for this attack and contains a postinstall hook that runs a RAT dropper. It has since been replaced by an npm security stub (plain-crypto-js@0.0.1-security.0).
How do I check if my machine is compromised?
Check your package.json for axios@1.14.1 or axios@0.30.4. Then check node_modules for plain-crypto-js. If found, check for outbound connections to sfrclak.com:8000 in your network or build logs. Rotate all credentials immediately and follow the Step Security incident guide.
Will npm audit catch this attack?
No. The post-install script self-destructs — it deletes setup.js, replaces plain-crypto-js package.json with a clean decoy, and removes the postinstall hook. npm audit reports nothing after the fact.
Who is behind this attack?
Early analysis by SANS and Wiz links this attack to a threat actor group called TeamPCP, which compromised four other major open-source projects between March 19-27, 2026: Trivy, KICS, LiteLLM, and Telnyx.
How was the attack detected if it self-erases?
StepSecurity's Harden-Runner flagged anomalous outbound connections to sfrclak.com:8000 from CI runs — a domain that had never appeared in prior workflow runs. Sophos customer telemetry also picked up the C2 callback at around 00:45 UTC on March 31.
Was I affected if my lockfile was committed before March 31?
If your package-lock.json or yarn.lock was committed before the malicious versions were published and your install did not update it, you were not affected. The exposure window was approximately 00:21-03:15 UTC on March 31, 2026.