Mini Shai-Hulud 活动:分析大规模 npm 供应链攻击

npm 生态系统再次成为了大规模供应链攻击的受害者。在 2026 年 5 月 19 日的一次快速、自动化的爆发中,属于用户 atool 的一个被盗账号被用于在 317 个不同的包中发布 637 个恶意版本。其影响规模令人震惊,受影响的包如 size-sensorecharts-for-react 每月拥有数百万次下载。

这场名为 "Mini Shai-Hulud" 的活动并非简单的凭据窃取工具。它是一个经过高度工程化设计的工具包,旨在实现深度持久化、横向移动,并利用现代开发者的工作流,包括 AI 编程代理和 CI/CD 流水线。

该攻击与之前针对 SAP 的攻击具有直接的架构血缘关系,使用了相同的混淆模式和扫描器架构。

攻击向量:账号被盗与分发

此次泄露始于 atool npm 账号的被盗。在 22 分钟的时间窗口内,攻击者发布了两波恶意更新。由于许多项目使用 semver 范围(例如 ^3.0.6),即使许多包的 latest 标签保持不变,这些恶意版本也会在全新安装时被自动解析并安装。

为了确保执行,攻击者采用了两种主要的触发机制:

  1. Preinstall Hooks: 每个被盗版本都添加了一个 preinstall 脚本(bun run index.js),需要 Bun 运行时来执行初始负载。
  2. Imposter Commits: 在一个复杂的手段中,630 个版本注入了一个 optionalDependencies 条目,指向合法 antvis/G2 GitHub 仓库中的孤儿提交(orphan commits)。通过利用 GitHub 的 fork 对象共享机制,攻击者无需获得目标仓库的写入权限即可托管第二份负载副本。这创建了一个冗余的执行路径,即使在禁用 preinstall 钩子时也能生存。

恶意负载深度解析

该负载是一个 498KB 的混淆 Bun bundle,作为一个全面的侦察和外泄引擎运行。它利用了一个具有 100KB 刷新阈值的扫描器架构,以便在传输前批量处理窃取的数据。

全面的凭据收割

该恶意软件针对多个环境中的详尽凭据列表进行攻击:

  • 云基础设施: 它遍历完整的 AWS 凭据链,包括环境变量、配置文件、EC2 Instance Metadata Service (IMDSv2) 和 ECS 容器元数据。它还探测 HashiCorp Vault 和 Kubernetes service account tokens。
  • 开发者令牌: 正则表达式集针对 GitHub PATs、npm tokens、GCP service accounts、Azure credentials、Stripe keys、Slack tokens 和 SSH keys。
  • 数据库与身份验证: 它扫描 MongoDB、MySQL、PostgreSQL 和 Redis 的连接字符串,以及 Docker 身份验证令牌。

Docker 容器逃逸

意识到许多开发者使用容器进行隔离,该负载会积极尝试逃逸。如果它检测到 Docker socket(例如 /var/run/docker.sock),它会尝试创建一个具有主机文件系统绑定挂载的特权容器,从而有效地授予攻击者对主机机器的完全访问权限。

通过 GitHub "Dead-Drop" 进行外泄

攻击者并没有连接到可疑的外部服务器,而是使用 GitHub API 作为命令与控制 (C2) 通道。这使得流量可以与合法的开发者活动混入在一起。

外泄流水线

  1. 验证: 负载会验证窃取的 GitHub tokens 并检查 repo 权限。
  2. 仓库-创建: 它在被盗账号下使用 Dune 主题的命名约定(例如 harkonnen-melange-742)创建一个公开仓库。描述是一个反转字符串:"Shai-Hulud: Here We Go Again"。
  3. Git 对象上传: 窃取的数据被直接作为 Git blobs、trees 和 commits 提交到这些仓库中,有效地将 GitHub 作为窃取秘密的存储后端。

kitty-monitor 后门

为了实现长期持久化,该负载会安装一个名为 kitty-monitor 的 systemd service (Linux) 或 LaunchAgent (macOS) 服务。这个 Python 守护进程会每小时轮询 GitHub Search API,查找包含关键词 firedalazer 的提交。如果它发现一个具有有效 RSA-PSS 签名的提交,它就会从指定的 URL 下载并执行任意 Python 代码。

针对现代 AI Stack 堆栈

此次活动中最令人担忧的方面之一是明确针对 AI 编程代理。恶意软件向 .claude/settings.json (针对 Claude Code) 和 .vscode/tasks.json (针对 Codex 和 VS Code) 注入 SessionStart 钩子。

当开发者开始一个 AI 会话或打开一个项目文件夹时,这些钩子会触发一个 Bun bootstrapper (setup.mjs),该脚本会下载 Bun 运行时并重新执行主负载。

这确保了感染在开发者与 AI 工具交互时能够持续存在并重新激活。

CI/CD 与 Sigstore 滥用

在 CI 环境中,攻击者针对 GitHub Actions 来窃取秘密并伪造来源证明 (provenance):

  • Workflow Injection: 负载注入了一个名为 "Run Copilot" 的恶意 workflow (.github/workflows/codeql.yml),该 workflow 会将所有仓库秘密 dump 到一个 artifact,攻击者随后会下载并删除该 artifact 以隐藏踪迹。
  • OIDC Token 交换: 它将 GitHub Actions OIDC tokens 交换为 npm publish tokens,允许攻击者使用流水线自身的身份进行发布新的恶意包。
  • Sigstore Forgery: 使用窃取的 OIDC tokens,攻击者可以通过 Fulcio 和 Rekor 进行 artifact 签名,从而创建具有伪造来源证明的、合法签名的 artifact,从而通过大多数安全检查。

社区观点与缓解措施

此次攻击引发了开发者社区关于 npm 信任模型固有不安全性的重大辩论。许多用户对更新依赖项的担忧日益增长,一些人建议转向使用 "jailBuilds" 或像 Podman 这样的 rootless VM 引擎来防止容器逃逸。

"Node 是我们这个时代的 Visual Basic... 所有开发的便捷性都是以根权限获取的便捷性为代价的支付。"

如何保护您的环境

如果您怀疑您的环境已被入侵,请采取以下步骤:

  • 审计 Lockfiles: 检查 package-lock.jsonpnpm-lock.json 是否包含在 2026-05-19 发布于受影响包的任何版本。n
  • 核心资产轮换: 立即轮换所有 AWS keys、GitHub PATs、npm tokens、数据库密码,以及您的构建环境可以访问的的所有凭据。
  • 检查持久化机制: 搜索 kitty-monitor 服务、.claude/settings.json 钩子以及调用 setup.mjs.vscode/tasks.json 条目。
  • 实施冷却期: 使用类似 npm config set min-release-age=2 的设置,以避免安装最近几天内发布的包,从而为安全研究人员检测恶意爆发提供窗口期。
  • 使用安装代理: 工具如 pmg (Package Manager Guard) 可以评估包在 preinstall 脚本运行之前是否具有威胁情报情报。

Sources