Mini Shai-Hulud 活动:分析大规模 npm 供应链攻击
npm 生态系统再次成为了大规模供应链攻击的受害者。在 2026 年 5 月 19 日的一次快速、自动化的爆发中,属于用户 atool 的一个被盗账号被用于在 317 个不同的包中发布 637 个恶意版本。其影响规模令人震惊,受影响的包如 size-sensor 和 echarts-for-react 每月拥有数百万次下载。
这场名为 "Mini Shai-Hulud" 的活动并非简单的凭据窃取工具。它是一个经过高度工程化设计的工具包,旨在实现深度持久化、横向移动,并利用现代开发者的工作流,包括 AI 编程代理和 CI/CD 流水线。
该攻击与之前针对 SAP 的攻击具有直接的架构血缘关系,使用了相同的混淆模式和扫描器架构。
攻击向量:账号被盗与分发
此次泄露始于 atool npm 账号的被盗。在 22 分钟的时间窗口内,攻击者发布了两波恶意更新。由于许多项目使用 semver 范围(例如 ^3.0.6),即使许多包的 latest 标签保持不变,这些恶意版本也会在全新安装时被自动解析并安装。
为了确保执行,攻击者采用了两种主要的触发机制:
- Preinstall Hooks: 每个被盗版本都添加了一个
preinstall脚本(bun run index.js),需要 Bun 运行时来执行初始负载。 - Imposter Commits: 在一个复杂的手段中,630 个版本注入了一个
optionalDependencies条目,指向合法antvis/G2GitHub 仓库中的孤儿提交(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) 通道。这使得流量可以与合法的开发者活动混入在一起。
外泄流水线
- 验证: 负载会验证窃取的 GitHub tokens 并检查
repo权限。 - 仓库-创建: 它在被盗账号下使用 Dune 主题的命名约定(例如
harkonnen-melange-742)创建一个公开仓库。描述是一个反转字符串:"Shai-Hulud: Here We Go Again"。 - 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.json或pnpm-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脚本运行之前是否具有威胁情报情报。