One stolen npm token gave a DPRK threat actor publish rights to axios — a package with roughly 100 million weekly downloads that sits, directly or transitively, in about 80% of cloud and code environments. For three hours on March 31, 2026, every npm install that resolved axios@latest pulled a trojanized build that silently fetched a cross-platform Remote Access Trojan. Huntress alone counted at least 135 endpoints beaconing to the command-and-control server across Windows, macOS, and Linux. OpenAI publicly confirmed it was hit.
This is the highest-blast-radius open-source compromise observed to date, and ESET's APT Activity Report Q4 2025–Q1 2026 attributes it to Lazarus — specifically the financially-motivated sub-cluster tracked as Sapphire Sleet (Microsoft) and UNC1069 (Google Threat Intelligence Group), folded into Lazarus's long-running Operation DangerousPassword. The DPRK has formally moved beyond crypto theft and aerospace espionage into the open-source supply chain.
The attack ran for three hours and reached security-mature organizations worldwide. The defensive question is no longer "did we scan the package?" — it's "do we control who can publish, and what an install script is allowed to do?"
Why axios Is the Perfect Target
axios is a promise-based HTTP client for the browser and Node.js, maintained as a community project on npm. Three properties make it a single point of failure for a huge slice of the software ecosystem:
- ~100 million weekly downloads (corroborated by Trend Micro, Microsoft, ESET, Datadog Security Labs, Hackread, and Help Net Security).
- ~80% of cloud and code environments consume it directly or transitively (Datadog, Microsoft).
- More than 174,000 dependent npm packages sit downstream in the dependency graph.
A single namespace, a single maintainer account, and ubiquitous downstream consumption — that combination is exactly what a supply-chain adversary prizes. Compromise the publisher once, and registry trust collapses for every downstream consumer who runs npm install.
Who Is Behind It
MITRE ATT&CK tracks Lazarus Group as G0032, attributed to the DPRK Reconnaissance General Bureau (RGB), with aliases including Labyrinth Chollima, HIDDEN COBRA, ZINC, and Diamond Sleet. The sub-cluster responsible for this operation carries a thick alias stack across vendors:
| Tracking name | Source |
|---|---|
| Sapphire Sleet | Microsoft |
| UNC1069 | Google Threat Intelligence Group |
| BlueNoroff | Industry common |
| STARDUST CHOLLIMA | CrowdStrike naming convention |
| Alluring Pisces | Palo Alto Unit 42 |
| CageyChameleon / CryptoCore | Industry |
ESET groups the activity under Operation DangerousPassword, historically focused on cryptocurrency-exchange and Web3-developer targeting — now extended into the npm registry. The same Q4 2025–Q1 2026 window saw a sharp rise in DPRK operations against open-source ecosystems, including the DeceptiveDevelopment / Contagious Interview chain in which operators pose as recruiters to push malicious packages onto candidate developers. The axios pivot is the logical endpoint of that trajectory: from social-engineering individuals to one-shot compromise of a registry-tier dependency.
Timeline of the Compromise
All timestamps UTC. The live exposure window — from latest-tag activation to npm's removal — was roughly three hours, but the first infection landed 89 seconds after the tag flipped.
| Time (UTC) | Event | Source |
|---|---|---|
| 2026-03-30 ~23:00 | Attacker email nrwise@proton[.]me creates the plain-crypto-js namespace | Huntress |
| 2026-03-30 23:59:12 | axios@1.14.1 published with malicious plain-crypto-js@4.2.1 dependency | Huntress |
| 2026-03-31 00:05:41 | Socket's automated scanner flags the release as malicious (~6 min after publish) | Socket / Huntress |
| 2026-03-31 00:21:58 | axios@1.14.1 retagged as latest; live exposure begins | Huntress |
| 2026-03-31 00:23:27 | First telemetry-observed infection (89 s after the tag) | Huntress |
| 2026-03-31 ~03:20–03:30 | npm Security removes both malicious versions | Huntress / Microsoft |
| 2026-04-01 | Microsoft publishes incident guidance | Microsoft |
| 2026-04-20 | CISA publishes Supply Chain Compromise advisory | CISA |
| 2026-05-28 | ESET attributes the activity to Lazarus / Operation DangerousPassword | ESET |
Datadog Security Labs reported observed execution in roughly 3% of environments where axios was present during the window. Two malicious versions shipped: axios@1.14.1 (tagged latest) and axios@0.30.4 (tagged legacy). The primary asset of interest was secrets — npm publish tokens, SSH keys, cloud IAM credentials, OAuth refresh tokens, and GitHub PATs — harvested from developer workstations and CI/CD runners.
The Kill Chain
Initial Access — Maintainer Account Takeover
The compromised account, jasonsaayman, is one of the active axios maintainers. Critically, both Huntress and Microsoft report that initial access came via a stolen long-lived npm access token — not a credential phish at login. The token bypassed any interactive MFA. To retain control, the attacker set a recovery email (ifstap@proton[.]me) in case the legitimate owner reset the password.
The detection-and-prevention lesson is sharp: MFA on the login flow does nothing against a stolen, long-lived publish token. The publish path is the asset to harden.
Execution — postinstall Lifecycle Hook Abuse
Both malicious axios releases added a single line to package.json:
{
"dependencies": {
"plain-crypto-js": "^4.2.1"
}
}
plain-crypto-js@4.2.1 (SHA1 07d889e2dadce6f3910dcbc253317d28ca61c766) is a typosquat of the legitimate crypto-js and a phantom dependency — nothing in axios source actually imports it. Its only purpose is to register a postinstall script (setup.js, 4,209 bytes) that the package manager runs automatically during npm install, npm ci, yarn add, or pnpm install.
setup.js uses two obfuscation layers before branching on os.platform():
- Reversed Base64 with underscore-padding manipulation.
- XOR cipher keyed with the string
OrDeR_7077(digit form[0,0,0,0,0,0,7,0,7,7]).
postinstall (setup.js)
├─ deobfuscate: reverse-base64 → XOR(OrDeR_7077)
└─ switch(os.platform())
├─ win32 → %TEMP%\6202033.vbs → powershell copied to %PROGRAMDATA%\wt.exe
├─ darwin → osascript → curl → /Library/Caches/com.apple.act.mond (Mach-O)
└─ linux → /tmp/ld.py (stdlib-only Python; no persistence)
Cross-Platform Second Stages (the SILKBELL dropper)
Google Threat Intelligence Group tracks the dropper family as SILKBELL. It delivers a distinct payload per OS — full coverage across the heterogeneous engineering estate in a single pull.
| OS | Payload | Path / masquerade | Persistence |
|---|---|---|---|
| Windows | PowerShell RAT proxied through a renamed powershell.exe | %PROGRAMDATA%\wt.exe (mimics Windows Terminal); dropped via %TEMP%\6202033.vbs | Run key HKCU\...\Run\MicrosoftUpdate → hidden %PROGRAMDATA%\system.bat |
| macOS | Mach-O Universal binary (x86_64 + ARM64) | /Library/Caches/com.apple.act.mond (mimics Apple activitymonitord) | Re-signed via codesign; delivered via AppleScript over curl |
| Linux | Python 3 script, stdlib only | /tmp/ld.py | None — targets ephemeral CI/CD runners for one-shot secret theft |
Notable artifacts and operator slips:
- Windows beacon interval is 60 seconds. Datadog notes the Windows variant defines a
work()beacon loop but does not invoke it on first run — a coding flaw that limited persistence on some hosts. Observed RAT hashes:617b67a8e1210e4fc87c92d1d1da45a2f311c08d26e89b12307cf583c900d101anded8560c1ac7ceb6983ba995124d5917dc1a00288912387a6389296637d5f815c;system.batisf7d335205b8d7b20208fb3ef93ee6dc817905dc3ae0c10a0b164f4e7d07121cd. - macOS development artifacts leaked the Xcode project name
macWebTand the path/Users/mac/Desktop/Jain_DEV/client_mac/macWebT/. ThewebTstring matches BlueNoroff's previously documented webT macOS backdoor module. Mach-O hash:92ff08773995ebc8d55ec4b8e1a225d0d1e51efa4ef88b8849d0071230c9645a. - Linux uses only the standard library (
http.client,subprocess,json,base64,os,platform) and deliberately omits persistence — it is built to grab secrets from a CI/CD runner before the container is destroyed. Datadog observed it crashing onos.getlogin()in many container environments lacking a TTY, partially limiting impact. Hash:fcb81618bb15edfdedfb638b4c08a2af9cac9ecfa551af135a8402bf980375cf.
Command and Control
The C2 protocol is deliberately simple — and that simplicity makes it easy to catch at the network layer.
Domain : sfrclak[.]com (registered 2026-03-30 via Namecheap)
IP : 142.11.206.73 (Hostwinds ASN)
Endpoint : http://sfrclak[.]com:8000/6202033
Campaign : 6202033 → reverses to 3-30-2026 (operator's staging date)
User-Agent: mozilla/4.0 (compatible; msie 8.0; windows nt 5.1; trident/4.0)
(anachronistic IE8 string — a canary fingerprint)
Paths : packages.npm.org/product0 (macOS)
packages.npm.org/product1 (Windows)
packages.npm.org/product2 (Linux)
Transport : HTTP POST, Base64-encoded JSON body. NO TLS.
The absence of TLS is a gift to defenders — the C2 host, port, URI, and User-Agent are all visible to network inspection.
The RAT Command Set
Uniform across all three platforms:
| Command | Behavior |
|---|---|
kill | Graceful termination of the implant |
peinject | In-memory .NET assembly load (Windows) / native binary exec (others) — fileless tool execution |
runscript | Inline / Base64 / temp-file shell, PowerShell, or Python execution |
rundir | Recursive directory enumeration with metadata — credential and project-file harvesting |
The initial check-in sends a FirstInfo message containing directory listings of $HOME, Desktop, Documents, and config folders — consistent with developer-machine targeting.
MITRE ATT&CK Mapping
| Tactic | Technique | Observed |
|---|---|---|
| Initial Access | T1195.002 Compromise Software Supply Chain | Malicious axios@1.14.1 / axios@0.30.4 on npm |
| Initial Access | T1078 Valid Accounts | Stolen npm publish token for jasonsaayman |
| Execution | T1059.001 PowerShell | Windows stage-2 RAT (wt.exe-proxied PowerShell) |
| Execution | T1059.005 Visual Basic | %TEMP%\6202033.vbs dropper |
| Execution | T1059.007 JavaScript | setup.js postinstall dropper |
| Execution | T1059.006 Python | Linux /tmp/ld.py |
| Execution | T1059.002 AppleScript | macOS osascript delivery |
| Persistence | T1547.001 Registry Run Keys | HKCU\...\Run\MicrosoftUpdate → system.bat |
| Defense Evasion | T1027 Obfuscated Files or Information | Base64 + XOR (OrDeR_7077) |
| Defense Evasion | T1140 Deobfuscate/Decode Files | Runtime decode in setup.js |
| Defense Evasion | T1036.005 Masquerading | wt.exe, com.apple.act.mond, MicrosoftUpdate key |
| Defense Evasion | T1553.002 Code Signing | macOS payload re-signed via codesign |
| Defense Evasion | T1620 Reflective Code Loading | peinject in-memory .NET load |
| Discovery | T1083 File and Directory Discovery | rundir; FirstInfo enumerates $HOME/Desktop/Documents |
| Discovery | T1082 System Information Discovery | Linux reads /proc/, /sys/class/dmi/id/ |
| C2 | T1071.001 Web Protocols | HTTP POST to sfrclak[.]com:8000 |
| C2 | T1132.001 Standard Encoding | Base64-encoded JSON body |
| C2 | T1105 Ingress Tool Transfer | Stage-2 payload retrieval |
| Credential Access | T1552.001 Credentials In Files | .npmrc, ~/.aws/credentials, SSH keys |
| Exfiltration | T1041 Exfiltration Over C2 Channel | All exfil via sfrclak[.]com:8000 |
Detection: Hunt It in Your Own Stack
Every leg of this kill chain is observable. Frame the logic in terms of the telemetry you already collect — process execution and ancestry, DNS, network connections, file/script writes, registry modification, and module load in your EDR/XDR; egress flow records in your perimeter / load-balancer / ADC logs; and inbound/outbound HTTP in your WAF. Encode these as permanent custom detection rules.
The Cheapest Hunt First: Inventory for plain-crypto-js
Independently of any EDR or WAF, run an estate-wide code-host scan: search every internal Git remote, lockfile, and artifact registry for the literal string plain-crypto-js. There are zero legitimate hits for that package name. Any match — in package-lock.json, yarn.lock, a node_modules/plain-crypto-js/ directory, or a container image — is a confirmed compromise candidate. This is the highest-signal, lowest-cost check available.
Linux Endpoint — the Highest-Value Target
The Linux payload /tmp/ld.py is the prime detection target for any Linux developer or CI/CD fleet, because it leaves the least behind:
# EDR script-write + execution chain
new_file == /tmp/ld.py AND writer_ancestor IN (node, npm)
python_source_write CONTAINS ("sfrclak.com:8000" OR <IE8 UA string>)
process: python3 /tmp/ld.py WITH parent IN (node, npm)
ancestry: node → node setup.js → (python3 | curl)
originating from node_modules/plain-crypto-js/ working dir
# Network / DNS
dns_query == sfrclak.com
outbound_tcp == 142.11.206.73:8000 (plain HTTP)
# Filesystem / packaging
file_create: node_modules/plain-crypto-js/ (anywhere, incl. container images)
pkg_resolve: npm|yarn|pnpm fetching plain-crypto-js@4.2.1
Also flag shell history capturing npm install axios shortly before infection, and any unexpected sudo immediately after an npm install.
Windows Endpoint
The single cleanest indicator is powershell.exe copied to %PROGRAMDATA%\wt.exe. Build a composite rule and layer the supporting signals:
# Composite: dropper write + beacon within N seconds
node.exe writes node_modules\plain-crypto-js\setup.js
AND outbound to 142.11.206.73:8000 within N seconds
# Process chain
node.exe → wscript.exe %TEMP%\6202033.vbs
wscript.exe → (cmd.exe | powershell.exe)
powershell (-w hidden) running Base64 payload referencing sfrclak.com
PowerShell launched as renamed wt.exe from %PROGRAMDATA%
# Persistence / autostart
new value HKCU\...\CurrentVersion\Run\MicrosoftUpdate → .bat / PS command
# File writes
%PROGRAMDATA%\wt.exe (watch for missing/anomalous Mark-of-the-Web)
%PROGRAMDATA%\system.bat
%TEMP%\6202033.vbs ; 6202033.ps1
node_modules\plain-crypto-js\setup.js
# Module load / network
reflective .NET assembly load under wt.exe (the peinject command)
dns_query == sfrclak.com ; outbound 142.11.206.73:8000
HTTP URL contains /6202033 OR packages.npm.org/product1
macOS Endpoint
macOS-focused EDR earns its keep here. The payload mimics Apple's activitymonitord:
file_create: /Library/Caches/com.apple.act.mond by non-Apple-signed process
ancestry: node → setup.js → osascript → curl
process: codesign invoked on a binary in /Library/Caches/
module_load: unsigned/anomalously-signed Mach-O from /Library/Caches/
dns_query == sfrclak.com ; outbound 142.11.206.73:8000
file: any Mach-O Universal binary written to a Caches dir by a node descendant
Network Layer — WAF and Egress Flow Logs
Defense-in-depth for hosts without EDR coverage, and a permanent control against infrastructure reuse:
- In your WAF: alert/block any request whose Host header is
sfrclak.com, whose URI path contains/6202033, or whose User-Agent equals the IE8 string. Add that IE8 anachronism to your bad-UA blocklist permanently — legitimate IE8 traffic in 2026 is effectively nil, and multiple DPRK and FIN clusters reuse it. - In your perimeter / load-balancer / ADC flow logs: alert on any outbound connection to
142.11.206.73on any port, and on any outbound connection to port8000from server-side workloads where8000is not a sanctioned destination.
Prevention: Harden the Publish Path, Not Just the Package
The defensive emphasis is shifting from "scanning packages" to "hardening the publish path." Concretely:
- Pin exact versions. Drop
^/~for high-blast-radius libraries; pinaxiosand equivalents to known-good immutable versions, and usenpm ciplusoverridesto enforce pinning transitively. - Disable lifecycle scripts by default. Run
npm ci --ignore-scriptsin CI where install-time execution provides no business value; allowlist scripts by package + version only where genuinely required.postinstallis the new Office macro. - Eliminate long-lived publish tokens. Migrate internally-maintained packages to OIDC trusted publishing; require hardware-key MFA on every publisher account whose package exceeds ~10k weekly downloads. This is the control that would have stopped the takeover.
- Egress allowlist for build runners. A correctly allowlisted CI runner cannot reach
sfrclak.com:8000— the attack ends at the firewall. Treat CI egress as production egress. - SCA / SBOM gating. Fail any build that introduces a brand-new transitive dependency with no prior history in the lockfile.
plain-crypto-jshad a namespace creation date of 2026-03-30. - Pause update bots for the affected window. Hold Dependabot / Renovate on
axiosuntil a verified-key signed release and a public post-mortem exist.
Response: Block, Hunt, Rotate — In That Order
If any host resolved axios@1.14.1 or axios@0.30.4 between 2026-03-31 00:21 and ~03:30 UTC, treat it as potentially compromised pending forensic clearing.
- Block —
sfrclak.comand142.11.206.73at DNS and perimeter; outbound port8000from server estates; the IE8 User-Agent on your WAF. Push endpoint detection/blocking rules. - Hunt — run every detection above against the last 60 days, plus the estate-wide
plain-crypto-jsgrep. On ephemeral Linux runners, hunt in centralized job logs and egress flow records, since the file may have died with the container. - Rotate — treat every credential reachable from any positive-hit host as compromised: npm publish tokens, SSH keys, GitHub PATs, cloud IAM keys, OAuth refresh tokens. Rotate immediately.
- Remediate — downgrade
axiosto1.14.0/0.30.3(or a known-good prior version) and clear caches:npm cache clean --force.
Then rehearse the "lockfile recall" drill: practice finding every repo and CI job that resolved a specific malicious package within a specific window, and rotating every credential within reach. The next high-blast-radius package incident is a question of when, not if — expect more, not fewer, from DPRK in 2026.
The Takeaway
The axios compromise didn't require a novel exploit or a zero-day. It required one stolen long-lived token and an install-time script hook that runs by default. That is the uncomfortable lesson: the open-source supply chain trusts the publisher, and DPRK has industrialized stealing publisher identities. Your defenses have to assume the package is hostile and the install script is malicious — and constrain both.
If you're not sure whether your CI/CD runners and developer workstations would have caught a three-hour, install-time supply-chain compromise like this one — or whether your publish path and CI egress are actually locked down — that's exactly the kind of question our team helps answer. For a faster line, reach us at support@eagleeye-sec.com.
IOC Appendix
Network
| Indicator | Type | Role |
|---|---|---|
sfrclak[.]com | Domain | C2 (registered 2026-03-30, Namecheap) |
142.11.206.73 | IPv4 | C2 (Hostwinds ASN) |
http://sfrclak[.]com:8000/6202033 | URL | C2 endpoint (no TLS) |
8000 | TCP port | C2 listener |
mozilla/4.0 (compatible; msie 8.0; windows nt 5.1; trident/4.0) | User-Agent | Canary fingerprint on all platforms |
packages.npm.org/product0 / product1 / product2 | URI path | macOS / Windows / Linux fetch templates |
nrwise@proton[.]me | Created plain-crypto-js namespace | |
ifstap@proton[.]me | Attacker recovery address on the hijacked account |
Packages & Files
| Indicator | Type | Notes |
|---|---|---|
plain-crypto-js@4.2.1 | npm package | Typosquat / phantom dependency |
node_modules/plain-crypto-js/ | Path | Any presence is suspicious |
axios@1.14.1 / axios@0.30.4 | npm package | Malicious releases (tagged latest / legacy) |
%TEMP%\6202033.vbs | File | Windows VBScript dropper |
%PROGRAMDATA%\wt.exe | File | Renamed powershell.exe |
%PROGRAMDATA%\system.bat | File | Windows persistence |
/Library/Caches/com.apple.act.mond | File | macOS Mach-O implant |
/tmp/ld.py | File | Linux Python implant |
Hashes
| Hash | Algo | Artifact |
|---|---|---|
07d889e2dadce6f3910dcbc253317d28ca61c766 | SHA1 | setup.js inside plain-crypto-js@4.2.1 |
617b67a8e1210e4fc87c92d1d1da45a2f311c08d26e89b12307cf583c900d101 | SHA256 | Windows PowerShell RAT |
ed8560c1ac7ceb6983ba995124d5917dc1a00288912387a6389296637d5f815c | SHA256 | Windows PowerShell RAT (alt) |
f7d335205b8d7b20208fb3ef93ee6dc817905dc3ae0c10a0b164f4e7d07121cd | SHA256 | Windows system.bat |
92ff08773995ebc8d55ec4b8e1a225d0d1e51efa4ef88b8849d0071230c9645a | SHA256 | macOS Mach-O Universal binary |
fcb81618bb15edfdedfb638b4c08a2af9cac9ecfa551af135a8402bf980375cf | SHA256 | Linux /tmp/ld.py |
Other
- Obfuscation key:
OrDeR_7077(XOR, digit form[0,0,0,0,0,0,7,0,7,7]). - Campaign ID:
6202033→ reverses to3-30-2026. - macOS build artifacts: Xcode project
macWebT; path/Users/mac/Desktop/Jain_DEV/client_mac/macWebT/(links to BlueNoroff'swebTbackdoor).
References
- ESET WeLiveSecurity — APT Activity Report Q4 2025–Q1 2026
- Help Net Security — Oil shipments, drone makers, and a poisoned code library
- Huntress — Supply Chain Compromise of axios npm Package
- Huntress — Tradecraft Tuesday Recap: axios npm Supply Chain Compromise
- Microsoft Security Blog — Mitigating the axios npm supply chain compromise
- CISA — Supply Chain Compromise Impacts axios Node Package Manager
- Datadog Security Labs — Compromised axios npm package delivers cross-platform RAT
- Elastic Security Labs — Inside the axios supply chain compromise
- Trend Micro Research — Axios NPM Package Compromised
- SOCRadar — Axios npm Hijack 2026: Everything You Need to Know
- The Cyber Express — North Korea's Lazarus Group Behind the axios npm Supply Chain Attack
- Dark Reading — Axios NPM Package Compromised in Precision Attack
- Hackread — Hackers Poison Axios npm Package with 100 Million Weekly Downloads
- Mend.io — Poisoned Axios: npm Account Takeover and a RAT That Vanishes After Install
- ThreatBook — Lazarus Group Poisons Axios
- TechBriefly — OpenAI confirms axios npm supply chain attack linked to Lazarus
- HivePro — Axios npm Supply Chain Attack: What You Need to Know
- MITRE ATT&CK — Lazarus Group (G0032)
- MITRE ATT&CK — Supply Chain Compromise (T1195.002)

EagleEye Security Team
Threat Intelligence — writing on threat intelligence, detections, and agentic security operations.

