If you ran npm install between March 31, 00:21 UTC and 03:15 UTC, your system may be compromised. Check for node_modules/plain-crypto-js/ — its presence alone confirms the dropper executed.
In what researchers are calling one of the most operationally sophisticated supply chain attacks ever documented against a top-10 npm package, two malicious versions of axios — the JavaScript HTTP client used by millions of developers worldwide — were quietly published to the npm registry on March 31, 2026, carrying a hidden payload designed to deliver a remote access trojan and then vanish without a trace.
The attack was detected by StepSecurity’s AI Package Analyst and Harden-Runner, which flagged anomalous outbound network connections to an unknown C2 server within seconds of npm install completing on monitored pipelines.
“This was not opportunistic. It was precision. Every artifact was designed to self-destruct.”
Attack Timeline
The operation was pre-staged across 18 hours, with attacker infrastructure seeded before the poisoned packages appeared, specifically to avoid triggering “brand-new package” alarms in security scanners.
05:57 UTC
plain-crypto-js@4.2.0 published by attacker account nrwise@proton.me to establish a legitimate-looking publish history on the registry.23:59 UTC
plain-crypto-js@4.2.1 published — identical to the decoy except for a postinstall: node setup.js hook and an obfuscated RAT dropper.00:21 UTC
jasonsaayman (email changed to ifstap@proton.me) publishes axios@1.14.1 injecting the malicious dependency — bypassing all GitHub Actions CI/CD controls.01:00 UTC
axios@0.30.4 published 39 minutes later — same injection into the legacy branch, maximising exposure across both user bases simultaneously.~03:15 UTC
latest dist-tag reverts to 1.14.0. Total live window: ~2h 53m for v1.14.1, ~2h 15m for v0.30.4.04:26 UTC
plain-crypto-js@0.0.1-security.0, formally replacing the malicious package. Any install attempt now returns a security notice.How the Attack Worked
The attackers compromised the npm account of jasonsaayman, the primary axios maintainer, changing the registered email to an attacker-controlled ProtonMail address. With those credentials in hand, they published two new versions directly via the npm CLI — completely bypassing the project’s GitHub Actions CI/CD pipeline that signs all legitimate releases with OIDC provenance. Neither version has a corresponding tag or commit in the axios GitHub repository.
jasonsaayman and rotated the account email to an anonymous ProtonMail address.package.json. A single dependency — plain-crypto-js@^4.2.1 — was added. Never used in any axios source file.postinstall script in plain-crypto-js, launching the dropper silently.sfrclak.com:8000, fetches a platform-specific second-stage RAT, executes it in background, then self-destructs.Platform-Specific Payloads
Three separate RAT payloads were pre-built for each major operating system, each using platform-native tools to blend into normal system activity and establish persistence.
Writes an AppleScript to /tmp/6202033, downloads RAT binary to Apple’s cache folder, launches via /bin/zsh, then deletes the script. The filename mimics a system daemon.
/Library/Caches/com.apple.act.mond
Copies PowerShell to %PROGRAMDATA%\wt.exe (disguised as Windows Terminal), runs a hidden VBScript to fetch and execute the PowerShell RAT stage. Creates persistent re-fetch on every login via Registry Run key.
%PROGRAMDATA%\wt.exe
Uses curl to fetch a Python RAT script to /tmp/ld.py and launches it detached via nohup, orphaned to PID 1 to sever all process attribution.
/tmp/ld.py
The Forensic Cover-Up
After launching its payload, the dropper methodically erased itself: it deleted setup.js, deleted the malicious package.json, and replaced it with a pre-staged clean stub that even reports the wrong version number — 4.2.0 instead of 4.2.1 — to mislead responders running npm list. Running npm audit after infection shows nothing.
One clue survives: the directory node_modules/plain-crypto-js/ itself. This package is not a dependency of any legitimate axios version. If it exists in your project, the dropper ran.
Indicators of Compromise
| Type | Indicator |
|---|---|
| Package | axios@1.14.1 · shasum: 2553649f |
| Package | axios@0.30.4 · shasum: d6f3f62f |
| Package | plain-crypto-js@4.2.1 · shasum: 07d889e2 |
| Network | sfrclak.com · IP: 142.11.206.73 |
| Network | http://sfrclak.com:8000/6202033 |
| File (macOS) | /Library/Caches/com.apple.act.mond |
| File (Windows) | %PROGRAMDATA%\wt.exe |
| File (Linux) | /tmp/ld.py |
| Account | nrwise@proton.me (attacker-created) |
| Account | ifstap@proton.me (hijacked maintainer) |
What You Must Do Now
npm install axios@1.14.0 (1.x users) or npm install axios@0.30.3 (0.x users). Pin the version and add an overrides block to your package.json.ls node_modules/plain-crypto-js — if it exists, assume the dropper executed, regardless of what the package.json inside says.sfrclak.com (142.11.206.73) and investigate logs for beaconing to port 8000 or unusual POST requests.--ignore-scripts in CI/CD. Use npm ci --ignore-scripts as a standing policy to prevent postinstall hooks from running in automated builds going forward.







