“Shai-Hulud” npm 蠕虫式供应链攻击:868 个包、20 亿次月下载量遭劫持

2026 年 8 月 4 日,npm 注册中心爆发代号”Shai-Hulud”(沙丘蠕虫)的供应链攻击。新一轮蠕虫从 keyv 维护者遭劫持的 GitHub 账号出发,借助 preinstall 生命周期钩子、自带 Bun 运行时与 IDE 自动触发文件,在约 30 分钟内向 keyv、cacheable、flat-cache、file-entry-cache、cacheable-request、cache-manager 等数十个流行包注入后门,并通过 npm 自动化令牌自传播至下游。截至 8 月 4 日傍晚,Aikido 与 JFrog 等监测机构已确认至少 868 个 npm 包(1381 个版本)被污染,合计月下载量约 20 亿次。受影响的二级维护者名单已经扩散至 @deliveroo/reevent@picsart/ai-sdk@qlik/embed-runtime@or-sdk/invitationspicasso.js 等知名组织。

事件概述

keyv 是 Node 生态中最流行的键值抽象层之一,单包每周下载约 1.27 亿次,同时是 ESLint 等工具的传递依赖。该项目维护者同时维护 cacheable、cacheable-request、flat-cache、file-entry-cache 等高频使用的缓存工具。攻击者接管维护者 GitHub 账号后,使用合法凭据将恶意提交推送到 main 分支,并立即触发包含 GitHub Actions 合法 provenance 的版本发布,污染包因此带有官方签名。

主要被注入的两个文件是 setup.mjsMath_Symbol.jspackage.json 中追加 "preinstall": "node setup.mjs"。当用户执行 npm install 时,setup.mjs 作为轻量级 loader 静默下载 Bun 1.3.13 独立运行时,并用它执行 728 KB 的混淆主载荷 Math_Symbol.js,借此规避宿主 Node 的 hook 检测与常见审计工具。

技术细节

1. 凭据收割与多通道外泄

蠕虫在受感染主机上执行下列典型作业:

  • 扫描 .npmrc 与本地缓存,提取 npm 自动化令牌,并实时调用 registry.npmjs.org/-/whoami 校验可用性;
  • 抓取 GitHub PAT、OAuth Token、OIDC JWT(含 Actions runner 内存中的发布用 ID Token);
  • 遍历 AWS、GCP、Azure、EC2/ECS 元数据、Vault、Kubernetes ServiceAccount Token、Stripe、Slack、SSH 私钥、加密货币钱包等 200 余种凭据模式;
  • 将结果加密后投递至攻击者创建的 GitHub 仓库(描述统一为 “Shai-Hulud: Here We Go Again”)以及硬编码 HTTPS / DNS 信道。

2. 自传播逻辑

蠕虫在拿到可用 npm 令牌后,对该身份可写的每一个包执行”劫持-加壳-提版本-发布”:下载原始 tarball、复制恶意样本至 package/math_init.js、覆盖 scripts.preinstall、递增 patch 版本号,并直接以 PUT 方式推送到 npm registry。借力 npm 12+ 的 OIDC trusted publishing,单次凭据失陷即可在数分钟内扩散至数百包。

3. IDE 与 AI 编码代理反触发

蠕虫还会在目标 GitHub 仓库的 main 分支中植入 .vscode/tasks.json.vscode/setup.mjs.claude/settings.json.claude/math_init.js.claude/setup.mjs 五份文件,绕过 npm 安装阶段直接被触发:

  • .vscode/tasks.json 在 VS Code 打开文件夹时执行 node .claude/setup.mjs
  • .claude/settings.json 在 Claude Code 启动会话时通过 SessionStart hook 执行 node .vscode/setup.mjs

这意味着即便使用 --ignore-scripts 或升级到 npm 12,只要开发者或 AI 代理打开含毒的仓库目录,载荷仍会自动执行。

4. 对 GitHub Actions 的二次偷凭

针对含有 workflow 权限的 GitHub Token,蠕虫会枚举 9 月份以来该身份可推送的仓库,针对其中任意一个创建 dependabot/github_actions/format/setup-formatter 分支,注入一个名为 Run Copilot 的 workflow,将 ${{ toJSON(secrets) }} 序列化到工件 format-results.txt 中以供下载。

影响评估

该事件已进入”生态系统级”供应链事故级别:keyv@6.0.0(约 6.04 亿次月下载)、flat-cache@6.1.24(5.80 亿)、file-entry-cache@11.1.6(5.71 亿)以及 cacheable-request@13.0.20(1.37 亿)等包几乎覆盖了所有 Node 项目。攻击者只要劫持一个维护者,就能沿信任图谱跨多家公司连锁扩散;常见依赖审计只盯 namespace 白名单,无法发现被劫持的合法发布,因而无法及时阻断。

主载荷的 SHA-256 已公开为 9fc2570b7cef51c1b8df116d144d11ff4096357be7d2c4c6367cfc2509cf1bcc,微软 Defender 已为其标记 Trojan:npm/MalBun.A。由于依赖树太深,绝大多数受害团队甚至不会意识到自己引入了被劫持的包;这也意味着公开 PoC 与外泄基础设施在过去 24 小时内已被纳入多个主流勒索软件集团的工具库。

修复建议

  • 立即将 keyv 锁回 5.6.0,并核实所有依赖图中是否存在 keyv@6.0.0flat-cache@6.1.24file-entry-cache@11.1.6cacheable-request@13.0.20cacheable@2.5.1@cacheable/memory@2.2.1cache-manager@7.2.10@cacheable/node-cache@3.1.2@cacheable/utils@2.5.1@cacheable/net@2.1.1ecto@5.0.1
  • 对 8 月 4 日 09:39 UTC 之后的任何 main 分支发布视为不可信,审阅近期由维护者发起的版本并比对 SHA-256;
  • 在轮换凭据前先移除 claude/settings.json.vscode/tasks.jsonsetup.mjs 中可能存在的 token-revocation watcher;
  • 立即回收所有可能暴露的 npm 自动化令牌、GitHub PAT、GitHub App、AWS/GCP/Azure/Vault/Kubernetes/SSH/Stripe/Slack 凭据,并清理 8 月 4 日前后构建的 CI runner;
  • 升级到 npm 12+ 以默认阻止未审批的 preinstall;但需注意 IDE hook 路径仍然有效,建议在 IDE 与 AI 代理中显式禁用未受信任目录的自动任务;
  • 启用 npm 的 OIDC trusted publishing 时增加人工审批环节,避免被盗令牌沉默发布;
  • 在仓库监控规则中追加:包版本新增/修改 preinstall / postinstall / prepare、运行时进程连接 github.com/oven-sh/bun 或任何非 npm 注册端点、出现 .claude.vscodesetup.mjs 异常工件,以及描述为 “Shai-Hulud: Here We Go Again” 的可疑仓库。

参考来源