Executive Summary

On March 31, 2026, attackers briefly turned one of the most widely used JavaScript libraries into a cross-platform malware delivery mechanism. The compromised package was Axios, the default HTTP client in a large share of Node and frontend projects. The malicious releases were axios@1.14.1 and axios@0.30.4. The Axios source code itself was not modified. Instead, the attacker added a dependency, plain-crypto-js@^4.2.1, whose only real job was to execute malicious code during package installation.1

That distinction matters. Teams reviewing application behavior, unit tests, or Git diffs inside the Axios repository could miss the compromise entirely. Apps often continued working normally. The malicious activity happened during npm install, npm update, and CI dependency resolution. If a workstation or build runner pulled one of the bad versions during the exposure window, the host may have downloaded and executed a second-stage remote access trojan without any visible breakage in the application itself.2

For practitioners, the right mental model is simple: if you installed the bad Axios versions, you are investigating host compromise, not a broken dependency.

What Happened

According to Google Threat Intelligence Group, the malicious Axios releases were live between 00:21 UTC and 03:20 UTC on March 31, 2026.1 During that window, an attacker who had hijacked the package maintainer’s npm account changed the account email to ifstap@proton.me and published two tainted versions: 1.14.1 on the main line and 0.30.4 on the older branch.1

The attacker did not alter Axios runtime code. Instead, they made a surgical manifest-only change by adding plain-crypto-js@^4.2.1 as a dependency. That package had been prepared in advance. A clean plain-crypto-js@4.2.0 was seeded first to create benign publishing history and reduce scrutiny. Then 4.2.1 introduced a postinstall hook that silently ran node setup.js during installation.2

This is what made the attack dangerous. Dependency consumers were not required to execute Axios in production to get infected. Pulling the package could be enough. Developer laptops, ephemeral CI runners, build servers, and release pipelines were the likely initial blast radius. In a mature environment, those systems often hold the exact secrets an operator wants: cloud credentials, package publishing tokens, SSH keys, signing material, service account access, and sometimes production deployment rights.

Axios reportedly serves more than 100 million weekly downloads across the ecosystem, which is why this incident should be treated as infrastructure-level supply chain compromise, not a niche package event.1 Even with the malicious releases available for only about three hours, that is a wide enough window for significant downstream exposure.

Who Did It

Google attributed the operation to UNC1069, a North Korea-nexus threat actor active since at least 2018 and known for financially motivated operations against cryptocurrency, blockchain, and related targets.1 Microsoft attributed the same campaign to Sapphire Sleet, which is Microsoft’s tracking name for the cluster also associated with aliases including STARDUST CHOLLIMA, Alluring Pisces, BlueNoroff, CageyChameleon, and CryptoCore.23

The attribution is not based on vibe or geopolitics. Google tied the campaign to UNC1069 through a combination of malware lineage and infrastructure overlap. The command and control domain sfrclak[.]com, which resolved to 142.11.206.73, showed connections from an AstrillVPN node previously used by UNC1069. Google also assessed the deployed malware, WAVESHAPER.V2, as a direct evolution of WAVESHAPER, a backdoor family already linked to the same actor.1

That actor profile matters for defenders because it suggests two things. First, credential theft and monetization are plausible objectives even if the initial compromise vector was a developer dependency. Second, post-exploitation may extend beyond the local machine. A compromised build host is often a path to code signing, artifact tampering, lateral movement into CI systems, and theft of cloud or wallet credentials.

The Attack Chain

The install-time chain was compact and professionally built. That is part of why it worked.

The malicious dependency, plain-crypto-js@4.2.1, defined a postinstall hook that ran node setup.js silently during npm installation.1 Google tracks that obfuscated first stage as SILKBELL. The script used XOR and Base64 obfuscation to hide strings and dynamically loaded modules such as fs, os, and execSync to reduce obvious static indicators.1

Once executed, setup.js detected the operating system and reached out to hxxp://sfrclak[.]com:8000/6202033. The POST body told the server which payload to return:

  • packages.npm.org/product0 for macOS
  • packages.npm.org/product1 for Windows
  • packages.npm.org/product2 for Linux1

After fetching and launching the next stage, setup.js attempted cleanup. It deleted itself and replaced the malicious package.json with a benign package.md, restoring the package metadata to a clean-looking state. That is a smart anti-forensics move. If an engineer inspects node_modules/plain-crypto-js after the fact, the obvious install hook may already be gone.12

Windows Path

On Windows, the loader looked for powershell.exe, copied it to %PROGRAMDATA%\wt.exe, downloaded a PowerShell script to %TEMP%\6202033.ps1, and executed it with a hidden window and execution policy bypass. Persistence was established via %PROGRAMDATA%\system.bat and a user Run key at HKCU:\Software\Microsoft\Windows\CurrentVersion\Run\MicrosoftUpdate.12

This path matters for detection because it combines several signals that should not normally cluster together on a developer workstation: Node spawning script hosts, a renamed PowerShell binary under ProgramData, a temp payload named 6202033.ps1, and a new Run key masquerading as a Microsoft update task.

macOS Path

On macOS, the loader used AppleScript and shell commands to fetch a native Mach-O payload to /Library/Caches/com.apple.act.mond, set mode 770, and launch it in the background via /bin/zsh. The AppleScript was then removed.12

The final on-disk artifact is more important than the transient script. If /Library/Caches/com.apple.act.mond exists, treat it as high-confidence evidence of compromise.

Linux Path

On Linux, the loader downloaded a Python payload to /tmp/ld.py and launched it detached with nohup python3.12

Build runners often have Python available, permissive temp directories, and outbound network access. In cloud CI, that can be enough to collect tokens and environment variables before the runner is destroyed.

WAVESHAPER.V2 Capabilities

The second stage across platforms is tracked as WAVESHAPER.V2. Google describes it as beaconing every 60 seconds with Base64-encoded JSON to the same C2 and using the user-agent mozilla/4.0 (compatible; msie 8.0; windows nt 5.1; trident/4.0).1

Supported commands include:

  • kill
  • rundir for directory listing and metadata collection
  • runscript for AppleScript and platform-specific script execution
  • peinject for binary injection1

On Windows in particular, the RAT supports in-memory PE injection, arbitrary PowerShell execution, process enumeration, and detailed host telemetry collection such as hostname, username, boot time, timezone, OS version, and running processes.12

That is enough capability to move from initial access into credential theft, recon, and follow-on payload delivery.

How to Check If You’re Affected

This is the part that matters most. You are trying to answer four questions:

  1. Did any environment resolve axios@1.14.1 or axios@0.30.4?
  2. Did any environment install plain-crypto-js@4.2.1?
  3. Did any host execute the second stage or retain on-disk artifacts?
  4. Did any CI or build system run npm during the exposure window?

Do not stop at package.json. Check lockfiles, caches, CI logs, and the host itself.

Step 1: Check Axios Versions Everywhere

Start with the obvious local dependency tree checks.

# Check all axios in your project
npm ls axios

# Check globally
npm list -g axios

# Specific check for the bad versions
npm ls axios | grep -E "1\.14\.1|0\.30\.4"

That only tells you what is currently installed in the working directory. It does not answer whether a machine pulled the bad version earlier and later replaced it.

Search the filesystem for Axios package manifests with the compromised versions.

# Find ALL axios installations on the machine
find / -name "package.json" -path "*/axios/package.json" -exec grep -l '"version": "1.14.1\|0.30.4"' {} \; 2>/dev/null

If you use multiple package managers or monorepos, also inspect lockfiles directly. This is usually faster and more reliable than trusting the current node_modules state.

# Search lockfiles in the current repo
grep -R -n -E 'axios(@|": ")?(1\.14\.1|0\.30\.4)' . 2>/dev/null

# Search common lockfiles recursively
find . \( -name "package-lock.json" -o -name "yarn.lock" -o -name "pnpm-lock.yaml" \) -print0 | \
  xargs -0 grep -n -E 'axios(@|": ")?(1\.14\.1|0\.30\.4)' 2>/dev/null

If your organization uses artifact proxies such as Verdaccio, Nexus, JFrog Artifactory, or GitHub Packages, search those logs too. A clean workstation does not prove the package was never pulled somewhere else.

Step 2: Check for the Malicious Dependency

The cleanest direct indicator is the injected dependency. Search for plain-crypto-js on disk and in lockfiles.

# Look for plain-crypto-js anywhere
find / -name "plain-crypto-js" -type d 2>/dev/null
npm ls plain-crypto-js 2>/dev/null

# Check lockfiles
grep -r "plain-crypto-js" package-lock.json yarn.lock pnpm-lock.yaml 2>/dev/null

For large repos or a whole home directory, broaden that search.

# Search from the current repo downward
find . -type f \( -name "package-lock.json" -o -name "yarn.lock" -o -name "pnpm-lock.yaml" \) -print0 | \
  xargs -0 grep -n "plain-crypto-js" 2>/dev/null

# Search node_modules package manifests for postinstall evidence
find . -path "*/node_modules/plain-crypto-js/package.json" -print -exec sed -n '1,120p' {} \; 2>/dev/null

Remember the loader attempted self-cleanup by restoring a clean manifest. That means absence of the postinstall hook in a surviving package directory does not prove the dependency was harmless. If lockfiles show plain-crypto-js@4.2.1, assume the install hook may have run.12

Also check package manager caches.

# npm cache location
npm config get cache

# Search npm cache for indicators
CACHE_DIR=$(npm config get cache 2>/dev/null)
[ -n "$CACHE_DIR" ] && find "$CACHE_DIR" -type f | grep -E 'axios|plain-crypto-js|6202033' 2>/dev/null

# Yarn and pnpm default cache hints
find ~/.cache ~/.local/share ~/Library/Caches -type f 2>/dev/null | grep -E 'axios|plain-crypto-js|6202033' 2>/dev/null

On CI systems, look at the shared cache backing store, not just the current runner.

Step 3: Check Your Install and Build History

A lot of teams are going to miss this if they only inspect code. You need to know whether npm install, npm update, or image builds happened during the exposure window, which was 00:21 to 03:20 UTC on March 31, 2026.1

Look at:

  • CI job history
  • container build logs
  • self-hosted runner logs
  • shell history on developer workstations
  • package proxy access logs
  • EDR process execution logs

Useful local checks:

# Shell history hints
grep -R -n -E 'npm (install|update|ci)|yarn install|pnpm install' ~/.bash_history ~/.zsh_history 2>/dev/null

# GitHub Actions logs downloaded locally, if present
find . -type f | grep -E 'actions|workflow|build|ci' 2>/dev/null

# Docker build history in local project files
grep -R -n -E 'npm (install|ci)|yarn install|pnpm install' Dockerfile docker-compose*.yml .github .gitlab-ci.yml .circleci 2>/dev/null

In centralized logging, query for Node or package manager process execution during the window and correlate it with outbound connections to sfrclak[.]com, 142.11.206.73, or TCP port 8000.

If you run ephemeral CI runners, do not assume you are safe because the host is gone. The runner may have exposed tokens during execution, and those are still live unless you rotate them.

Step 4: Check for On-Disk IOCs by Operating System

If you find host artifacts, stop debating severity. Treat the host as compromised.

Windows

Use the PowerShell checks below first.

# Check for persistence
Get-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" | Select-Object MicrosoftUpdate

# Check for renamed PowerShell
Test-Path "$env:PROGRAMDATA\wt.exe"

# Check for system.bat
Test-Path "$env:PROGRAMDATA\system.bat"

# Check temp for payload remnants
Get-ChildItem "$env:TEMP" -Filter "6202033*"

Then expand to process, registry, and file metadata checks.

# Look for suspicious running processes
Get-Process | Where-Object { $_.Path -like "*wt.exe" -or $_.ProcessName -match "powershell|cscript|wscript" } | \
  Select-Object ProcessName, Id, Path

# Inspect the Run key in full
reg query "HKCU\Software\Microsoft\Windows\CurrentVersion\Run"

# Hash suspicious files if present
Get-FileHash "$env:PROGRAMDATA\system.bat" -Algorithm SHA256
Get-FileHash "$env:PROGRAMDATA\wt.exe" -Algorithm SHA256

Look for unexpected staging under ProgramData, renamed binaries, temp script artifacts, and evidence that Node or script hosts launched hidden PowerShell.

macOS

Start with the known path and process list.

# Check for the Mach-O binary
ls -la /Library/Caches/com.apple.act.mond

# Check running processes
ps aux | grep -i "com.apple.act.mond"

Then add metadata, signing, and quarantine checks.

# File metadata and hash
stat /Library/Caches/com.apple.act.mond 2>/dev/null
shasum -a 256 /Library/Caches/com.apple.act.mond 2>/dev/null
file /Library/Caches/com.apple.act.mond 2>/dev/null

# Code signing and notarization status
codesign -dv --verbose=4 /Library/Caches/com.apple.act.mond 2>&1
spctl --assess --verbose /Library/Caches/com.apple.act.mond 2>&1

# Unified log hunting
log show --last 7d --predicate 'eventMessage CONTAINS[c] "com.apple.act.mond" OR eventMessage CONTAINS[c] "sfrclak"' 2>/dev/null

A binary in /Library/Caches with that name is already suspicious. If it is unsigned, ad hoc signed, or shows execution around the install window, you have enough to escalate.

Linux

Start with the path and running processes.

# Check for the Python payload
ls -la /tmp/ld.py

# Check for running instances
ps aux | grep "ld.py"

# Check for unexpected python3 processes
ps aux | grep python3 | grep -v grep

Then inspect command lines, sockets, and hashes.

# Detailed process list
ps -ef | grep -E 'ld.py|6202033|python3' | grep -v grep

# File metadata and hash
stat /tmp/ld.py 2>/dev/null
sha256sum /tmp/ld.py 2>/dev/null
file /tmp/ld.py 2>/dev/null

# Parent-child process review if audit logs exist
grep -R -n -E 'ld.py|nohup python3|sfrclak|6202033' /var/log 2>/dev/null

If the host is a CI runner, also inspect environment variable exposure, /proc histories if captured, and any artifact upload logs that may reveal credential theft paths.

Step 5: Check Network IOCs

Network evidence helps answer whether the first stage made it to the second stage.

# Check DNS cache and connections
# Linux
ss -tnp | grep "142.11.206.73\|8000"
grep -r "sfrclak" /var/log/ 2>/dev/null

# macOS
lsof -i :8000 | grep -i "sfrclak\|142.11"

Add more practical checks for common environments.

# Linux and macOS if lsof is available
lsof -nPi | grep -E '142\.11\.206\.73|:8000'

# Curl, proxy, and resolver logs where present
grep -R -n -E 'sfrclak|142\.11\.206\.73|6202033' /var/log ~/.npm/_logs ~/.yarn ~/Library/Logs 2>/dev/null

# Search EDR or netflow exports downloaded locally
find . -type f | xargs grep -n -E 'sfrclak|142\.11\.206\.73|6202033' 2>/dev/null

If you operate DNS logging, proxy logging, firewall telemetry, or netflow, query those sources centrally. A single DNS lookup for sfrclak[.]com from a build host during the exposure window is highly material.

Step 6: Check for Hash Matches

If you have EDR, an artifact repository, or malware scanning pipelines, search for the SHA256s published by Google.

  • fcb81618bb15edfdedfb638b4c08a2af9cac9ecfa551af135a8402bf980375cf for the Linux Python RAT
  • 92ff08773995ebc8d55ec4b8e1a225d0d1e51efa4ef88b8849d0071230c9645a for the macOS native binary
  • 617b67a8e1210e4fc87c92d1d1da45a2f311c08d26e89b12307cf583c900d101 for the Windows stage
  • ed8560c1ac7ceb6983ba995124d5917dc1a00288912387a6389296637d5f815c for WAVESHAPER.V2
  • e10b1fa84f1d6481625f741b69892780140d4e0e7769e7491e5f4d894c2e0e09 for SILKBELL setup.js
  • f7d335205b8d7b20208fb3ef93ee6dc817905dc3ae0c10a0b164f4e7d07121cd for system.bat
  • 58401c195fe0a6204b42f5f90995ece5fab74ce7c69c67a24c61a057325af668 for plain-crypto-js-4.2.1.tgz1

If you find any of them on a workstation, cache, artifact mirror, or EDR platform, document the host, isolate it, and move to incident response.

Step 7: Decide Severity Correctly

Use this triage model:

  • Low confidence exposure: lockfile references bad Axios versions, but no proof of installation.
  • Confirmed install exposure: package manager logs, cache evidence, or plain-crypto-js@4.2.1 present.
  • Confirmed host compromise: on-disk IOC, network IOC, matching hash, or suspicious persistence/process artifacts.

Because the malicious logic ran automatically during install, confirmed installation on a connected host should be treated as potentially compromised until disproven.

What to Do If You Are Affected

Immediate Actions for the First Hour

  1. Downgrade Axios to known good versions: 1.14.0 or 0.30.3.12
  2. Clear package caches. For npm, run npm cache clean --force.
  3. Remove dependencies and reinstall from a clean lockfile: rm -rf node_modules && npm ci.
  4. Block sfrclak[.]com and 142.11.206.73 on DNS, proxy, firewall, and egress controls. Include port 8000.1
  5. Isolate any machine with confirmed install or IOC evidence from the network.

Freeze deployments that rely on Axios until you verify lockfiles and caches.

If You Find IOCs, Assume Full Host Compromise

If you find plain-crypto-js@4.2.1 plus install evidence, or any second-stage IOC, do not treat this as a simple dependency cleanup. Treat it as endpoint compromise.

Do this next:

  1. Rotate all credentials that existed on the machine. SSH keys, cloud tokens, npm tokens, CI secrets, database credentials, SSO refresh tokens, wallet material, everything.
  2. Review and revoke long-lived credentials first. Those are what attackers monetize.
  3. Check Windows Run keys, scheduled tasks, and startup folders.
  4. Check Linux systemd units, user services, crontabs, and shell profiles.
  5. Check macOS LaunchAgents, LaunchDaemons, login items, and unusual binaries in caches.
  6. Review CI logs for package installs during the attack window. Assume runner secrets are exposed if installs occurred.
  7. Rebuild affected systems from a known-good state where possible. Reimaging is often faster and safer than trying to prove cleanliness.

Example Linux persistence review:

systemctl list-units --type=service --all | grep -i -E 'python|node|ld|6202033'
systemctl --user list-units --type=service --all | grep -i -E 'python|node|ld|6202033'
crontab -l
sudo crontab -l
ls -la /etc/cron* ~/.config/systemd/user ~/.config/autostart 2>/dev/null

Example Windows follow-up:

Get-ScheduledTask | Where-Object { $_.TaskName -match 'Update|Microsoft|6202033|wt' }
Get-ChildItem "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup"

Long-Term Hardening

The lesson here is not just “pin Axios.” The lesson is that install-time script execution in package managers remains a high-leverage attack surface.

Priority hardening steps:

  1. Pin exact versions. Remove ^ and ~ from production dependency specs.
  2. Commit lockfiles and review them in code review.
  3. Use npm ci --ignore-scripts in CI wherever possible. If scripts are required, isolate and explicitly allow the ones you trust.
  4. Add supply chain monitoring such as Socket.dev, Snyk, or equivalent tooling that flags unexpected dependency insertions and lifecycle scripts.45
  5. Adopt npm Trusted Publishing with OIDC and avoid human-maintained publishing tokens where possible.2
  6. Sandbox developer environments and builds in containers or ephemeral VMs with constrained host access.
  7. Move secrets out of plaintext dotfiles and environment sprawl into an OS keychain or vault-backed workflow.1

If your developers still run broad npm install operations on their daily-driver workstation with cloud admin tokens and long-lived SSH keys loaded, this incident is your reminder to change that.

The Bigger Picture

This attack landed the same day the Claude Code source leak was dominating security discussions, which made for an unusually chaotic day in the developer security world. It also arrived during a week that included the backdooring of LiteLLM, another supply chain event with credential theft and post-compromise implications.16

The pattern is clear. Attackers are not just publishing typosquats and hoping for random installs. They are targeting trusted distribution paths, maintainer accounts, CI workflows, and package ecosystems with planning and patience.

The Axios compromise shows what a mature package ecosystem attack now looks like:

  • a hijacked maintainer account
  • an 18-hour staging step using a clean decoy package
  • a manifest-only dependency insertion instead of noisy source edits
  • prebuilt payloads for Windows, macOS, and Linux
  • self-deleting loader behavior designed to frustrate responders23

This is not a one-off anomaly. It is a template.

Full IOC Table

The following IOCs are drawn from Google’s published reporting.1

Network Indicators

IndicatorTypeNotes
142.11.206.73C2WAVESHAPER.V2
sfrclak[.]comC2WAVESHAPER.V2
hxxp://sfrclak[.]com:8000C2WAVESHAPER.V2
hxxp://sfrclak[.]com:8000/6202033C2WAVESHAPER.V2
23.254.167.216C2Suspected UNC1069 infrastructure

File Indicators

SHA256Family / ArtifactNotes
fcb81618bb15edfdedfb638b4c08a2af9cac9ecfa551af135a8402bf980375cfWAVESHAPER.V2Linux Python RAT
92ff08773995ebc8d55ec4b8e1a225d0d1e51efa4ef88b8849d0071230c9645aWAVESHAPER.V2macOS native binary
617b67a8e1210e4fc87c92d1d1da45a2f311c08d26e89b12307cf583c900d101WAVESHAPER.V2Windows Stage 1
ed8560c1ac7ceb6983ba995124d5917dc1a00288912387a6389296637d5f815cWAVESHAPER.V2N/A
e10b1fa84f1d6481625f741b69892780140d4e0e7769e7491e5f4d894c2e0e09SILKBELLsetup.js
f7d335205b8d7b20208fb3ef93ee6dc817905dc3ae0c10a0b164f4e7d07121cdN/Asystem.bat
58401c195fe0a6204b42f5f90995ece5fab74ce7c69c67a24c61a057325af668N/Aplain-crypto-js-4.2.1.tgz

Closing Take

The key point is operational. The attacker did not need to break your app. They only needed you to install a dependency on a machine that held something valuable.

If your environment resolved axios@1.14.1 or axios@0.30.4, verify that exposure now. Check lockfiles. Check caches. Check CI history. Check the host for IOCs. If you find them, rotate credentials and rebuild fast.

That is the difference between a supply chain incident you contain in a morning and one you spend the next month explaining.

Footnotes

Footnotes

  1. Austin Larsen et al., “North Korea-Nexus Threat Actor Compromises Widely Used Axios NPM Package in Supply Chain Attack,” Google Cloud Blog, April 1, 2026, https://cloud.google.com/blog/topics/threat-intelligence/north-korea-threat-actor-targets-axios-npm-package. 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

  2. Microsoft Threat Intelligence, “Mitigating the Axios npm Supply Chain Compromise,” Microsoft Security Blog, April 1, 2026, https://www.microsoft.com/en-us/security/blog/2026/04/01/mitigating-the-axios-npm-supply-chain-compromise/. 2 3 4 5 6 7 8 9 10 11 12

  3. Connor Jones, “Supply Chain Blast: Top npm Package Backdoored to Drop Dirty RAT on Dev Machines,” The Register, March 31, 2026, https://www.theregister.com/2026/03/31/axios_npm_backdoor_rat/. 2

  4. Ryan Naraine, “Axios npm Package Compromised to Deploy Malware,” Sophos News, April 1, 2026, https://www.sophos.com/en-us/blog/axios-npm-package-compromised-to-deploy-malware.

  5. Ravie Lakshmanan, “Google Attributes Axios npm Supply Chain Attack to North Korean Group UNC1069,” The Hacker News, April 1, 2026, https://thehackernews.com/2026/04/google-attributes-axios-npm-supply.html.

  6. Austin Larsen et al., “North Korea-Nexus Threat Actor Compromises Widely Used Axios NPM Package in Supply Chain Attack,” Google Cloud Blog, April 1, 2026, section “Outlook and Implications,” https://cloud.google.com/blog/topics/threat-intelligence/north-korea-threat-actor-targets-axios-npm-package.