硬核拆解axios 供应链投毒和对策

这次供应链攻击极具“反共识”特征:axios 的源码基本未被改动,攻击者只在发布到 npm 的两个版本里“动了清单文件(package.json)的一行/一处依赖”,塞进一个从未在代码中被引用的新依赖 plain-crypto-js,利用安装期脚本(postinstall)静默执行,从而把恶意载荷送进开发者电脑和 CI 环境。

硬核拆解axios 供应链投毒和对策

事件概述

这起事件的关键,不是“某个库出现了高危代码漏洞”,而是:攻击者绕开代码层面,直接打穿“信任层面”。被短暂投毒的并非某个小众依赖,而是 JavaScript 生态里几乎“基础设施级别”的 axios——公开情报与多家安全厂商的复盘都强调其周下载量在“上亿次级别”,并且拥有庞大的下游依赖(生态扩散半径异常惊人)。

从技术呈现看,这次供应链攻击极具“反共识”特征:axios 的源码基本未被改动,攻击者只在发布到 npm 的两个版本里“动了清单文件(package.json)的一行/一处依赖”,塞进一个从未在代码中被引用的新依赖 plain-crypto-js,利用安装期脚本(postinstall)静默执行,从而把恶意载荷送进开发者电脑和 CI 环境。

在归因层面,Google Threat Intelligence Group 将活动归因到朝鲜关联威胁集群 UNC1069;与此同时,Microsoft 威胁情报团队则将这次基础设施与活动归因到其命名体系里的 Sapphire Sleet,并明确指出该命名与其他厂商对 UNC1069 等的跟踪存在重叠(即同一或高度相关的威胁行为体在不同厂商体系下名称不同)。

一个容易混淆但在新闻报道里被刻意澄清的点:被攻击的是 npm 上的 axios 软件包,不是新闻机构Axios Media本身。

经验证的时间线与三小时传播窗口

多份复盘给出了可交叉验证的关键时间线(均为 UTC):

攻击者在发布前“铺垫”了一个看似正常的依赖版本:plain-crypto-js@4.2.0 于 3 月 30 日 05:57 发布;之后在 3 月 31 日 00:21 发布被投毒的 axios@1.14.1,并在约 01:00 左右发布同样携带投毒依赖的 axios@0.30.4

关于“污染窗口”结束时间,不同来源给出的边界略有差异,但共同结论是“约三小时量级”:
官方事后说明给出的窗口是 00:21–03:15(恶意版本从 npm 被移除);Google 的公告提到 00:21–03:20;Datadog Security Labs 的分析写到约 00:21–03:25;StepSecurity 则按注册表元数据推断 03:15 左右完成 unpublish,并进一步估算两个被投毒版本分别在架时间约 2 小时 53 分与 2 小时 15 分。

这类差异本身就是工程启示:在依赖分发系统中,“精确到秒的可审计撤包时间”未必总能从公开 API 获得,组织侧更应依赖多源证据(注册表元数据、CI 日志、网络出站、EDR 告警)去界定自身是否在窗口期内执行过安装/更新。

对你提供的两条“关键数据”做一次校验说明:
其一,“约 3 小时 19 分钟”的窗口,在目前公开的官方与主流厂商复盘里并非主流写法,更一致的区间集中在 00:21–03:15/03:20/03:25 这一“约三小时”量级;
其二,“1900 个 TypeScript 文件”这一指标,并未出现在主流技术复盘对 axios 投毒包的描述中;相反,多家分析都强调“在 axios 包内的变更极小、文件级 diff 几乎只有 package.json”,并给出诸如“85 个库源文件位级相同”“86 个文件中仅一处差异”等更贴近事实的描述。

攻击链解析:从假公司社交工程到真依赖投毒

Jason Saayman (axios维护者)在事后说明中确认:攻击者通过高度定制的社会工程学和远控木马(RAT)先攻陷其个人设备,从而窃取发布凭证并完成投毒发布。

真正让这起事件“细思极恐”的,是其“认知层投送”的完成度:安全新闻与Simon Willison 的整理转述显示,攻击者并非简单冒名顶替,而是营造了一个“看起来可长期运营的公司环境”——以“知名公司创始人”身份接触目标、克隆对方形象与公司外观,再把目标拉入一个视觉与组织结构都高度拟真的协作空间(包含用来分享 LinkedIn 动态的频道、团队成员画像等),随后再通过会议工具制造“系统需要更新否则会影响会议/音视频”的场景压力,诱导受害者安装“更新”,进而落地 RAT。

把社会工程学与“可扩散的供应链投毒”连接起来的关键节点,是“发布凭证被盗”。一旦攻击者拿到维护者的 npm 凭证,就可以在短时间内发布“看起来完全合法”的新版本。

从纯技术链条看,投毒版本的“载荷投递”利用了安装期脚本机制:
受影响版本把 plain-crypto-js@4.2.1 作为运行时依赖引入,而该依赖通过 postinstall 触发 setup.js 的静默执行。setup.js 随后连接攻击者 C2(公开 IOC 指向 sfrclak[.]com 与特定 IP/端口),并通过不同 POST body 标识来区分 macOS、Windows、Linux,从同一路径下发平台特定二阶段载荷。

为了降低事后排障和取证效率,多家复盘都描述了反取证动作:执行后删除/替换关键文件,甚至把看似“干净”的清单文件留在 node_modules 中,以掩盖 postinstall 曾经存在过。

为什么这是认知层漏洞:信任链条如何被系统性利用

“这不是技术漏洞,而是人性弱点的系统性利用”这句话并非修辞夸张,而是可被复盘材料直接支撑:多个技术分析都证明被投毒的 axios 版本“代码逻辑不变”,变化集中在依赖清单;也就是说,传统的“代码审计思维”(盯业务逻辑、盯漏洞函数、盯 diff 行数)在这里几乎失效。

更深层的工程问题在于:生态对“发布过程”的验证普遍缺位。Elastic Security Labs 与 Datadog 等指出,正常版本使用基于 GitHub Actions 的 OIDC 可信发布与 SLSA provenance 证明,而恶意版本发布方式转为“直接 CLI 发布、无 provenance”。这提供了一个几乎“二元可判定”的信号:只要你的构建/部署链路强制校验 provenance,就能在依赖进入组织前将其拦下。

这点在开发者社区讨论中也被反复强调:在 Hacker News 的相关串里,有评论直指“缺失 OIDC provenance”是一个可自动化的二元检查,但生态里几乎没人默认去做,这使得“锁文件/依赖扫描”给人一种“供应链已解决”的错觉。

AI 的作用,则主要体现在“把社工从手艺活变成工业化流程”。就 UNC1069 的既有活动而言,Mandiant 在今年二月的报告里描述过与其相关的“AI 辅助社交工程”与会议仿冒(包含受害者报告的 AI 生成视频欺骗,但报告也坦诚其无法独立取证验证该次是否确使用 AI 模型)。这一类战术与本次“会议中诱导安装修复/更新”的手法高度同构。

从更宏观的趋势看,Federal Bureau of Investigation 早在 2024 年就公开提示过:犯罪分子正在利用 AI 强化钓鱼/社工与语音视频克隆骗局,要求个人与企业提高警惕。 这意味着“看上去像你老板/像你客户/像你合作方的人”在工程上应被视为高风险输入,而不是可靠身份凭证。

工程防御:把防线从代码层扩展到认知层

对企业与团队而言,最关键的转变是:把“依赖安装”视作一次潜在的端点入侵事件,把“身份与流程验证”视作供应链安全的一部分,而不是合规附属品。

在紧急处置层面,官方事后说明与多家厂商建议高度一致:检查 lockfile 是否出现 axios@1.14.1/axios@0.30.4plain-crypto-js;若命中,则按“主机可能已失陷”处理,回退到安全版本并轮换凭证;同时排查网络侧 IOC(域名/IP/端口)与落地文件痕迹。

在依赖治理层面,这次事件特别暴露了“自动更新”的风险。Microsoft Threat Intelligence 明确建议通过移除 ^/~ 等写法来避免无感知升级,并可用 overrides 强制锁定传递依赖版本;同时在可行场景下启用 --ignore-scripts 或默认忽略安装脚本,降低 install-time 代码执行面。

在发布与溯源层面,axios 维护团队在事后改进计划中明确提出:采用不可变发布(immutable release setup)与 OIDC 流程发布,并改进整体安全姿态;这一方向与外部复盘强调的“用 provenance 驱动的门禁策略”是一致的。

在生态监测层面,Datadog 等提到“依赖冷却期(cooldown)”策略:对新发布或刚更新的版本,在自动化构建中延迟引入,给社区检测与情报同步留出时间窗口。这对“只存在三小时但危害巨大的”事件尤其有效。

最后也是最容易被低估的一层:认知与流程的零信任。IBM与Check Point 等都从不同角度指出生成式 AI 让社工更难检测、更易定制;而微软对 Sapphire Sleet 的描述也强调其常利用社交平台与会议仿冒手段进行初始接触。把这些拼在一起,工程上应形成明确“验证仪式”:会议里不安装任何东西、涉及凭证/令牌/权限提升必须跨渠道二次确认、关键操作引入双人复核(尤其是发布、换密钥、增加维护者权限)。