TL:DR: Malicious versions of Axios (1.14.1 and 0.30.4) hit the npm registry yesterday (30 March). This release was dropped on npm via compromising Axios maintainer’s account of and replacing the email ID with a proton mail address. They carry a malware dropper called plain-crypto-js@4.2.1. If you ran npm install in the last 24 hours, check your lockfile. Roll back to 1.14.0 and rotate every credential that was in your environment. As of now, the affected versions have been pulled from npm but any existing packages using those 2 versions or the malware dropper plain-crypto-js package should immediately check IoCs and proceed with recommended steps of removal.
Google has linked the compromise of the widely used Axios npm package to a financially driven threat cluster believed to be tied to North Korea, tracked internally as UNC1069. John Hultquist of Google’s Threat Intelligence Group (GTIG) said the activity aligns with patterns previously observed from this group. He noted that North Korean operators have a long track record of leveraging supply chain intrusions—often with the goal of siphoning cryptocurrency. While the full scope of the incident is still being assessed, the widespread use of Axios means the potential impact could extend across a large number of systems and organizations.
Axios npm package is a fundamental building block of modern web development which has been compromised. Axios gets downloaded 83 million times a week. It’s used in almost everything. That’s exactly why someone targeted it.

On March 30, attackers pushed two poisoned versions directly to npm – not a lookalike package, not a typo, the actual official package. They didn’t touch Axios’s core code, which would have triggered size-comparison alarms. Instead they introduced a malicious transitive dependency: plain-crypto-js@4.2.1. Most developers occasionally audit their direct dependencies. Almost nobody audits the dependencies of those dependencies. That’s the gap.
The timeline is the part that should unsettle you. plain-crypto-js was published at 23:59:12 UTC on March 30, a brand new package with no prior history, created for this. The poisoned Axios versions went up roughly two minutes later. Socket’s detection flagged the anomaly at 00:05:41. Five minutes from dropper to infected package. This wasn’t manual; it was a script waiting to fire.

The payload
plain-crypto-js runs via a postinstall hook, so it executes the moment you run npm install. It decodes its C2 addresses at runtime to dodge static analysis, pulls in Node.js modules to run shell commands at your privilege level – run sudo npm install, the malware had root and knows what OS it’s on. Windows: it stages payloads in ProgramData. Linux/macOS: it hides in temp directories and tries to modify your shell profile so it survives a reboot. Then it deletes its own files, leaving responders with an empty directory and not much to work with.
Researchers are tracking a newly identified backdoor, dubbed WAVESHAPER.V2, which appears to be an evolved iteration of the original WAVESHAPER malware. The earlier variant, written in C++, has been linked to campaigns attributed to UNC1069 – a threat group active since at least 2018 and known for targeting organizations in the cryptocurrency space. According to findings from Mandiant and GTIG, WAVESHAPER.V2 builds on the earlier WAVESHAPER malware, which targeted macOS and Linux systems and has been associated with the UNC1069 threat group.
The original variant relied on a minimalistic command-and-control setup using a raw binary protocol and included basic code-packing techniques to hinder analysis. In contrast, the newer version shifts to JSON-based communication, expands the scope of system data it gathers, and introduces a broader set of capabilities for remote control and execution.
The connection between this latest supply chain compromise and UNC1069 was initially highlighted by Elastic Security Labs, which pointed to notable similarities in behavior and functionality between the two malware strains.
It calls home to sfrclak.com (142.11.206.73:8000) and exfiltrates environment variables. That’s your AWS keys. Your database passwords. Your API tokens. Whatever you had in .env.
Inside the malicious plain-crypto-js Package
The malware lives in setup.js (4,209 bytes) and runs automatically via npm’s postinstall lifecycle hook. When you run npm install, the script fires before you see a prompt.
The execution path: npm install → postinstall hook → node setup.js → _entry(“6202033”) → OS detection → platform-specific payload → evidence deletion. After delivering its payload, the script deletes itself, renames package.md, and strips the postinstall hook from package.json so a post-infection scan sees nothing.
Obfuscation: XOR + reversed Base64
All 18 attack strings – module names, the command-and-control URL, shell commands, file paths are hidden behind a two-layer encoding scheme. First, the string is reversed, underscores swapped for = padding characters, then base64-decoded. Then each character gets XORed against a key (OrDeR_7077) using an index formula of 7 × i² mod 10, with a constant of 333 applied to the XOR. This isn’t minification. Someone built this.
FUNCTION decode_obfuscated(input_string): # Step 1: Reverse the string reversed_str = REVERSE(input_string) # Step 2: Replace "_" with "=" (restore Base64 padding) base64_ready = REPLACE_ALL(reversed_str, "_", "=") # Step 3: Base64 decode decoded_bytes = BASE64_DECODE(base64_ready) # Step 4: Prepare XOR key key = "OrDeR_7077" key_length = LENGTH(key) output = EMPTY_STRING # Step 5: Iterate through decoded bytes FOR i FROM 0 TO LENGTH(decoded_bytes) - 1: byte = decoded_bytes[i] # Index formula: (7 * i^2) mod 10 key_index = (7 * i * i) MOD 10 key_char = key[key_index MOD key_length] # XOR operation with constant 333 # (Assume byte and key_char are integers) transformed = (byte XOR ASCII(key_char)) XOR 333 # Append character to output output += CHAR(transformed) RETURN output
Obfuscation Decode Pseudocode (XOR + Reversed Base64)
Static decoding of all 18 entries reveals: Node.js modules (child_process, os, fs), the C2 URL (sfrclak.com:8000), platform-specific droppers for Windows, macOS, and Linux, and file targets for cleanup.
Each platform sends a POST to a different path on a fake domain – packages.npm.org/product0 (macOS), /product1 (Windows), /product2 (Linux). The actual npm registry is registry.npmjs.org. Using a close lookalike is deliberate: it makes the malicious traffic blend into SIEM logs as routine registry traffic.
Platform payloads
MacOS – The dropper uses AppleScript to download a binary to /Library/Caches/com.apple.act.mond, mimicking Apple’s com.apple.* daemon naming. The binary is made executable and launched in the background via nohup.
Windows – PowerShell gets located via where powershell and copied to %PROGRAMDATA%\wt.exe, disguised as “Windows Terminal” to sidestep EDR rules that watch powershell.exe. A VBScript wrapper then downloads a .ps1 script to %TEMP%\6202033.ps1 and runs it with -w hidden -ep bypass.
Linux – curl downloads a Python script to /tmp/ld.py and runs it detached with nohup python3.
The macOS second-stage RAT
Elastic Security’s Joe Desimone reverse-engineered the macOS binary. It’s a C++ remote access trojan with the following capabilities:
- Beaconing – Every 60 seconds it phones home with hostname, username, macOS version, timezone, CPU type, and directory listings of /Applications and ~/Library.
- User-Agent masquerade – It presents as Internet Explorer 8 on Windows XP. Probably to blend in as “weird but old” traffic rather than obviously attacker-tooling.
- peinject – Receives a Base64-encoded binary, writes it to a hidden temp file, codesigns it ad hoc, and runs it.
- runscript – Executes shell commands or AppleScripts on demand.
- rundir – Enumerates filesystems for exfiltration.
Indicators of Compromise (IoCs)
| Category | Indicator Type | Value / Artifact | Description |
|---|---|---|---|
| Network | C2 Domain | sfrclak.com | Primary command-and-control domain |
| Network | C2 URL | http://sfrclak.com:8000/ | Base endpoint used for payload delivery |
| Network | Campaign URL | http://sfrclak.com:8000/6202033 | Campaign-specific payload endpoint |
| Network | Campaign ID | 6202033 | Identifier used across payload artifacts |
| Filesystem (macOS) | Binary Path | /Library/Caches/com.apple.act.mond | Malicious binary disguised as Apple daemon |
| Filesystem (Windows) | Executable | %PROGRAMDATA%\wt.exe | Renamed PowerShell binary (masquerading as Windows Terminal) |
| Filesystem (Windows) | Script | %TEMP%\6202033.vbs | VBScript loader used for execution |
| Filesystem (Windows) | Script | %TEMP%\6202033.ps1 | PowerShell payload (often deleted post-execution) |
| Filesystem (Linux) | Script | /tmp/ld.py | Python-based stage-2 payload |
| Packages | Malicious Version | axios@1.14.1 | Compromised release (tagged latest) |
| Packages | Malicious Version | axios@0.30.4 | Compromised release (tagged legacy) |
| Packages | Malicious Package | plain-crypto-js@4.2.0 | Backdoor delivery package |
| Packages | Malicious Package | plain-crypto-js@4.2.1 | Backdoor delivery package |
| npm Account | Maintainer | jasonsaayman | Compromised npm account |
| npm Account | Malicious Email | ifstap@proton.me | Email used during compromise |
| npm Account | Legitimate Email | jasonsaayman@gmail.com | Original maintainer email |
| Behavioral | Execution | node setup.js | Triggered via npm postinstall hook |
| Behavioral | Tool Usage | curl | Used to download payloads |
| Behavioral | Tool Usage | osascript | macOS execution mechanism |
| Behavioral | Tool Usage | cscript //nologo | Windows execution mechanism |
| Behavioral | Tool Usage | nohup python3 | Linux background execution |
| Obfuscation | XOR Key | OrDeR_7077 | Key used for string decryption |
| Obfuscation | Encoding | Reversed Base64 + XOR | Two-layer obfuscation technique |
| Obfuscation | Variable | stq[] | Encoded string storage array |
| Anti-Forensics | Self-Deletion | fs.unlink(__filename) | Removes installer after execution |
| Anti-Forensics | File Swap | package.md → package.json | Hides malicious postinstall evidence |
Why GitHub showed nothing
v1.14.0 is still the latest legitimate GitHub release. v1.14.1 was pushed straight to the npm registry with no corresponding tag or commit. Either the attacker had the maintainer’s npm credentials, or they lifted a CI/CD token from a build server. The result is the same: a malicious version that looked, to most automated checks, completely legitimate.
Worse: the maintainers are currently locked out of their own package. According to a public GitHub issue, a collaborator can’t revoke the attacker’s access because the attacker’s permissions appear to exceed the legitimate staff’s. Until npm steps in manually, the situation is basically unresolved.
This is broader than Axios
Socket found two other packages in the same campaign. @shadanai/openclaw buries its malware in a vendored path where scanners typically don’t look. @qqbrowser/openclaw-qbot ships with a pre-populated node_modules folder containing the malicious Axios code already inside, no registry fetch needed.
The maintainer situation is bad
Axios collaborators have said publicly they can’t revoke the attacker’s access. The attacker’s permissions apparently exceed theirs in the npm organization, so remediation attempts are being overridden in real time.
The Axios team has now acknowledged the incident and said they are investigating how the breach happened while parallelly locking down their release pipeline. Initial findings point toward weaknesses in the publishing setup, specifically the coexistence of trusted publishing with an older, long-lived npm token that may have been abused to push malicious updates. It’s also speculated that one of the accounts of the maintainers were compromised and the email ID was changed to a proton mail address. The maintainers are currently trying to revoke existing credentials, enforcing stricter controls around package publishing, and rebuilding the release process to prevent a similar compromise going forward.
Two other packages are already distributing the same malware: @shadanai/openclaw (versions 2026.3.31-2 and 2026.3.32-1) and @qqbrowser/openclaw-qbot@0.0.130, which ships a compromised axios@1.14.1 inside its own node_modules. Both likely picked it up during automated builds.
Who’s affected
Direct: You manually updated to axios@1.14.1 or 0.30.4.
Indirect (and this is the dangerous part): You updated a library that depends on axios with a range like ^1.14.0, and your package manager silently resolved to the malicious version. Most people won’t even know to look.
| Compromised Package | Version | Malicious Dependency |
|---|---|---|
| Axios | 1.14.1 | plain-crypto-js@4.2.1 |
| Axios | 0.30.4 | plain-crypto-js@4.2.1 |
| plain-crypto-js | 4.2.1 | Primary Malicious Payload |
What to do
run npm list axios – if you see 1.14.1 or 0.30.4, you’re affected. Pin to "axios": "1.14.0" (remove the ^ or ~), run npm install axios@1.14.0 --save-exact, delete node_modules and your lockfile, then npm cache clean --force and reinstall.
Rotate credentials. Everything that was in your environment – AWS, GitHub, database passwords. Not optional.
Block sfrclak.com and 142.11.206.73 at the firewall or DNS level. Check CI/CD logs for outbound connections to port 8000.
The Axios codebase itself is fine. This was a distribution attack, not a code problem. v1.14.0 and earlier are safe but if you’ve updated anything in the last day, check before you assume you’re clean.








