Keyboard Navigation
W
A
S
D
or arrow keys · M for map · Q to exit
← Back to Incident Room
2026security vulnerabilitydeveloper

Axios. 70 Million Downloads a Week. North Korea Inside.

On March 31, 2026, Sapphire Sleet — a North Korean state actor — published two malicious versions of Axios (1.14.1 and 0.30.4) to npm. Any project with caret or tilde version ranges covering those releases automatically installed a hidden dependency (plain-crypto-js@4.2.1) that silently deployed a cross-platform remote access trojan during npm install or npm ci. With over 70 million weekly downloads, the exposure window spanned hundreds to potentially millions of developer machines and CI/CD pipelines before the packages were taken down.

6 min read
Root Cause

Axios's npm account was compromised. The attacker made a single, surgical change to the release manifest — adding plain-crypto-js as a dependency — leaving Axios source code entirely untouched. The malicious dependency used npm's postInstall lifecycle hook to download and execute a second-stage RAT payload before any developer reviewed a line of code. The attack exploited two compounding trust assumptions: that a package with an unchanged source diff is safe, and that caret/tilde semver ranges in package.json are an acceptable way to receive updates.

Aftermath

The malicious packages were removed from npm. Microsoft Threat Intelligence published detailed indicators of compromise and attributed the attack to Sapphire Sleet (also tracked as BlueNoroff, CryptoCore, STARDUST CHOLLIMA). Affected users were advised to rotate all secrets and credentials, roll back to Axios 1.14.0 or 0.30.3, and flush local npm caches. The incident accelerated adoption of Trusted Publishing (OIDC) and exact-version pinning in security-conscious engineering teams.

The Incident

On March 31, 2026, a developer or automated CI job ran npm install. Axios resolved to version 1.14.1. The source code of Axios was exactly what it had always been. Nothing in the diff looked wrong. No malicious logic had been inserted into the HTTP client's request handlers or interceptors.

What had changed was one line in the package manifest: a new entry in the dependencies field for plain-crypto-js@^4.2.1.

That dependency didn't do anything visible. It was never imported by Axios's runtime code. It existed for one purpose: to fire a postInstall lifecycle hook — a shell script that ran automatically, silently, during installation — that reached out to a command-and-control server controlled by Sapphire Sleet, a North Korean state actor, and downloaded a remote access trojan tailored to the victim's operating system.

Windows got a PowerShell RAT. macOS got a native binary. Linux got a Python payload. All three called home to hxxp://sfrclak[.]com:8000/6202033. All three established persistence. The Windows variant wrote a registry run key disguised as MicrosoftUpdate. The app kept running. The build kept passing. Nothing looked wrong.

The Attack Chain

Stage 1: Establishing publishing history. Sapphire Sleet first published plain-crypto-js@4.2.0 — a clean, benign package — to build publishing history on the account and reduce scrutiny. Reviewers and automated scanners that weight publishing history as a trust signal saw a package with a track record.

Stage 2: The malicious update. plain-crypto-js@4.2.1 followed shortly after, adding the postInstall hook that runs node setup.js. The hook used layered obfuscation — reconstructing module names, file paths, and command strings at runtime — to defeat static analysis.

Stage 3: Manifest injection into Axios. Two Axios releases were then published — 1.14.1 and 0.30.4 — with a single change: plain-crypto-js@^4.2.1 added as a dependency. The Axios source code itself was untouched. A developer diffing the Axios release would find nothing alarming in the library they were using.

Stage 4: Auto-update detonation. Any project with "axios": "^1.14.0" or "axios": "^0.30.0" in its package.json — which is the default pattern recommended in npm's own documentation — would silently resolve to the malicious version on the next npm install or npm ci. In CI/CD pipelines that run npm install on every build, the RAT was deployed at build time, on the build server, with whatever credentials the pipeline had access to.

Stage 5: Defense evasion. After the RAT launched, setup.js deleted itself and swapped package.md in place of package.json, leaving behind a clean-looking manifest. Post-incident inspection of node_modules showed nothing obviously wrong. The durable artifacts — %PROGRAMDATA%\wt.exe on Windows, /Library/Caches/com.apple.act.mond on macOS — had names chosen to blend into system directories.

The Pattern

This is the Ambient Authority failure at ecosystem scale.

The JavaScript dependency ecosystem operates on a trust model that is almost entirely implicit: packages published to npm are assumed safe because npm accounts are assumed to belong to their legitimate owners, and because a package whose source code hasn't changed is assumed to contain no new risk. Both assumptions are wrong, and this attack exploited both simultaneously.

The postInstall hook system is the critical enabler. npm allows packages to run arbitrary shell code during installation — before any human has reviewed the code, before any linter has run, before any sandbox has been applied. This capability exists because the ecosystem assumed that package authors are trustworthy. For a state actor who has compromised an account, that assumption is the entire attack surface.

The semver caret (^) compounds it. The convention "axios": "^1.14.0" means "give me any non-breaking update to Axios automatically." In a world where Axios's npm credentials were secure and Axios's maintainers were vigilant, this is a reasonable convenience. In a world where a state actor can publish a new Axios version, it is an automated malware deployment mechanism.

Who Is Sapphire Sleet?

Microsoft Threat Intelligence tracks Sapphire Sleet as a North Korean state actor active since at least March 2020. The group's primary objective is cryptocurrency theft — targeting wallets, trading platforms, blockchain infrastructure, venture capital firms, and financial institutions. They are known by multiple names across the security industry: BlueNoroff, CryptoCore, STARDUST CHOLLIMA, UNC1069, Alluring Pisces, CageyChameleon.

Their toolkit is social engineering layered on top of technical compromise: LinkedIn outreach, fake meeting links impersonating Zoom, malicious files on OneDrive or Google Drive, domains mimicking US banks and crypto exchanges. The Axios attack represents an escalation of their supply chain capability — moving from targeting individual users to targeting the development toolchain itself, ensuring that compromised credentials reach systems with access to cryptocurrency-relevant secrets: API keys, wallet private keys, CI/CD environment variables.

Why It Matters

The Axios compromise matters because of the asset it targeted: developer machines and CI/CD pipelines.

When a RAT runs on a developer's laptop, it has access to everything that developer has authenticated to: cloud provider credentials, git tokens, npm publishing credentials, internal APIs, database connections, secrets managers. A single compromised developer machine in a cryptocurrency firm is a potential path to wallet keys worth millions.

When a RAT runs in a CI/CD pipeline, it has access to the environment variables that the pipeline uses to deploy software — the same credentials that allow publishing to npm, pushing to production, signing releases. A compromised CI environment is a path to poisoning the next release of whatever software that pipeline builds.

Sapphire Sleet did not need to compromise Axios's end users. They only needed to compromise Axios's npm account and wait for the ecosystem's own auto-update machinery to deliver their payload to the most credential-rich systems in every affected organization.

The Fix — and What It Wasn't

The packages were removed. Microsoft Threat Intelligence published indicators. Credentials were rotated. Pipelines were audited.

What was not fixed is the postInstall hook system. What was not fixed is the default recommendation to use caret ranges in package.json. What was not fixed is the npm account security model that allowed a state actor to publish to a package with 70 million weekly downloads.

The mitigations — exact-version pinning, Trusted Publishing with OIDC, restricting automated dependency bots, reviewing CI/CD logs for anomalous install events — are all correct. They are also entirely opt-in, recommended but not enforced, and not the default configuration in any npm tutorial or scaffold.

The attack required one compromised account and one malicious manifest line. The defense requires every downstream project in the ecosystem to proactively harden its dependency management practices against an attack class that most developers have never thought about.

The source code was clean. The diff was clean. The package was not.
Techniques
supply chain attackdependency injectionpost install hook abuseci cd poisoningdefense evasionpersistence via registry