Go 模块库劫持 (repojacking) 介绍 from VulnCheck

Go 模块库劫持 (repojacking) 介绍 from VulnCheck


背景

VulnCheck是一家漏洞情报初创公司,今天在浏览VulnCheck博客时发现一篇文章:Hijackable Go Module Repositories,对于Go 模块库劫持的漏洞原理讲解十分清晰明了,以下内容大部分来源于原文博客,个人只做整理,分析和总结。

VulnCheck 分析了 Go 模块生态系统,寻找其源代码存储库可能容易受到存储库劫持(repojacking)的模块。由于 GitHub 用户名更改,超过 9,000 个存储库容易受到重新劫持。VulnCheck 发现超过 6,000 个存储库由于帐户删除而容易受到重新劫持。这 15000 个存储库加起来支持超过 800000 个 Go 模块。

为什么 Go 特别容易受到存储库劫持(repojacking)

Go 模块生态系统是去中心化设计的,是独一无二的。其他打包系统(例如 Pypi 或 NPM)要求开发人员创建帐户才能上传其包。这使得软件包平台能够管理用户和内容。Go语言比较特殊,Go 开发人员通过将代码推送到 GitHub 等源代码控制平台来发布模块。然后任何人都可以从软件源获取 Go 模块和 pkg.go.dev 缓存模块的详细信息。
这种去中心化使得 Go 模块存储库特别容易受到重新劫持。当模块作者更改其用户名或删除其帐户时,存储库就会变得容易受到攻击。此时,攻击者可以注册新未使用的用户名,复制模块存储库,并将新模块发布到 proxy.golang.org 和 go.pkg.dev 。
GitHub 确实有一些针对重新劫持的保护措施,受欢迎的仓库命名空间回退功能可以防止任何仓库“在所有者的帐户被重命名或删除之前的一周内有超过100个克隆”的再劫持。这听起来可能很合理,但对于 Go 来说却不一定。 Go 模块通常由 Module 镜像缓存,因此不需要与源存储库交互或从源存储库克隆。对于某些情况,在 GitHub 上托管了一个开源库,我们每天都使用它,并且有 170 多个星,但该存储库在上周只看到了 20 个克隆。基于此,100 个克隆的保护并不一定像 GitHub 想象的那么好。

寻找可劫持的 Go 模块

2023 年 6 月,Aquasec 发表了研究报告,指出数百万个可劫持的 GitHub 存储库。了解到 Go 特别容易受到这种攻击向量的影响,我们开始准确枚举有多少 Go 模块版本可能受到影响。模块版本是指一个模块及其所有版本。
跟踪超过 2000 万个 Go 模块版本。它不完全是一个小数据集,但追踪可重复劫持模块的算法相对简单:

  1. 对于每个模块,从模块名称推断存储库 URL。例如, github.com/vulncheck-oss/go-exploit 是一个Go模块,其源代码托管在https://github.com/vulncheck-oss/go-exploit。
  2. 尝试连接到每个存储库。
  3. HTTP 301 响应表示用户名更改、存储库名称更改或两者兼而有之。出于我们的目的,我们验证了存储库名称是否相同(例如 go-exploit ),但用户名已更改。我们还验证了原始用户帐户(例如 vulncheck-oss )不存在。这进一步消除了存储库传输之类的东西。通过所有这些步骤的存储库被认为可能容易受到重新劫持。
  4. HTTP 404 响应表明存储库不再存在。然后我们将查看用户名是否仍然存在。如果不是,那么这也可能容易受到重新劫持。
  5. HTTP 200 响应表明存储库不易受到攻击。
  6. 然后我们验证了潜在可劫持的存储库实际上在 go.pkg.dev 中有一个条目(您可以使用模块镜像缓存几乎所有内容,因此清除非 Go 模块内容非常重要)。
    剩下的就是大量可以被重新劫持的存储库(假设过去 7 天问题中的 100 个克隆不是问题)。我们的第一个发现是,由于用户名更改,超过 9,000 个 Go 模块 GitHub 存储库容易受到重新劫持。潜在的重新劫持会影响超过 500,000 个 Go 模块版本。
    为了确定存储库的受欢迎程度,我们获取了每个存储库的 GitHub 星数。我们以 0 到 1000 的桶形式绘制了结果。按星号分组的可劫持 Go 模块存储库统计图如下:

可劫持 Go 模块存储库统计图

大多数存储库的星级为零。这些对于攻击者来说几乎没有价值。其余 3,000 个存储库的星级在 1 到 1000 之间。存储库对攻击者有价值的可能性随着星级的增加而增加,但仅靠星级并不能说明攻击有多有用。 Go 生态系统中的实际使用很重要,因为利用依赖于开发人员更新到新模块。
我们跟踪的另一个类别是可劫持的存储库,因为 GitHub 帐户已被删除。我们发现了超过 6,000 个此类存储库,最终影响了近 300,000 个模块版本。由于存储库已被删除,我们无法找到它们有多少颗星。攻击者必须依赖于查找使用模式 - 最终搜索代码的导入。
搜索代码导入
这是一个沉重的负担,但对于合适的攻击者来说是值得的。尽管如此,即使在野外找到用途也是不够的。最终,受害者将需要更新到攻击者的新模块,因为攻击者无法覆盖旧模块。因此,尽管威胁确实存在,但 Go 生态系统内的重新劫持对于攻击者来说并不是立竿见影的胜利。

结论

VulnCheck认为缓解重新劫持漏洞是 Go 或 GitHub 必须承担的任务,这个和禅道创始人王春生前段时间分享了一个开源协议在中国面临的 bug:开源软件许可协议通常会表明作者不对用户使用该开源软件所造成的任何问题负责,但是,这种条款,在中国,是违法的观点类似。这个观点博主不做置评,对于重劫持漏洞,博主个人观点则认为github只是一个代码托管的平台,应提供更为灵活的重劫持保护措施,责任方应由go官方来负责和承担。在那之前,Go 开发人员应了解他们使用的模块以及这些模块源自的存储库的状态非常重要。

参考