By falco365 · Published March 31, 2026

axios compromised on npm: account takeover drops cross-platform RAT across 25 million weekly downloads

The axios npm package, used by an estimated 25 million projects, was compromised via maintainer account takeover. Two malicious versions delivered a cross-platform remote access trojan before being pulled.

axios compromised on npm: account takeover drops cross-platform RAT across 25 million weekly downloads
Analysis

The axios HTTP client for JavaScript — npm's single most downloaded package, present in an estimated 25 million projects — was compromised on March 31, 2026 via maintainer account takeover. Two malicious versions were published: axios@1.14.1 and axios@0.30.4. Both delivered a cross-platform remote access trojan. At the time of initial discovery, npm's latest dist-tag pointed to 1.14.1, meaning any unpinned npm install axios or npm install against an unlocked project installed the compromised version. The payload didn't steal credentials at install time and exit — it dropped a persistent RAT that gave the attacker an ongoing foothold on every machine that installed it.

What we know
  • March 30, 2026plain-crypto-js@4.2.0 published to npm as a clean-looking decoy package. Later the same day, plain-crypto-js@4.2.1 published from the throwaway account nrwise with a malicious postinstall hook. The package is designed to resemble the legitimate crypto-js library.
  • March 31, 2026 — The jasonsaayman maintainer account — the primary axios maintainer — is compromised. The attacker publishes axios@1.14.1 and axios@0.30.4, both adding plain-crypto-js@4.2.1 as a dependency. The maintainer email in npm metadata changed from jasonsaayman@gmail.com (on all legitimate releases) to ifstap@proton.me on both malicious versions — a forensic artifact that confirms the account compromise and distinguishes attacker-published versions from maintainer-published ones.
  • Detection signal — StepSecurity noted that legitimate axios 1.x releases used npm's OIDC trusted publisher flow via GitHub Actions. axios@1.14.1 was published manually, had no matching Git tag, and lacked the trusted-publisher metadata. The break in publishing provenance was the first hard signal before payload analysis confirmed the compromise. Safe version: axios@1.14.0 or 0.30.3.
The maintainer email change in npm metadata is a triage shortcut that should be automated. Legitimate releases across the axios 1.x history all carry jasonsaayman@gmail.com. The two malicious versions carry a Proton Mail address. Any CI system that compares the publisher identity on a version bump can flag this class of account-takeover publish before the payload is ever analyzed.
The staged dependency: plain-crypto-js

The malicious payload doesn't live in the axios tarball — it lives in the staged dependency. plain-crypto-js@4.2.1 adds a postinstall script that runs node setup.js. npm runs postinstall hooks for all dependencies during npm install, so installing a compromised axios transitively executes the payload without any explicit import or call by the victim's code.

The staging technique — publish a clean decoy version first, then publish the malicious version — serves two purposes. The decoy version establishes the package in npm's index before the attack, making it appear as a real dependency rather than a brand-new addition. And if the malicious version is yanked but the clean version remains, the dependency entry persists in the registry and can be re-weaponized.

plain-crypto-js is never imported anywhere in the axios source tree. Its sole purpose is to be installed as a dependency and run its postinstall hook.

Cross-platform RAT delivery

setup.js contacts http://sfrclak[.]com:8000/6202033 and delivers a different payload depending on the detected platform:

  • macOS — downloads a compiled RAT binary to /Library/Caches/com.apple.act.mond (a path designed to blend into Apple's cache directory structure) and launches it in the background.
  • Windows — drops a VBScript bootstrapper and a PowerShell chain. The final payload is written to %PROGRAMDATA%\wt.exe (a path designed to be confused with Windows Terminal's legitimate wt.exe binary).
  • Linux — downloads a Python RAT to /tmp/ld.py (using a name designed to evoke the legitimate ld linker) and starts it with nohup so it persists past the install session.

All three platform paths result in a persistent remote access trojan — not a one-time credential sweep. The attacker retains access to every compromised machine for as long as the RAT remains running and the victim doesn't detect it. This is operationally different from the TeamPCP and Shai-Hulud payloads in this cluster, which are designed for one-time credential exfiltration followed by worm propagation. The axios RAT is designed for sustained access to developer workstations and CI runners.

Self-deleting installer

After launching the RAT, setup.js removes itself and replaces the malicious package.json with a clean stub. A developer or incident responder who inspects node_modules/plain-crypto-js/ after the fact finds a normal-looking package with no visible malicious content. The payload is gone; only the running RAT process remains as evidence.

This self-deletion pattern makes post-infection filesystem forensics unreliable as a primary detection mechanism. Process telemetry and network telemetry — capturing the RAT's C2 beaconing and any lateral movement — are more durable artifacts than filesystem state.

One exception: the presence of node_modules/plain-crypto-js in any project is itself an indicator. Legitimate axios releases don't include this dependency. Finding the directory, even cleaned up, is evidence that an affected version was installed.

Blast radius

axios is the most downloaded package in the npm ecosystem. The compressed window between malicious publish and takedown — a matter of hours — nonetheless affected a large number of installs. Any project that ran npm install or npm ci against an unlocked dependency tree during the March 31 window may have installed the compromised version. CI/CD pipelines that install dependencies fresh on every run are the highest-risk surface: they install exact versions from npm's registry on a schedule independent of the developer's local environment.

Environments most likely to have been affected:

  • CI runners with axios as a direct dependency and no lockfile pin to a specific version
  • Developers who ran npm install axios or npm update axios on March 31 without a lockfile
  • Automated dependency update bots (Dependabot, Renovate) that may have opened PRs or merged updates to the malicious version
Indicators of compromise

Compromised packages:

  • axios@1.14.1 — malicious; safe version is 1.14.0
  • axios@0.30.4 — malicious; safe version is 0.30.3
  • plain-crypto-js@4.2.1 — the staged malicious dependency; any version of this package is suspect

npm metadata forensics:

  • Publisher email on malicious versions: ifstap@proton.me (all legitimate axios releases use jasonsaayman@gmail.com)
  • Malicious versions lack trusted publisher metadata (OIDC-sourced); published manually
  • No matching Git tag on the axios repository for 1.14.1 or 0.30.4

Filesystem artifacts (post-install; self-deletion may have removed setup.js):

  • node_modules/plain-crypto-js/ — presence indicates affected axios version was installed
  • macOS: /Library/Caches/com.apple.act.mond
  • Windows: %PROGRAMDATA%\wt.exe
  • Linux: /tmp/ld.py

Network indicator (defanged):

  • sfrclak[.]com:8000 — dropper download and RAT C2
Detection and mitigation
  • Audit lockfiles and build history for March 31 installs. Any CI run or local install that resolved to axios@1.14.1 or axios@0.30.4 on March 31, 2026 executed the payload. Check build logs, lockfile Git history, and dependency audit outputs (npm audit, npm ls axios).
  • Search for node_modules/plain-crypto-js across all repositories and build environments. This directory should not exist in any legitimate project. Its presence — even with a cleaned-up package.json — confirms the malicious axios version ran.
  • Search for RAT artifacts. On macOS: ls -la /Library/Caches/com.apple.act.mond. On Windows: %PROGRAMDATA%\wt.exe (check for an executable in this path that isn't the legitimate Windows Terminal). On Linux: /tmp/ld.py or any Python process daemonized with nohup that lacks a clear owner.
  • Review network telemetry for outbound connections to sfrclak[.]com:8000. This is a hard indicator of both the dropper firing and ongoing RAT C2.
  • Treat any CI runner or developer workstation that ran the affected versions as fully compromised. Rotate npm publish tokens, cloud credentials (AWS/Azure/GCP), GitHub tokens, SSH keys, API keys, and any secrets accessible from the affected host. The RAT provides persistent access; assume the operator has had time to enumerate the environment fully.
  • Rebuild affected CI runners from clean images. The RAT persists across session boundaries. A runner that isn't rebuilt may be re-compromised even after credential rotation.
  • Check Dependabot/Renovate PR history. If your projects use automated dependency updates, review whether any bot opened or merged a PR bumping axios to 1.14.1 or 0.30.4 on or after March 31. Those PRs may have triggered CI jobs that installed the payload.
What to automate

The axios compromise has three detection signals that are automatable and would have cut time-to-detection from "hours after discovery" to "minutes after publish":

  1. Publisher identity drift. Compare the npm account email on each new release to the historical average for the package. A proton.me address on a package that has published from gmail.com for years is a high-confidence signal.
  2. OIDC/trusted-publisher gap. For packages that have adopted npm's OIDC trusted publisher flow, flag any release that wasn't published through it. The axios compromise broke this invariant immediately.
  3. New dependency introduction in a patch release. plain-crypto-js didn't exist in any prior axios release. A patch version bump that adds a dependency not present in the previous minor release is unusual and warrants review before the version is merged into lockfiles.

All three signals are available from npm's package metadata before the tarball is ever installed. Integrating them into lockfile-update review processes — either via tooling like Socket's registry monitor or via custom checks in CI — would catch this class of attack at the metadata layer rather than at the payload layer.

Attribution

No public attribution to a specific group has been confirmed. The staging pattern (clean decoy package published first, malicious version second), the Proton Mail publisher identity, and the cross-platform RAT delivery are consistent with financially motivated actors operating in the npm ecosystem. The non-worm, non-credential-stealer payload profile — a persistent RAT — differentiates this from the TeamPCP and Shai-Hulud clusters, which prioritize token collection for propagation. The axios operator appears primarily interested in sustained access rather than supply-chain amplification.

Criminal-market signal

Dark-web sweeps run on May 6, 2026 found no criminal-market presence for this campaign on publicly-observable venues.

The absence here carries different weight than it does for the TeamPCP cluster. TeamPCP is operator-run — the actors use the tooling themselves and have no incentive to advertise it. The axios compromise is a commodity account-takeover delivering a persistent RAT, which is exactly the product access brokers sell. The clean negative on publicly-indexed dark-web venues does not mean the access is not being monetized. It means that if it is, the sale is happening through private Telegram channels or invite-only forums that this pipeline cannot reach.

The detection implication is the same regardless: registry-layer signals — the provenance gap, the manual publish breaking the OIDC chain, the staged dependency that appears in no legitimate axios release — are detectable before the RAT executes. Criminal-market monitoring adds nothing to early warning for this attack class.

The axios compromise in context

Two patterns from this incident are worth carrying forward as general detection principles.

First, the semantic mismatch between package name and purpose. plain-crypto-js sounds like a cryptography utility. It contains no cryptography. It is never imported by the dependent package. Its only code is a postinstall hook. A package that is added as a dependency but never imported anywhere in the depending package's source tree is a structural anomaly that static analysis can detect without executing the code. This pattern — phantom dependency added to a trusted package's manifest — is the canonical supply-chain payload delivery vector.

Second, the publishing provenance gap. The npm ecosystem's trusted publisher infrastructure (OIDC-based, GitHub Actions-sourced) creates a verifiable audit trail between a release and the source commit that produced it. A release that breaks this provenance chain — published manually when all prior releases were automated — is the most reliable early-warning signal for this class of attack. The SLSA provenance signal flagged in the Shai-Hulud intercom-client compromise is the same indicator: 7.0.3 had provenance attestations, 7.0.4 did not. Automating "did this release break the provenance chain its predecessors established?" is one check that cuts across all account-takeover supply-chain attacks, regardless of payload.