大约在 UTC 时间 2026年8月4日 09:00,攻击者控制了 Jared Wray 的 GitHub 账户 (jaredwray) 。利用该访问权限,他们直接将恶意代码推送到了 keyv 仓库的 main 分支,并立即在 keyv 和 cacheable 整个系列包中发布了新版本 。
初始的种子包涉及这两个命名空间下的 11 个软件包,包括:keyv、cacheable-request、cache-manager、@cacheable/utils、flat-cache、file-entry-cache 等 。多家安全研究机构,如 Aikido Security、StepSecurity(将此蠕虫追踪为 "ChainDrop")、Socket 和 Chainguard,在事件发生的最初几小时内都独立确认了这一入侵事件 。
每个被投毒的软件包都遵循相同的感染模式:两个新文件(setup.mjs 和 Math_Symbol.js)以及一个被修改的 package.json 文件,其中添加了 "preinstall": "node setup.mjs" 脚本 。
当任何开发者或 CI 系统运行 npm install 时,setup.mjs 这个 投放器 会在安装完成前自动执行。它的任务是从 GitHub Releases 下载一个合法的 Bun JavaScript 运行时二进制文件,然后用它来执行混淆过的第二阶段载荷 (Math_Symbol.js,大约 710-728 KB) 。
微软威胁情报确认,该载荷是 Mini Shai-Hulud 的一个变种 。它可以从被感染的环境中窃取大量凭证和机密信息 :
此次攻击的危险之处在于其自我复制能力。在窃取到被感染环境中的 npm 发布令牌和 GitHub PAT 后,蠕虫利用这些权限,发布更多恶意版本,而这些版本属于其他毫无关联的维护者 。
感染范围迅速扩大:
蠕虫跨越了命名空间的边界——它不仅局限于最初被攻陷的 keyv/cacheable 系列。它“跳转”到了其他组织拥有的软件包上,这些组织包括 Deliveroo、Ornikar、OneReach、Picsart、Qlik 和 ServiceTitan 等 。
窃取的凭证被 外泄到攻击者控制的 GitHub 仓库——蠕虫要么创建一个新仓库,要么为此目的使用一个专用的仓库 。载荷中内置了多种冗余的外泄渠道 ,使得即使某个渠道被关闭,攻击也能持续。
多家公司的安全研究人员敦促团队,凡是接触过受影响软件包的系统,都应被视为完全失陷。以下是各方一致建议的补救步骤 :
使用 npm/yarn/pnpm 的 overrides 机制(例如在 package.json 中设置 "overrides")来防止意外重新安装被投毒的版本 。检查锁文件(package-lock.json, yarn.lock, pnpm-lock.yaml)中是否包含任何受影响的软件包版本,包括传递性依赖 。
对于在受影响版本上运行过 npm install 的机器,不要仅仅删除恶意文件 。必须假设该主机上存在的所有机密信息都已泄露。
撤销并重新生成所有可能暴露的凭证 :
一个关键细节:恶意软件有时会设置 GitHub 工作流监控器,这些监控器能在新令牌被创建的瞬间就将其再次暴露 。研究人员建议,在轮换凭证前,先禁用或移除任何监控服务 。
清除开发者机器和 CI/CD 运行器上的 npm/pnpm/yarn 缓存 以及 Docker 构建缓存 。从头重新构建所有制品,以防止受污染的依赖项残留在构建缓存或 Docker 层中 。
查找新创建的仓库或未授权的工作流 。检查蠕虫可能创建的持久化遗留物,例如 .claude/settings.json 或 .vscode/tasks.json 。
2026年8月4日的 Shai-Hulud 蠕虫攻击是 JavaScript 生态系统安全的一个分水岭。它表明,一个被攻陷的维护者账户——即使没有对大多数受影响软件包的直接访问权限——也能级联成一个蠕虫,在数小时内投毒超过一千个软件包。此次攻击利用合法的 Bun 运行时来执行载荷、通过窃取的令牌自我传播,并拥有冗余的外泄渠道,使其比之前的供应链攻击更加复杂 。
对于工程和安全团队而言,这一事件凸显了锁定依赖项、尽可能禁用 pre/post-install 脚本、监控 GitHub 上的异常活动,以及维护供应链安全事件响应预案的重要性。