npm 官方封了 install scripts,这个恶意包直接在运行时偷数据——C2 地址藏在以太坊智能合约里,11 周才被发现

npm 在 2026 年 6 月收紧了对生命周期脚本的管控,很多团队以为「只要拦截 preinstall/postinstall 脚本就能防止供应链攻击」。2026 年 9 月,一个叫 GHAPPIER 的恶意加载器颠覆了这个假设:它根本不碰 install 脚本,而是在包的正常运行代码里埋入触发条件——当你调用 BTree.prototype.set 并传入特定 key 值时,后台悄悄启动一个 Node.js 进程,把你的机器信息发到 Slack 和 Telegram。C2 服务器的地址不在任何域名里,而是存在以太坊 Sepolia 测试网的智能合约上——想关停这个 C2,等于要把整个以太坊区块链回滚。

攻击过程:105 分钟的精确时间线

第一步:维护者账户被控制

@dforge-core/dforge-mcp 维护者账户在 105 分钟内被攻击者控制。攻击者在这段时间内完成了:获取 npm 发布权限 → 植入 GHAPPIER Loader → 修改 CI 工作流支持自动发布 → 发布恶意版本。

第二步:恶意版本 0.2.21 发布

版本 0.2.21 成功发布,在 npm 仓库里存活了 35 分 38 秒。在这 35 分钟里,所有通过 npm install @dforge-core/dforge-mcp 安装该包的开发者机器都受到污染。直到维护者发现异常,手动发布 0.2.22 才终结了这次攻击。

核心手法:可信发布机制被滥用

这次攻击最值得关注的技术细节是它滥用了 npm Trusted Publishing 机制。

npm Trusted Publishing 允许包作者配置「只要 GitHub Actions 工作流通过验证,就自动发布包,无需 npm Access Token」。攻击者利用了这个信任链条——恶意版本通过了 npm 的来源验证,因为它确实是从合法的 GitHub Actions 工作流发布的。

这揭示了一个根本问题:可信发布只验证「谁发布的」,不验证「发布了什么」。

恶意代码的隐蔽性:藏在 runtime 函数里

GHAPPIER 没有用 preinstall 或 postinstall 脚本。恶意代码藏在 BTree.prototype.set 方法的正常运行路径里:

BTree.prototype.set = function(key, value) {
  const result = this._originalSet(key, value);
  // 当 key 为特定值时启动载荷
  if (key === 'SPECIFIC_TRIGGER_KEY_XXXXXXXX') {
    const { spawn } = require('child_process');
    spawn('node', ['-e', '...obfuscated loader...'], {
      detached: true, stdio: 'ignore'
    }).unref();
  }
  return result;
};

触发条件和正常功能混在一起,npm 的脚本审计完全检测不到。

C2 藏在区块链里:无法关停

传统 C2 是域名或 IP,被封停后攻击者需要重新部署服务器。GHAPPIER 把 C2 地址存在以太坊 Sepolia 测试网的智能合约里:

contract GHAPPIER_C2 {
    function getC2Address() public view returns (string) {
        // 返回实时 C2 地址,攻击者更新合约后自动生效
        return "http://new-c2-address.onion";
    }
}

想关停 C2?必须回滚以太坊区块链。这使得 GHAPPIER 的基础设施几乎不可摧毁。

载荷链:4 阶段 + 自删除

  1. 触发:用户调用 BTree.prototype.set 并传入特定 key
  2. 启动:脱离当前进程启动 Node.js 子进程
  3. 信息收集:收集主机名、OS 架构、CPU、内存、运行时间
  4. 数据外泄:通过 Slack 频道 + Telegram 发送数据
  5. 自删除:清理恶意文件和触发代码,不留痕迹

与 PolinRider 的关联

CloudSEK 将 GHAPPIER 与 PolinRider 攻击活动关联。PolinRider 从 2026 年 3 月开始活跃,在上千个仓库活动,会窃取 git 凭证(OAuth tokens、SSH keys)。这些凭证可以横向移动,入侵更多开发者的 npm 包发布权限,形成自我强化的攻击循环。

防御建议

npm 侧:

  • 启用 npm 的代码审计和可选依赖审查
  • 对可信发布配置更细粒度的权限控制(如 IP 白名单)
  • 对包的内容增加完整性校验

开发者侧:

  • 锁版本:package-lock.json 确保每次安装的是完全相同的依赖
  • 审计依赖来源:安装陌生包前查看 GitHub 源码
  • 监控异常网络行为:如果 npm install 后发现机器在向意外目的地发请求,立即断网检查

CI/CD 侧:

  • CI 工作流发布 npm 包时使用最小权限的 OIDC 令牌
  • 关键包不要开启自动发布,启用发布审批机制

这次攻击的警示

npm 的 Trusted Publishing 本身没有问题,但缺少的是「发布内容验证」——npm 验证了「是谁发布的」,但没有验证「发布了什么」。区块链 C2 手法说明,攻击者已经在利用去中心化基础设施提高攻击弹性。传统的封域名/封 IP 思路在面对区块链存储的 C2 时完全失效。

评论区

0 条评论

登录后可评论。