深度剖析 AsyncAPI npm 供应链攻击及导入时载荷投递

深度剖析 AsyncAPI npm 供应链攻击及导入时载荷投递

导语:2026 年 7 月 14 日,微软威胁情报团队披露了一起针对 @asyncapi npm 组织的高度协调供应链攻击,攻击者在约 90 分钟内向四个 AsyncAPI 包名的五个版本注入了在导入时(而非安装时)触发的恶意加载器。

2026 年 7 月 14 日,微软威胁情报团队发现针对 @asyncapi npm 组织的一次协调性供应链攻击。@asyncapi 是广泛用于 AsyncAPI 规范及代码生成的包集合。攻击者在约 90 分钟内重新发布了横跨四个包名的五个版本,每个版本均注入了相同的恶意加载器,具体包括:@asyncapi/specs(含 6.11.2-alpha.1 预发布版与 6.11.2 稳定版)、@asyncapi/generator@3.3.1、@asyncapi/generator-components@0.7.1,以及 @asyncapi/generator-helpers@1.1.1。

由于 @asyncapi/specs 是众多 AsyncAPI 工具包的传递性依赖,凡是在暴露窗口内解析并导入受影响版本的开发工作站、CI/CD 流水线、容器构建或生产服务均受到影响。与常见的 postinstall 钩子型供应链攻击模式不同,本次攻击在模块加载(import/require)时执行。当任何下游构建或应用导入被投毒的包时,注入的代码块会立即运行。由于触发点位于 import 而非安装脚本,常见的 npm install –ignore-scripts 缓解措施无法生效。第二阶段解密并评估一个 Miasma 模块化运行时,具备活跃的命令与控制(C2)通道、持久化能力以及去中心化的回退通道。本实例中虽禁用了凭据收割、横向传播及其他高风险模块,但这些模块可通过持久化机制被启用。

微软 Defender 防病毒将恶意构件检测并拦截为 Trojan:JS/MiasmStealer.SC 和 Trojan:Script/Supychain.A。微软 Defender for Endpoint 针对可疑的分离式 Node.js 进程派生、IPFS 获取及持久化活动提供行为检测覆盖。各组织应立即移除全部五个受影响版本,清空 npm 与 Yarn 缓存,在 NodeJS 伪装目录下搜寻 sync.js,阻断至 85.137.53[.]71 的 8080、8081 和 8091 端口的出站连接,并轮换所有可从导入过被攻陷包的环境中访问到的凭据。文章后续章节提供了详细的威胁狩猎查询、攻击指示器(IOC)及缓解指导。

此次入侵的起点是针对 asyncapi/generator 仓库的一次 pwn 请求。一个配置错误的 GitHub Actions 工作流(pull_request_target)执行了攻击者控制的拉取请求(PR)代码,泄露了 asyncapi-bot 个人访问令牌(PAT),并使未经授权的推送能够发布到自动发布分支。随后合法的 GitHub Actions OpenID Connect(OIDC)发布工作流以自动身份 npm-oidc-no-reply@github[.]com 发布被投毒的包,生成了携带有效 provenance 签名、但源自未授权提交构件。

整个攻击活动按六个阶段推进(图 1 所示)。Miasma 运行时提供加密的引导程序、持久化、C2 通信、数据回传路径,以及通过 Nostr、Ethereum、BitTorrent DHT、libp2p 和 IPFS 实现的弹性发现能力。另有六个能力模块(凭据收割、加密外泄、供应链传播、变形生成、AI 工具投毒以及沙箱逃逸)虽已实现,但在此次构建中被禁用。

攻击链始于针对 asyncapi/generator 仓库文档预览自动化的一条恶意拉取请求。该请求以 PR #2155 形式提交,携带攻击者控制的提交 47be388,时间戳为 7 月 14 日 05:08:58 UTC。关联的 Docs Preview(Netlify)工作流于 05:11:05 UTC 启动。尽管 PR 和源 fork 之后被删除,工作流运行记录仍然可查。

原文配图

PR #2155 针对的是 manual-netlify-preview.yml,该文件同时做出了两个不安全的选择:使用 pull_request_target 将任务置于基础仓库的安全上下文中,并检出拉取请求中不受信任的头提交。此次运行拥有一个权限广泛的 GITHUB_TOKEN,检出凭据在本地 Git 配置中持续保留至任务后清理(即 actions/checkout 的默认行为),并且多个步骤引用了仓库密钥。

提交的 MDX 中包含的代码旨在从 rentry[.]co/elzotebo999 获取 JavaScript 并求值执行响应。公开日志确认该恶意提交确实被特权工作流处理,但并未显示 rentry[.]co 的 Web 请求是否成功,也未显示凭据是否被窃取。后续的推送记录将 asyncapi-bot 标识为已认证操作者。这些记录共同表明易受攻击的工作流在机器人认证的推送之前已经运行,但并未直接证明凭据是如何获取的。

该工作流本身的弱点早在入侵之前就已被指出。4 月 29 日的一项概念验证研究了不受信任的拉取请求内容是否能够在特权文档预览工作流中执行。5 月 17 日的一项提案试图将不受信任的构建活动与访问仓库密钥的步骤分离,但在事件发生时该提案仍在评审中。

一旦攻击者能够以 asyncapi-bot 身份推送提交,便无需攻陷 npm 或另行搭建发布通道。攻击者可以直接借用项目的正常发布路径,让受信的流水线完成分发。提交 3eab3ec 的时间戳为 06:58:42 UTC,而一条尚存的推送触发工作流于 07:05:42 UTC 启动。其提交信息"fix: test release workflow on next"与发布工作流的提交信息条件匹配。随后合法的 release-with-changesets.yml 工作流在约 07:10 UTC 发布了三个被投毒的包。

随后一起紧密关联的入侵影响了 asyncapi/spec-json-schemas。恶意谱系首先在 07:56 至 08:04 UTC 之间触发了 alpha 分支上的工作流。同一恶意提交随后在约 08:14 UTC 被推送到 master 分支,紧接着在 08:28 UTC 出现一个子提交。合法的 if-nodejs-release.yml 工作流于 08:06 UTC 发布 @asyncapi/specs@6.11.2-alpha.1,并于 08:30 UTC 发布 @asyncapi/specs@6.11.2。

原文配图

全部五个恶意版本均通过 npm 受信发布(GitHub OIDC)发布,并携带有效的 provenance 证明。这些证明准确地识别了生成包的合法仓库、提交和工作流,尽管触发它们的提交本身是未经授权的。

载荷分多个阶段执行,每个阶段都旨在提升规避能力并确保稳定运行。Stage 0 通过不声明任何 npm 生命周期钩子来建立隐蔽性。Stage 1 在 require 时执行加载器,并派生一个隐藏的子进程。Stage 1b 对 IPFS 获取逻辑进行去混淆,并下载 sync.js。Stage 2 通过三层密码学层解密约 8.2 MB 的加密包。Stage 3 初始化具备 C2、持久化以及去中心化回退通道的完整 Miasma 模块化运行时。

不包含生命周期钩子是攻击者有意为之的规避手段。专注于 preinstall/postinstall 审计的安全工具不会对这些包告警。所有受影响包的 package.json 中均未声明 preinstall、install 或 postinstall 钩子。这一做法绕过了聚焦于钩子的扫描器,使得导入时执行成为真正的触发路径。

加载器在任意应用导入被攻陷模块的瞬间即执行;除了依赖解析外,无需任何用户操作。攻击者将相同的引导模式放置在每个包的导出入口路径中,因此正常的应用启动会自动触发执行。

内层载荷揭示了硬编码的 IPFS 内容标识符和操作系统感知的投放逻辑。该中间阶段在运行时重建传输例程,因此更大的第二阶段永远不会在发布的包中以明文形式出现。

尽管表面上具有密码学复杂性,但整个解密链使用的都是静态嵌入的密钥材料,这意味着无需执行即可离线恢复该运行时。这种分层设计主要增加了分析人员的成本;解开包内全部所需密钥都已随加载器一同发布。


来源信息

来源:Microsoft Security Blog

原文链接:https://www.microsoft.com/en-us/security/blog/2026/07/15/unpacking-asyncapi-npm-supply-chain-compromise-import-time-payload-delivery/

作者:Microsoft Security Research, Ravikant Tiwari, Sagar Patil, Suriyaraj Natarajan and Arvind Gowda