TL博士
14 年 2025 月 XNUMX 日,研究人员发现 沙伊胡鲁德,隐藏在里面的自我复制蠕虫 npm 包,将常规依赖项更新转变为全面更新 供应链攻击. 首次发现于 @ctrl/tinycolor 由 Daniel dos Santos Pereira 开发的软件包 Shai-Hulud 收集机密信息,通过 GitHub 代码库和工作流窃取机密,并使用窃取的凭证在注册表中重新发布自身。几天之内,受感染软件包的数量从几十个激增到数百个,证实了 沙伊胡鲁德 不仅仅是另一个木马,而是一种旨在自动在 npm 生态系统中传播的蠕虫。
影响: 任何安装公共 npm 包的开发人员或 CI 运行者都面临风险。
立即采取的行动: 阻止已知版本、切换到仅锁定文件安装、轮换 npm 和 GitHub 令牌、审核工作流程以及监控妥协指标 (IoC)。
发生什么事?
此 Shai-Hulud 供应链攻击 npm 软件包 是近年来最具破坏性的事件之一。与孤立的木马不同,这种蠕虫病毒会混合 凭证盗窃、自动泄露和自我复制。因此,感染时间从几周缩短到几个小时。
对于 DevOps 团队来说,教训是明确的:如果每次安装都可以执行代码,那么每次依赖更新都是一个潜在的突破点。
可能的初始向量和目标凭证
早期分析 表明此次袭击可能始于 凭证被盗例如,钓鱼活动欺骗了 npm login 或者 MFA 提示可能已经捕获了开发者令牌。一旦攻击者获得第一个立足点,蠕虫就会通过嵌入到 npm 包中并窃取更多机密信息来进行传播:
- npm 配置文件 喜欢
.npmrc,通常包含发布令牌。 - 环境变量 并使用 GitHub PAT 进行配置 CI/CD 机密。
- 云元数据端点 (AWS、GCP、Azure)产生用于横向移动的短期凭证。
因此,凭证盗窃成了攻击的跳板。有了有效的 npm 令牌和 GitHub 密钥,Shai-Hulud 无需额外人力即可在多个软件包和存储库之间进行自我复制。
Shai-Hulud供应链袭击事件对高管的影响
Shai-Hulud 至今仍活跃。该蠕虫加快了影响时间。以前木马病毒需要数周才能完成的任务,现在只需数小时即可完成。因此, 传播速度更快,而且更难控制。 它通过以下方式推进:
- 窃取 npm 发布令牌和 GitHub 机密。
- 将自身重新发布到其他 npm 包中。
- 添加恶意 GitHub Actions 工作流以实现持久性。
谁受到影响:
任何安装公共 npm 软件包的团队都会面临风险。此外,拥有缓存 npm 或 GitHub 令牌的开发者面临高风险。使用广泛机密的 CI 运行人员也容易受到攻击。
经营风险
业务影响迅速增长。被盗令牌可能导致账户接管、软件包劫持,甚至云滥用。此外,GitHub 工作流程中的持久性使其更难清理。因此,各队必须将 Shai-Hulud 视为一个正在发生的事件,而不是一个已经结束的事件。
Shai-Hulud供应链攻击在npm包中的工作原理
攻击者的目标和动机
该活动针对三件事进行了优化:
- 第一个目标是 大规模窃取凭证 来自开发人员笔记本电脑和 CI 运行人员。这包括 npm 发布令牌、GitHub 令牌和云凭证。事实上,多项分析证实了系统性的秘密收集行为,例如运行 TruffleHog 和查询云元数据端点。
- 第二个目标是 自动传播 通过滥用受感染维护者的发布权。结果,一个立足点迅速扩展为多个立足点,因为无需额外的人力即可出现其他软件包的新受感染版本。
- 第三个目标是 坚持并可靠地撤离 通过 GitHub 基础设施。攻击者创建了一个名为“Shai-Hulud”的公共仓库,其中包含双重 base64 数据.json,此外,他们还植入了一个序列化的工作流程 ${{ toJSON(秘密) }} 并将其发布到静态 webhook。
可能的回报包括长期访问注册表和源代码、快速横向移动到云账户,以及进一步利用供应链的选项。公开报告显示,私人代码库被公开, “-迁移” 后缀,可增加数据曝光度和动量
Shai-Hulud 有效载荷内部: 包.js 在 npm 包中
夏胡鲁德号飞船 大型、Webpack 打包并高度压缩 JavaScript 文件 (包.js(约 3–3.7 MB) 从执行 安装后 钩入 的package.json。 最后, 每次安装都会自动触发有效负载。这种设计隐藏了标识符,压缩了控制流,并将所有逻辑推送到安装过程中执行的单个组件中。分析师一致确认存在 Webpack 打包、文件大小异常以及安装时执行的问题。
您将在样本中看到混淆和反分析特征:
- 最小化模块图 带有数字模块 ID、稀疏注释和扁平控制流。此外, 这种结构使得人工审查极其困难。
- 字符串隐藏 通过 base64 层和构造助手。例如, 重复的 base64 编码和解码通常出现在泄露例程周围。
- 动态调度 通过 评估风格的模式和生成的函数体,从而使代码能够在运行时改变行为。
- 操作系统过滤 更喜欢在 CI 运行器和开发人员笔记本电脑上执行 Linux 和 macOS。
从功能角度来看,该捆绑包是模块化的。 文档描述了操作系统发现、文件系统和 Git Secret 扫描、云 SDK 访问、GitHub API 操作以及用于编辑维护者拥有的其他软件包的传播引擎等模块。事实上,StepSecurity 和 ReversingLabs 都重点介绍了一个使用恶意钩子自动更新软件包的功能。
Shai-Hulud 中的安装时执行:npm 包如何触发蠕虫
攻击开始于 postinstall 运行节点 包.js. 此时,脚本初始化并解压内存中的工作状态,为蠕虫的全面运行做好准备。
发现和收获
- Payload 会转储 process.env 文件,并扫描本地文件中的高熵机密和令牌前缀。此外,它还通过运行 TruffleHog 来扩展覆盖范围。
- 它查询云元数据端点以收集短期凭证。 例如, 呼吁
169.254.169.254在 AWS 上或metadata.google.internalGCP 上经常出现在受感染的主机中。 - 所以, 找到的任何凭证都可以立即用于发布新的 npm 包或推送 GitHub 工作流程。
渗出
- 该蠕虫创建了一个名为 沙伊胡鲁德 并写入双 base64 编码
data.json包含平台详细信息、环境转储和机密信息。由此可见,如果防御者知道在哪里查看,这种干扰行为很容易被发现。 - 它还植入了一个 GitHub Actions 工作流,通常在名为 夏胡鲁德,序列化
${{ toJSON(secrets) }}并将数据发布到静态 webhook。此外,此工作流程将持续存在,直到有人主动将其删除。
传播
- 使用任何发现的 npm 令牌,payload 会枚举受感染维护者拥有的所有软件包。然后,它会获取每个 tarball,注入 bundle.js 和 postinstall 条目,并重新发布软件包。
- 因此,在几个小时内就会出现数十个受感染的软件包,从而增加整个生态系统的爆炸半径。
坚持和暴露
该蠕虫使恶意工作流程保持活跃,并在某些情况下将私人存储库翻转为公开存储库 “-迁移” 后缀。总之,这确保了攻击者的立足点并最大限度地泄露数据。
按键检测说明
这种不寻常的用法 ${{ toJSON(secrets) }} 在 Actions 工作流中很少见。 因此, 团队应该将其视为狩猎期间的高信号指标。
您应该寻找的净化工作流模式
这种不寻常的用法 至 JSON(秘密) 行动是此次事件中一个高信号指标。
高级传播伪代码(安全、描述性)
async function propagate(token, owner) {
const pkgs = await npmApi.listPackages(owner, token);
for (const p of pkgs) {
const tgz = await npmApi.fetchTarball(p, token);
const modified = injectBundleAndPostinstall(tgz); // adds bundle.js + "postinstall"
await npmApi.publish(modified, token); // publishes new malicious version
}
}
分析人士大规模地观察到了这种循环,这解释了受感染包裹数量从数十个迅速增加到数百个的原因。
为什么说这是软件包生态系统中的蠕虫
蠕虫是一种无需手动操作即可自行传播的恶意软件。在操作系统中,蠕虫通常利用网络漏洞从一台机器传播到另一台机器。相比之下,Shai-Hulud 在 npm 注册表内运行。其高效路径是通过 凭证重用.
该蠕虫利用窃取的 npm 发布令牌。一旦获得有效凭证,它就会在同一维护者拥有的其他软件包下重新发布受感染的版本。随后,这些软件包会被毫无戒心的开发人员或 CI 运行人员安装,如此循环往复。
因此,包括 黑暗阅读,将 Shai-Hulud 归类为 自我复制的蠕虫 而不是简单的木马病毒或域名抢注事件。两者之间的区别很重要:木马病毒通常只会感染一台主机,而蠕虫病毒会自动在整个生态系统中扩大其影响。
“工作原理”速查表
总结一下 Shai-Hulud 的生命周期,这里有一个cis其主要步骤的细分:
- 带有 安装后 已安装,并且
bundle.js执行。 - 有效载荷转储环境变量,扫描文件和 git 历史记录,运行 松露猪并查询云元数据服务。因此,任何发现的秘密都会立即变得有用。
- 泄露有两种方式:首先,通过创建名为 沙伊胡鲁德 使用双重 base64 编码
data.json; 其次,通过植入 GitHub Actions 工作流程,发布${{ toJSON(secrets) }}到 webhook。 - 该蠕虫会利用任何窃取的 npm 令牌,使用相同的恶意钩子重新发布受感染维护者拥有的所有其他软件包。通过这种方式,感染迅速增加。
- 最后,攻击者掌握了更多的秘密,更多的数据包可以传播,并且 坚持 在 GitHub 帐户和存储库内。
如何切实避免此类攻击
Shai-Hulud 敲响了警钟。窃取代币并重新发布的蠕虫病毒并非未来的风险,它存在于 npm 包生态系统 今天。为了防止这种 供应链攻击团队需要可编程、自动化且直接在 CI/CD pipelines. 这些防御措施与你之前已经实施的相同 西吉尼.
将不良文物拒之门外
您应该在 npm 包和 tarball 到达开发人员或 CI 作业之前对其进行扫描。超大 bundle.js 文件,可疑的安装后文件 hooks和混淆标记都是早期的危险信号。此外,在 pipelines 可防止自动消耗未经审查的新鲜版本。
硬化 CI/CD 默认情况下,
Guardrails in CI/CD 至关重要。它们会拒绝引入新脚本或二进制文件的合并或安装。同时,它们会阻止序列化机密或尝试外部发布的工作流程。团队还应要求仅使用锁文件安装(npm ci) 在所有 pipeline因此依赖集仍然可重现且安全。
减少代币爆炸半径
机密信息绝不能成为单点故障。持续扫描代码、配置和 pipeline 输出 暴露的凭证令牌应限定在狭窄的范围内,并赋予较短的生命周期,并在检测到暴露时自动轮换。通常情况下,在执行可疑安装后操作的主机上使用的任何令牌都应视为已泄露。
尽早发现蠕虫行为
非常规信号检测 至关重要。例如,npm 发布事件的突然激增、无缘无故出现的新工作流程,或者充斥着奇怪编码文件的新公共代码库,都可能预示着蠕虫活动。因此,团队应迅速发出警报,并隔离任何出现这些警告信号的维护人员或运行人员。
快速修复,无需中断构建
速度和安全必须并存。自动化 pull requests 可以用经过审查的版本替换受损的 npm 软件包。此外, 可达性 以及 开发性 分析确保升级保持最小化和稳定。最后,一旦确认暴露,就从干净的镜像重建受影响的CI运行器,防止攻击进一步蔓延。
妥协指标 (IoC)
在分析 Shai-Hulud 时,球队应该注意 静态 IoC 在文件和 行为 IoC in pipelines. 这些信号共同帮助及早发现感染并在蠕虫进一步扩散之前做出反应。
静态 IoC
观察到以下 SHA-256 摘要匹配 bundle.js 样本:
- 46faab8ab153fae6e80e7cca38eab363075bb524edd79e42269217a083628f09
- 81d2a004a1bca6ef87a1caf7d0e0b355ad1764238e40ff6d1b1cb77ad4f595c3
- dc67467a39b70d1cd4c1f7f7a459b35058163592f4a9e8fb4dffcbba98ef210c
此外,请注意以下重复出现的模式:
- A
bundle.js在包根目录下。 "postinstall": "node bundle.js"内package.json.- 存储库命名 沙伊胡鲁德.
- GitHub 工作流程包含
${{ toJSON(secrets) }}.
行为 IoC
除了文件签名之外,蠕虫活动还会通过行为暴露自身。例如:
- 一位维护者突然爆发大量 npm 发布事件。
- 将数据推送到外部端点的新工作流程。
- 由 CI 运行器触发的出站 POST 请求。
- 最近创建了带有编码 blob 的公共存储库。
快速狩猎
# Find postinstall in package.json
grep -R --line-number '"postinstall"' --include="package.json" /path/to/archives
# Detect tarballs with bundle.js
find /path/to/tarballs -name "*.tgz" -print0 \
| xargs -0 -n1 -I{} sh -c 'tar -tf "{}" | grep bundle.js && echo "== {}"'
# Search workflows for toJSON(secrets)
grep -R --line-number "toJSON(secrets)" --include="*.yml" .github || true
结论:来自沙伊·胡鲁德的教训
此 Shai-Hulud供应链攻击 npm 软件包中的漏洞表明了当今软件供应链的脆弱性。该蠕虫病毒不仅添加了恶意代码,还窃取了令牌,发送了数据,然后自动重新发布。因此,攻击在数小时内就蔓延开来,而不是数周。
对于开发人员和 DevOps 团队来说,教训是明确的:
- 每次安装都会运行代码。 即使是一个常见的 npm 包也可能隐藏安装后蠕虫。
- 每个代币都具有很高的价值。 一旦被盗,它就会被用来进一步传播恶意软件。
- 所有的 pipeline 需要检查。 没有 guardrails 在依赖关系、工作流和秘密方面,一次妥协就能迅速影响生产。
因此,要阻止像 Shai-Hulud 这样的攻击,需要自动化且易于执行的控制措施。团队应该在安装前扫描 npm 软件包,使用 lockfile 构建,检测异常发布活动,并保持令牌的短期有效期。这些步骤不再是可选的,而是现代系统韧性的基础。 pipelines.
在 Xygeni,我们将 Shai-Hulud 供应链攻击视为对整个开源生态系统的警示。可持续的前进之路是将供应链安全直接纳入开发流程,在代码、npm 包和 pipeline连接。
以下是 Shai-Hulud 中已报告受损的 npm 软件包和版本的完整列表。请使用它来检查您的锁文件、注册表和 CI。 pipelines 代表曝光。
受损软件包列表
📦 受损 npm 软件包预览
| 软件包名称 | 版本 | 发布日期 |
|---|---|---|
| json-规则引擎简化 | 0.2.1 | 2025-09-14T17:58:51.203Z |
| 飞行员 | 0.8.8 | 2025-09-14T18:35:07.600Z |
| mcp-知识图谱 | 1.2.1 | 2025-09-14T18:35:09.494Z |
| 空军首领 | 0.3.1 | 2025-09-14T18:35:09.521Z |
| 跳跃门 | 0.0.2 | 2025-09-14T18:35:09.651Z |
| TVI-CLI | 0.1.5 | 2025-09-14T18:35:10.996Z |
| @thangved/回调窗口 | 1.1.4 | 2025-09-14T20:31:38.479Z |
| @tnf-dev/api | 1.0.8 | 2025-09-14T20:31:39.547Z |
| @tnf-dev/js | 1.0.8 | 2025-09-14T20:31:41.251Z |
| @tnf-dev/mui | 1.0.8 | 2025-09-14T20:31:41.259Z |
| @tnf-dev/核心 | 1.0.8 | 2025-09-14T20:31:42.728Z |
| @teselagen/react-table | 6.10.20 | 2025-09-14T20:37:08.597Z |
| @hestjs/演示 | 0.1.2 | 2025-09-14T20:45:52.348Z |
| @nexe/eslint-配置 | 0.1.1 | 2025-09-14T20:45:53.625Z |
| @hestjs/eslint-配置 | 0.1.2 | 2025-09-14T20:45:55.044Z |
| @nexe/配置管理器 | 0.1.1 | 2025-09-14T20:45:55.066Z |
| @nexe/记录器 | 0.1.3 | 2025-09-14T20:45:55.170Z |
| @hestjs/记录器 | 0.1.6 | 2025-09-14T20:45:55.197Z |
| @hestjs/验证 | 0.1.6 | 2025-09-14T20:45:55.595Z |
| @hestjs/核心 | 0.2.1 | 2025-09-14T20:45:55.888Z |
➡️ 查看受损软件包的完整列表
| 软件包名称 | 版本 | 发布日期 |
|---|---|---|
| json-规则引擎简化 | 0.2.1 | 2025-09-14T17:58:51.203Z |
| 飞行员 | 0.8.8 | 2025-09-14T18:35:07.600Z |
| mcp-知识图谱 | 1.2.1 | 2025-09-14T18:35:09.494Z |
| 空军首领 | 0.3.1 | 2025-09-14T18:35:09.521Z |
| 跳跃门 | 0.0.2 | 2025-09-14T18:35:09.651Z |
| TVI-CLI | 0.1.5 | 2025-09-14T18:35:10.996Z |
| @thangved/回调窗口 | 1.1.4 | 2025-09-14T20:31:38.479Z |
| @tnf-dev/api | 1.0.8 | 2025-09-14T20:31:39.547Z |
| @tnf-dev/js | 1.0.8 | 2025-09-14T20:31:41.251Z |
| @tnf-dev/mui | 1.0.8 | 2025-09-14T20:31:41.259Z |
| @tnf-dev/核心 | 1.0.8 | 2025-09-14T20:31:42.728Z |
| @teselagen/react-table | 6.10.20 | 2025-09-14T20:37:08.597Z |
| @hestjs/演示 | 0.1.2 | 2025-09-14T20:45:52.348Z |
| @nexe/eslint-配置 | 0.1.1 | 2025-09-14T20:45:53.625Z |
| @hestjs/eslint-配置 | 0.1.2 | 2025-09-14T20:45:55.044Z |
| @nexe/配置管理器 | 0.1.1 | 2025-09-14T20:45:55.066Z |
| @nexe/记录器 | 0.1.3 | 2025-09-14T20:45:55.170Z |
| @hestjs/记录器 | 0.1.6 | 2025-09-14T20:45:55.197Z |
| @hestjs/验证 | 0.1.6 | 2025-09-14T20:45:55.595Z |
| @hestjs/核心 | 0.2.1 | 2025-09-14T20:45:55.888Z |
| @hestjs/cqrs | 0.1.6 | 2025-09-14T20:45:55.966Z |
| @hestjs/标量 | 0.1.7 | 2025-09-14T20:45:56.386Z |
| ng2 文件上传 | 7.0.3 | 2025-09-15T02:44:29.555Z |
| 电容器通知处理程序 | 0.0.2 | 2025-09-15T04:54:48.431Z |
| 电容器插件-vonage | 1.0.2 | 2025-09-15T04:54:48.501Z |
| 电容器插件健康应用 | 0.0.2 | 2025-09-15T04:54:48.704Z |
| 电容器android权限 | 0.0.4 | 2025-09-15T04:54:48.753Z |
| voip呼叫套件 | 1.0.2 | 2025-09-15T04:54:49.223Z |
| 电容器插件-ihealth | 1.1.8 | 2025-09-15T04:55:08.113Z |
| @art-ws/common | 2.0.22 | 2025-09-15T05:21:15.411Z |
| @art-ws/config-eslint | 2.0.4 | 2025-09-15T05:21:17.199Z |
| ngx-ws | 1.1.5 | 2025-09-15T05:21:17.514Z |
| @art-ws/slf | 2.0.15 | 2025-09-15T05:21:17.524Z |
| @art-ws/http 服务器 | 2.0.21 | 2025-09-15T05:21:17.745Z |
| pm2-gelf-json | 1.0.4 | 2025-09-15T05:21:18.413Z |
| @art-ws/di | 2.0.28 | 2025-09-15T05:21:18.488Z |
| @art-ws/di-节点 | 2.0.13 | 2025-09-15T05:21:18.849Z |
| @art-ws/config-ts | 2.0.7 | 2025-09-15T05:21:19.408Z |
| @art-ws/db-context | 2.0.21 | 2025-09-15T05:21:19.814Z |
| @art-ws/openapi | 0.1.9 | 2025-09-15T05:21:19.969Z |
| @art-ws/web-app | 1.0.3 | 2025-09-15T05:21:20.383Z |
| @art-ws/ssl-信息 | 1.0.9 | 2025-09-15T05:21:20.927Z |





