EagleEye
All posts

Lazarus Poisons axios: A 100-Million-Download npm Package, Owned for Three Hours

DPRK's Lazarus stole one maintainer token and pushed a trojanized axios to npm — a library in 80% of cloud environments. The blast radius was global; the exposure window was three hours. Here's the kill chain, the cross-platform RAT, and every IOC.

EagleEye Security Team

EagleEye Security Team

Threat Intelligence ·

Share

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 nameSource
Sapphire SleetMicrosoft
UNC1069Google Threat Intelligence Group
BlueNoroffIndustry common
STARDUST CHOLLIMACrowdStrike naming convention
Alluring PiscesPalo Alto Unit 42
CageyChameleon / CryptoCoreIndustry

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)EventSource
2026-03-30 ~23:00Attacker email nrwise@proton[.]me creates the plain-crypto-js namespaceHuntress
2026-03-30 23:59:12axios@1.14.1 published with malicious plain-crypto-js@4.2.1 dependencyHuntress
2026-03-31 00:05:41Socket's automated scanner flags the release as malicious (~6 min after publish)Socket / Huntress
2026-03-31 00:21:58axios@1.14.1 retagged as latest; live exposure beginsHuntress
2026-03-31 00:23:27First telemetry-observed infection (89 s after the tag)Huntress
2026-03-31 ~03:20–03:30npm Security removes both malicious versionsHuntress / Microsoft
2026-04-01Microsoft publishes incident guidanceMicrosoft
2026-04-20CISA publishes Supply Chain Compromise advisoryCISA
2026-05-28ESET attributes the activity to Lazarus / Operation DangerousPasswordESET

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():

  1. Reversed Base64 with underscore-padding manipulation.
  2. 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.

OSPayloadPath / masqueradePersistence
WindowsPowerShell RAT proxied through a renamed powershell.exe%PROGRAMDATA%\wt.exe (mimics Windows Terminal); dropped via %TEMP%\6202033.vbsRun key HKCU\...\Run\MicrosoftUpdate → hidden %PROGRAMDATA%\system.bat
macOSMach-O Universal binary (x86_64 + ARM64)/Library/Caches/com.apple.act.mond (mimics Apple activitymonitord)Re-signed via codesign; delivered via AppleScript over curl
LinuxPython 3 script, stdlib only/tmp/ld.pyNone — 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: 617b67a8e1210e4fc87c92d1d1da45a2f311c08d26e89b12307cf583c900d101 and ed8560c1ac7ceb6983ba995124d5917dc1a00288912387a6389296637d5f815c; system.bat is f7d335205b8d7b20208fb3ef93ee6dc817905dc3ae0c10a0b164f4e7d07121cd.
  • macOS development artifacts leaked the Xcode project name macWebT and the path /Users/mac/Desktop/Jain_DEV/client_mac/macWebT/. The webT string 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 on os.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:

CommandBehavior
killGraceful termination of the implant
peinjectIn-memory .NET assembly load (Windows) / native binary exec (others) — fileless tool execution
runscriptInline / Base64 / temp-file shell, PowerShell, or Python execution
rundirRecursive 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

TacticTechniqueObserved
Initial AccessT1195.002 Compromise Software Supply ChainMalicious axios@1.14.1 / axios@0.30.4 on npm
Initial AccessT1078 Valid AccountsStolen npm publish token for jasonsaayman
ExecutionT1059.001 PowerShellWindows stage-2 RAT (wt.exe-proxied PowerShell)
ExecutionT1059.005 Visual Basic%TEMP%\6202033.vbs dropper
ExecutionT1059.007 JavaScriptsetup.js postinstall dropper
ExecutionT1059.006 PythonLinux /tmp/ld.py
ExecutionT1059.002 AppleScriptmacOS osascript delivery
PersistenceT1547.001 Registry Run KeysHKCU\...\Run\MicrosoftUpdatesystem.bat
Defense EvasionT1027 Obfuscated Files or InformationBase64 + XOR (OrDeR_7077)
Defense EvasionT1140 Deobfuscate/Decode FilesRuntime decode in setup.js
Defense EvasionT1036.005 Masqueradingwt.exe, com.apple.act.mond, MicrosoftUpdate key
Defense EvasionT1553.002 Code SigningmacOS payload re-signed via codesign
Defense EvasionT1620 Reflective Code Loadingpeinject in-memory .NET load
DiscoveryT1083 File and Directory Discoveryrundir; FirstInfo enumerates $HOME/Desktop/Documents
DiscoveryT1082 System Information DiscoveryLinux reads /proc/, /sys/class/dmi/id/
C2T1071.001 Web ProtocolsHTTP POST to sfrclak[.]com:8000
C2T1132.001 Standard EncodingBase64-encoded JSON body
C2T1105 Ingress Tool TransferStage-2 payload retrieval
Credential AccessT1552.001 Credentials In Files.npmrc, ~/.aws/credentials, SSH keys
ExfiltrationT1041 Exfiltration Over C2 ChannelAll 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.73 on any port, and on any outbound connection to port 8000 from server-side workloads where 8000 is 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; pin axios and equivalents to known-good immutable versions, and use npm ci plus overrides to enforce pinning transitively.
  • Disable lifecycle scripts by default. Run npm ci --ignore-scripts in CI where install-time execution provides no business value; allowlist scripts by package + version only where genuinely required. postinstall is 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-js had a namespace creation date of 2026-03-30.
  • Pause update bots for the affected window. Hold Dependabot / Renovate on axios until 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.

  1. Blocksfrclak.com and 142.11.206.73 at DNS and perimeter; outbound port 8000 from server estates; the IE8 User-Agent on your WAF. Push endpoint detection/blocking rules.
  2. Hunt — run every detection above against the last 60 days, plus the estate-wide plain-crypto-js grep. On ephemeral Linux runners, hunt in centralized job logs and egress flow records, since the file may have died with the container.
  3. 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.
  4. Remediate — downgrade axios to 1.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

IndicatorTypeRole
sfrclak[.]comDomainC2 (registered 2026-03-30, Namecheap)
142.11.206.73IPv4C2 (Hostwinds ASN)
http://sfrclak[.]com:8000/6202033URLC2 endpoint (no TLS)
8000TCP portC2 listener
mozilla/4.0 (compatible; msie 8.0; windows nt 5.1; trident/4.0)User-AgentCanary fingerprint on all platforms
packages.npm.org/product0 / product1 / product2URI pathmacOS / Windows / Linux fetch templates
nrwise@proton[.]meEmailCreated plain-crypto-js namespace
ifstap@proton[.]meEmailAttacker recovery address on the hijacked account

Packages & Files

IndicatorTypeNotes
plain-crypto-js@4.2.1npm packageTyposquat / phantom dependency
node_modules/plain-crypto-js/PathAny presence is suspicious
axios@1.14.1 / axios@0.30.4npm packageMalicious releases (tagged latest / legacy)
%TEMP%\6202033.vbsFileWindows VBScript dropper
%PROGRAMDATA%\wt.exeFileRenamed powershell.exe
%PROGRAMDATA%\system.batFileWindows persistence
/Library/Caches/com.apple.act.mondFilemacOS Mach-O implant
/tmp/ld.pyFileLinux Python implant

Hashes

HashAlgoArtifact
07d889e2dadce6f3910dcbc253317d28ca61c766SHA1setup.js inside plain-crypto-js@4.2.1
617b67a8e1210e4fc87c92d1d1da45a2f311c08d26e89b12307cf583c900d101SHA256Windows PowerShell RAT
ed8560c1ac7ceb6983ba995124d5917dc1a00288912387a6389296637d5f815cSHA256Windows PowerShell RAT (alt)
f7d335205b8d7b20208fb3ef93ee6dc817905dc3ae0c10a0b164f4e7d07121cdSHA256Windows system.bat
92ff08773995ebc8d55ec4b8e1a225d0d1e51efa4ef88b8849d0071230c9645aSHA256macOS Mach-O Universal binary
fcb81618bb15edfdedfb638b4c08a2af9cac9ecfa551af135a8402bf980375cfSHA256Linux /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 to 3-30-2026.
  • macOS build artifacts: Xcode project macWebT; path /Users/mac/Desktop/Jain_DEV/client_mac/macWebT/ (links to BlueNoroff's webT backdoor).

References

  1. ESET WeLiveSecurity — APT Activity Report Q4 2025–Q1 2026
  2. Help Net Security — Oil shipments, drone makers, and a poisoned code library
  3. Huntress — Supply Chain Compromise of axios npm Package
  4. Huntress — Tradecraft Tuesday Recap: axios npm Supply Chain Compromise
  5. Microsoft Security Blog — Mitigating the axios npm supply chain compromise
  6. CISA — Supply Chain Compromise Impacts axios Node Package Manager
  7. Datadog Security Labs — Compromised axios npm package delivers cross-platform RAT
  8. Elastic Security Labs — Inside the axios supply chain compromise
  9. Trend Micro Research — Axios NPM Package Compromised
  10. SOCRadar — Axios npm Hijack 2026: Everything You Need to Know
  11. The Cyber Express — North Korea's Lazarus Group Behind the axios npm Supply Chain Attack
  12. Dark Reading — Axios NPM Package Compromised in Precision Attack
  13. Hackread — Hackers Poison Axios npm Package with 100 Million Weekly Downloads
  14. Mend.io — Poisoned Axios: npm Account Takeover and a RAT That Vanishes After Install
  15. ThreatBook — Lazarus Group Poisons Axios
  16. TechBriefly — OpenAI confirms axios npm supply chain attack linked to Lazarus
  17. HivePro — Axios npm Supply Chain Attack: What You Need to Know
  18. MITRE ATT&CK — Lazarus Group (G0032)
  19. MITRE ATT&CK — Supply Chain Compromise (T1195.002)
#lazarus#npm#supply-chain#axios#dprk#bluenoroff
EagleEye Security Team

EagleEye Security Team

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

Share

Keep reading

All posts →

CVE-2026-20131: How Interlock Turned a Cisco Firewall Into Root for 36 Days

Interlock ransomware exploited a maximum-severity deserialization flaw in Cisco Secure FMC as a zero-day for 36 days before a patch existed — owning the management plane of enterprise firewall estates. Here's the kill chain, the IOCs, and how to hunt it.

EagleEye Security Team