Skip to main content
Overview

The Axios Supply Chain Attack

Merox
Merox HPC Sysadmin
5 min read
Security Beginner

StepSecurity identified and published a full technical breakdown of a supply chain attack against axios on March 30. Malwarebytes picked it up the next day. I came across the Malwarebytes report first, then went back to read StepSecurity’s analysis — then checked whether this site is exposed. Axios is the most downloaded JavaScript HTTP client on npm, around 100 million weekly downloads.

What Happened

A compromised maintainer account was used to push two malicious axios versions to npm: axios@1.14.1 and axios@0.30.4. Neither version exists in the axios GitHub repository — no commits, no tags. The attack happened entirely at the npm registry level.

Both versions were live for under three hours on March 31 before npm took them down. The window was short, but exposure wasn’t limited to developers who explicitly installed axios — anyone who ran npm install with either version already in their package.json, or with a dependency that pulled axios transitively, got the poisoned package.

The injection was precise: the only difference between the clean and malicious releases was the addition of a single hidden dependency called plain-crypto-js — a package that doesn’t exist anywhere in the real axios source code. Its only job was to run a script at install time.

What the Attack Does

When you install the compromised axios, plain-crypto-js runs automatically as part of the install process. It contacts a command-and-control server (sfrclak.com) and downloads a Remote Access Trojan specific to your operating system — macOS, Windows, or Linux. After delivering the payload, the malicious script erases itself and replaces its own metadata with a clean-looking file to avoid detection.

Warning

This means running npm audit after the fact shows nothing suspicious. The evidence is gone. If the install happened, assume the machine is compromised.

The attack was also pre-planned: 18 hours before the poisoned axios versions went live, the attacker published a clean, innocent-looking version of plain-crypto-js to establish account history — specifically to avoid “brand new package” alarms from security scanners. The malicious version followed later that night, minutes before the axios releases.

Checking This Project

This site runs on Astro and doesn’t use axios. But “not in my package.json” isn’t enough — a dependency of a dependency can pull it in without you knowing. I ran four checks:

Terminal window
npm ls axios
# astro-erudite@1.6.3
# └── (empty)
grep -i "axios" package-lock.json
# (no output)
ls node_modules/plain-crypto-js
# NOT FOUND
ls -la /Library/Caches/com.apple.act.mond
# NOT FOUND

Axios isn’t present anywhere in the dependency tree or lockfile. The plain-crypto-js directory doesn’t exist. The macOS RAT artifact isn’t on this machine. I also ran npm audit — 5 moderate vulnerabilities unrelated to this incident, zero mentions of axios or plain-crypto-js.

That folder check matters more than the version number, because the malware replaces its own package.json with a clean decoy after running — so npm list will show 4.2.0 instead of the malicious 4.2.1. If the directory exists at all, the dropper ran.

What to Do If You Use Axios

Check which version you have installed. The affected versions are 1.14.1 and 0.30.4. If you’re on either, or if you ran any npm install on March 31 between 00:21 and 03:15 UTC with axios anywhere in your dependency tree, treat the machine as compromised.

The persistent files left behind by the RAT differ by platform:

  • macOS/Library/Caches/com.apple.act.mond
  • Linux/tmp/ld.py
  • Windows (persistent)%PROGRAMDATA%\wt.exe
  • Windows (temp, self-deletes)%TEMP%\6202033.vbs, %TEMP%\6202033.ps1

If any of those exist on a machine where axios was recently installed, rotate every credential that was accessible on that machine: npm tokens, SSH keys, cloud credentials, API keys, anything stored or loaded in the shell environment.

Warning

Don’t try to clean in place. Reformat and rebuild from a known-good state, then rotate secrets from a separate clean machine.

Indicators of Compromise

For incident response or detection tooling. If you use axios and ran npm install on March 31 between 00:21–03:15 UTC, these are the signals to check.

Packages — malicious vs safe

PackageVersionSHA1Status
axios1.14.12553649f2322049666871cea80a5d0d6adc700camalicious
axios0.30.4d6f3f62fd3b9f5432f5782b62d8cfd5247d5ee71malicious
plain-crypto-js4.2.107d889e2dadce6f3910dcbc253317d28ca61c766malicious
axios1.14.07c29f4cf2ea91ef05018d5aa5399bf23ed3120ebsafe

Network

C2 domain sfrclak.com · IP 142.11.206.73 · URL http://sfrclak.com:8000/6202033

POST body by OS: macOS → packages.npm.org/product0, Windows → product1, Linux → product2

File system artifacts

PlatformPath
macOS/Library/Caches/com.apple.act.mond
Windows (persistent)%PROGRAMDATA%\wt.exe
Windows (temp)%TEMP%\6202033.vbs, %TEMP%\6202033.ps1
Linux/tmp/ld.py

Broader sweep commands

Terminal window
# installs during attack window
grep -i "npm install" ~/.zsh_history | grep "2026-03-31"
# all lockfiles referencing malicious versions
find ~ -name "package-lock.json" -exec grep -l "1\.14\.1\|0\.30\.4" {} \;
# active C2 connection
lsof -i | grep sfrclak

Attacker accounts

  • jasonsaayman — compromised axios maintainer; email changed to ifstap@proton.me
  • nrwise — attacker-created (nrwise@proton.me); published plain-crypto-js

The Broader Takeaway

What made this attack harder to catch than most is the preparation. The attacker staged the malicious dependency nearly a day in advance to build a credible history. Both the modern 1.x and legacy 0.x release branches were hit within 39 minutes. And the payload self-destructs, so post-infection inspection looks clean.

The one signal that doesn’t lie is the npm registry metadata: every legitimate axios release is published through GitHub Actions with a verified OIDC token tied to a specific workflow run. The malicious versions were published manually with a stolen account token — no workflow link, no corresponding GitHub commit. That mismatch is checkable for any package before you trust a version.

Resources

Share this post

Related Posts

SSH Hardening - Securing Your Linux Servers

Practical SSH hardening for production Linux servers — key-based auth, sshd_config, 2FA, host-based auth, fail2ban, and log monitoring.

9 min read 6 parts

SMB Authentication with AD on Linux

How to integrate Linux SMB file servers with Active Directory using SSSD, Samba, Kerberos, and realmd — tested on RHEL 8 and OpenSUSE 15.6.

7 min read

Tailscale site-to-site pfSense - Linux

How to set up a Tailscale site-to-site L3 connection between a pfSense homelab subnet and a Linux cloud VM subnet.

3 min read
Loading comments...