On 31 March 2026, attackers used the compromised npm account of the lead maintainer of Axios to publish two malicious versions of the package to the registry. The compromised releases carried a new dependency that includes a cross-platform dropper which retrieves and deploys a platform-specific remote access trojan (RAT) onto any machine that installed the package via npm. The dropper contacts a hardcoded command-and-control server, downloads platform-specific payloads for macOS, Windows, or Linux, then erases all evidence of itself and the malicious content in the package from disk, leaving only the deployed RAT.
What is Axios?
Axios is the JavaScript ecosystem's most widely used HTTP client library. Frontend frameworks, backend services, CI/CD tooling, enterprise internal apps - if it makes an HTTP request in Node.js or the browser, there's a good chance Axios is involved. Any project using a standard caret range like ^1.14.0 in its package.json would have silently resolved to the malicious version on its next install, with no visible change to the Axios source code and no indication from the package name, publisher, or version number that anything was wrong.
Axios has over 100 million weekly downloads and sits in the dependency tree of almost all Node.js applications that make HTTP requests, giving this the potential to be an extremely impactful compromise.
How was this discovered?
Socket Security's automated scanner flagged the malicious dependency plain-crypto-js@4.2.1 within six minutes of publication. StepSecurity independently identified both compromised Axios versions and published a full technical teardown, confirming through runtime analysis that the malware made its first C2 callback 1.1 seconds after npm install started.
Feross Aboukhadijeh, Socket's co-founder, posted the public alert:
"Active supply chain attack on axios – one of npm's most depended-on packages. This is textbook supply chain installer malware."
The npm security team pulled both poisoned versions and replaced them with security-holder stubs. axios@1.14.1 had been live for roughly three hours, axios@0.30.4 for about two hours and fifteen minutes. During that window, the legitimate maintainer Jason Saayman was locked out of his own account, and an Axios collaborator reported on GitHub that they could not revoke the attacker's access because the attacker's permissions exceeded their own.
Technical breakdown
Affected versions
The two compromised releases are axios@1.14.1 and axios@0.30.4. Both the modern 1.x and legacy 0.x branches were poisoned within 39 minutes of each other. Safe versions to pin are axios@1.14.0 and axios@0.30.3. Socket also identified two additional packages distributing the same payload through vendored dependencies: @shadanai/openclaw and @qqbrowser/openclaw-qbot@0.0.130.
How the attack unfolded
The attacker compromised the jasonsaayman npm account (Axios's lead maintainer), changed its registered email to an anonymous ProtonMail address, and published both malicious versions directly via the npm CLI, completely bypassing the project's normal GitHub Actions pipeline. No corresponding commits, tags, or releases exist in the Axios GitHub repository for either version.
Eighteen hours before the Axios compromise, a throwaway account (nrwise, also using ProtonMail) published plain-crypto-js@4.2.0. This is a clean copy of the legitimate crypto-js library, designed solely to build npm publishing history and avoid zero-history scanner flags.
At 23:59 UTC on 30 March, the attacker published plain-crypto-js@4.2.1 with an obfuscated dropper payload and a pre-staged clean package.json stub ready for post-execution evidence destruction.
Twenty-two minutes later, axios@1.14.1 went live with a single new dependency added: "plain-crypto-js": "^4.2.1", a package never imported or referenced anywhere in the Axios codebase. Its only purpose was to trigger npm's postinstall hook.
Exploit flow
The dropper (setup.js) uses two-layers obfuscation, string reversal plus base64, then XOR decryption to hide all sensitive strings at rest and decode them only at runtime. It dynamically imports child_process, os, and fs to avoid detection through static analysis, then branches based on the target operating system:
- npm resolves plain-crypto-js@4.2.1 as a dependency of the compromised Axios version and installs it automatically.
- The postinstall hook fires, executing node setup.js before npm install finishes.
- The dropper identifies the OS via os.platform() and contacts the C2 server at sfrclak[.]com:8000 with a platform identifier, disguised as an npm registry URL in the POST body.
- Platform-specific RAT delivery:
- macOS: AppleScript downloads a binary to /Library/Caches/com.apple.act.mond (mimicking an Apple daemon name), sets it executable, and launches it via /bin/zsh.
- Windows: Copies legitimate powershell.exe to %PROGRAMDATA%\wt.exe (disguised as Windows Terminal), then executes a hidden VBScript/PowerShell chain to fetch and run the RAT with -ExecutionPolicy Bypass.
- Linux: curl fetches a Python RAT to /tmp/ld.py and runs it detached with nohup python3, ensuring it survives after the parent process exits.
- Self-destruction: The dropper deletes setup.js, removes the package.json containing the postinstall hook, and renames a pre-staged clean stub into its place. Post-infection inspection of node_modules/plain-crypto-js/ reveals nothing suspicious.
StepSecurity's full static and runtime analysis, including decoded dropper strings and the Harden-Runner process tree, is available at https://www.stepsecurity.io/blog/axios-compromised-on-npm-malicious-versions-drop-remote-access-trojan.
Socket's analysis can be found at https://socket.dev/blog/axios-npm-package-compromised.
An important characteristic of the RAT is that it has no persistence mechanism. If an infected device is rebooted then while the binary will still exist on the disk, it will not be executed. This could be intentional by the attacker, as setting up a persistence mechanism would make the RAT more visible to EDR and defenders. Alternatively, it could be that the RAT was intended to connect to the C2 server and either retrieve a secondary payload, or receive commands which would set up persistence. It could be that the attacker intended to reserve persistence functionality only for their intended targets or high value victims.
Attribution and purpose
At present it is not possible to attribute this attack to a particular actor, or to identify the goals or motivations of the attacker, although the attack appears to have been capable and to have prioritised stealth in its payloads and functionality.
The RAT functionality is generic and versatile, as expected of an initial stage RAT, with hardcoded commands of:
- Self-removal
- Download and execution of a binary
- Script and command execution
- Enumeration of directories (hard coded targets were /home/user/.ssh and /etc/passwd)
The RAT functionality could be of use to any attacker, and by not including specific malicious functionality (such as crypto mining or credential theft) it is less likely that the RAT would be picked up by EDR. The compromise of the packages was rapid and the actions taken relatively stealthy, however this does not narrow down the actor to either a nation state or financially motivated attacker, any number of which could be capable of this attack if they had access to the maintainer’s account
DPRK actors have repeatedly performed software supply chain attacks targeting crypto developers. While Axios is not exclusively used for cryptocurrency related software projects, it is extremely widely used, and so would likely overlap with that subset of developers. There is no other reason to associate it with DPRK at present, however.
The XOR key used includes multiple 7s, often an indication that the actor is Chinese, but it also seemingly references Star Wars “Order 66”, which is not a popular franchise in China.
It may be possible to attribute the attack through forensic analysis of the C2, of the compromised maintainer account actions by npm and/or GitHub, or of the method of compromise of the maintainer’s account, though attribution of the attack may not bring much additional clarity.
How to detect whether you are affected
- Check your lockfile for references to axios@1.14.1, axios@0.30.4, or plain-crypto-js. If node_modules/plain-crypto-js/ exists in any project, the dropper ran on that machine, even though the cleanup routine will have replaced its contents with an innocent-looking stub.
- Check for RAT artifacts at the platform-specific paths: /Library/Caches/com.apple.act.mond on macOS, %PROGRAMDATA%\wt.exe on Windows, and /tmp/ld.py on Linux. Note that the dropper's self-cleanup means these may already be gone.
- Check network telemetry for any outbound connections to sfrclak[.]com or 11.206.73 on port 8000. This is your most reliable indicator, since filesystem artifacts self-delete but network logs persist.
- Audit CI/CD pipelines for any workflow that ran npm install between roughly 00:21 and 03:15 UTC on March 31. These had access to injected secrets and deployment keys.
If you suspect you are affected
Downgrade to axios@1.14.0 or axios@0.30.3, remove node_modules/plain-crypto-js, and reinstall with npm install --ignore-scripts. If you find RAT artifacts, treat the system as fully compromised and rebuild from a known-good image.
Rotate all credentials that were accessible on the compromised machine: saved passwords, npm or other tokens, SSH keys, cloud access keys, .env contents, and CI/CD secrets.
Block sfrclak[.]com and 142.11.206.73 at your firewall and DNS layer.
Hardening your supply chain
- Pin exact dependency versions and commit your lockfile. Caret ranges are what allowed automatic resolution to the malicious release. Use npm ci in CI/CD to enforce lockfile-only installs.
- Disable postinstall scripts in automated builds with npm ci --ignore-scripts. The entire attack chain depends on npm's postinstall lifecycle hook, and most packages don't need it.
- Enforce a minimum package age, e.g. by setting min-release-age=7 in .npmrc. Tools like Aikido's Safe Chain can also block packages published within a configurable cooldown window. The malicious plain-crypto-js@4.2.1 package version had existed for less than 24 hours, and even a 48-hour age gate for new packages would have prevented exploitation.
- Control egress traffic from CI/CD runners – the dropper phones home within 1.1 seconds of install. If your runners allow unrestricted outbound connections, any postinstall script can reach any C2 server.
A familiar story
The Axios compromise follows the same pattern as every major npm supply chain attack in the past year: compromised maintainer credentials, a small change to the dependency manifest, a postinstall hook running a self-deleting payload. In September 2025, phished credentials led to the compromise of Chalk and Debug (over 2 billion combined weekly downloads). That same month, the Shai-Hulud worm became the first wormable npm malware in history. By December, its second iteration had harvested roughly 400,000 developer secrets.
Recently, npm has announced planned security improvements such as mandatory FIDO 2FA, disabling automation tokens by default, and enforced trusted publishing. At present however these improvements remain in development. Until they are implemented it is third-party tools and developer awareness that must catch what npm does not.
Need help?
If you suspect your organisation has been affected by this incident and you need urgent assistance, or you want to assess your supply chain exposure before the next attack, LRQA can help.
Indicators of Compromise (IOCs)
|
Network |
|
|
C2 domain - hxxp |
//sfrclak[.]com:8000 |
|
C2 IP |
142.11.206.73:8000 |
|
C2 URL |
hxxp:////sfrclak[.]com:8000/6202033 |
|
User-Agent string |
mozilla/4.0 (compatible; msie 8.0; windows nt 5.1; trident/4.0) |
|
Mac C2 payload retrieval POST body |
packages.npm.org/product0 |
|
Windows C2 payload retrieval POST body |
packages.npm.org/product1 |
|
Linux C2 payload retrieval POST body |
packages.npm.org/product2 |
|
Mac Host |
|
|
MacOS temporary file (deleted after use) |
/tmp/6202033 |
|
MacOS RAT binary file |
/Library/Caches/com.apple.act.mond |
|
Injected binaries from RAT activity |
/private/tmp/.[XXXXXX] |
|
Mac RAT SHA256 |
92ff08773995ebc8d55ec4b8e1a225d0d1e51efa4ef88b8849d0071230c9645a |
|
Mac RAT SHA1 |
13ab317c5dcab9af2d1bdb22118b9f09f8a4038e |
|
Mac RAT MD5 |
7a9ddef00f69477b96252ca234fcbeeb |
|
Windows Host |
|
|
Copies powershell.exe to |
%PROGRAMDATA%\wt.exe |
|
VBS wrapper, deleted after use |
%TEMP%\6202033.vbs |
|
Powershell payload, deleted after use |
%TEMP%\6202033.ps1 |
|
Linux Host |
|
|
Linux temporary file |
/tmp/ld.py |
|
NPM Packages |
|
|
axios@1.14.1 shasum |
2553649f232204966871cea80a5d0d6adc700ca |
|
axios@0.30.4 shasum |
d6f3f62fd3b9f5432f5782b62d8cfd5247d5ee71 |
|
plain-crypto-js@4.2.1 shasum |
07d889e2dadce6f3910dcbc253317d28ca61c766 |
|
Post compromise activity |
After payload execution, the dropper removes any indication of the malicious code from the package directory by deleting setup.js and package.json, replacing package.json with a legitimate/clean file. |
|
Notable strings |
|
|
XOR key - OrDeR_7077 |
|
|
XOR constant - 333 |
|
|
Campaign id |
6202033 |
|
Plain-crypto-js@4.2.0 publisher |
nrwise@proton.me |
|
Attacker controlled email address used in axios maintainer account hijack |
ifstap@proton.me |
|
Attacker controlled email address used to publish plain-crypto-js |
nrwise@proton.me |
|
Npm audit commands |
|
|
Check for existence of plain-crypto-js |
npm ls plain-crypto-js |
|
Check for existence of plain-crypto-js |
cat package-lock.json | grep -A3 "plain-crypto-js" |
|
Check for existence of malicious axios versions |
grep -E '"axios".*"(1\.14\.1|0\.30\.4)"' package-lock.json |
