git|窃取任意GitHub Actions敏感信息如此简单,只需要分支改个名?

【CSDN 编者按】对于软件中存在的漏洞 , 工程师一般会优先修复那些影响用户数多的漏洞 , 比如导致服务器死机的漏洞 。 但一些特殊情况下才会出现且的漏洞 , 不易被人发现 , 就会长时间存在 。 本文就介绍了作者发现了GitHub上不易察觉的安全漏洞 , 通过这个漏洞可以窃取GitHub Actions上的敏感信息 , 并且作者还尝试用不同方法来触发漏洞看看还是否存在其他漏洞 。 一起来看看吧 。
原文链接:https://blog.teddykatz.com/2022/02/23/ghosts-of-branches-past.html
译者 | 章雨铭 责编 | 屠敏
出品 | CSDN(ID:CSDNnews)
不久前 , 我在GitHub中发现的一个安全漏洞 , 这个漏洞会让攻击者获得对几乎所有公共库的写入权限 。 下面是简单回顾 。

  • GitHub上的每个拉取请求都有一个 "基分支" , 也就是与拉取请求相同的库中的一个git分支 。
  • GitHub Actions有时会执行拉取请求的基分支的代码 , 使代码能够访问库的敏感信息 。 (具体来说 , 如果基分支有一个 pull_request_target 工作流 , 就会发生这种情况) 。 这通常是安全的 , 因为任何可能推送到基分支的人都可以直接写到库 。
  • 由于一时疏忽 , 用户将其拉取请求的"基分支"名称设置为commit hash , 而不是一个真正的分支 。
  • 因此 , 攻击者可以从fork中创建一个拉取请求 , 然后将"基分支"改为commit hash , 在攻击者的fork中的恶意commit 。 由于commit hash在fork中是共享的 , GitHub Actions会执行这个恶意commit的代码 , 使其能够访问父存储库及其敏感信息 。
这篇文章描述了我在GitHub中发现的一个不同的安全漏洞 , 使用了类似的攻击策略 。
Git、GitHub和异常的分支名称
去年夏天 , 我观察了git和GitHub对异常的分支名的表现 。
在某些地方 , git CLI允许使用分支或commit hash 。 例如 , 要切换到一个叫 my-branch的分支 , 下面这个就可以使用 。
$git checkout my-branch同样地 , 要切换到一个特定的commit , 可以使用commit hash作为命令行参数 。
$git checkout a047be85247755cdbe0acce6f1dafc8beb84f2ac我们还可以自己给分支命名 。 如果创建一个名为
$ git branch a047be 85247755cdbe0acce6f1dafc8beb84f2ac$ git checkout a047be85247755cdbe0acce6f1dafc8beb84f2ac warning:refname 'a047be85247755cdbe0acce6f1dafc8beb84f2ac'is ambiguous.... $ git branch* a047be85247755cdbe0acce6f1dafc8beb84f2acmain
另一方面 , GitHub返回了一个错误:[1]
$ git pushremote: error: GH002: Sorry, branch ortag names consisting of 40hexcharacters are notallowed. remote: error: Invalid branch ortag name "a047be85247755cdbe0acce6f1dafc8beb84f2ac"dafc8beb84f2ac "这没什么意思 。 但如果我们用commit的短hash来命名分支 , 而不是用完整的hash来命名呢?
$ git branch a047be8$ git checkout a047be8$ # ...$ git push* [new branch] a047be8 -> a047be8branch 'a047be8'set up to track 'a047be8'GitHub接受了这次拉取!我们成功创建了一个模棱两可的分支 。
和分支同名的短hash
总结一下上一节的内容:
  • 我们在GitHub库中有一个commit , 其短hash为 a047be8。
  • 再推送一个名为 a047be8 的分支到同一个库 。
此时 , 当我们访问 github.com/someone/some-repo/tree/a047be8时 , GitHub选择显示 a047be8分支的文件 , 而不是显示 a047be8commit的文件 。[2]这很合理 。 如果GitHub将 a047be8解析为一个commit短hash , 那么就不能用这个名字来指代这个分支了 。 就目前而言 , 将 a047be8解析为一个分支 , 只会在有人在URL中使用短hash并期望它解析为一个commit时产生问题 。 但这种情况是不可取的 , 因为如果发生短hash冲突 , 它也会失败 。
换句话说 ,a047be8分支实际上"影射"了带有短hash a047be8的commit , 阻止了短hash提及该commit 。
“幽灵分支”
我做了一个实验 , 看看GitHub能否很好地处理与commit短hash相匹配的已被删除的分支 。
在前面的例子中 , 在储存库里有一个带有特定短hash的commit , 然后我们推送了一个名字与短hash相同的分支 。 当然 , 我们也可以反过来做——如果一个分支已经存在 , 我们可以推送一个与该分支名称相同的短hash的commit 。 所以我进行了以下操作 。
  1. 首先 , 我推送了一个名为 deadbeef 的分支到GitHub储存库 。
  2. 然后 , 我从另一个分支创建了一个拉动请求到 deadbeef。
  3. 接下来 , 我删除了 deadbeef 分支 , 这导致拉动请求自动关闭 。
  4. 最后 , 我开始使用我的lucky-commit , 生成了一个带有短hash deadbeef 的新commit 。 然后我把这个commit也推送到GitHub仓库 。[3]
我在想 , 如果出了什么错误 , 可能是在显示拉动请求diff 。 例如 , 我以为UI可能会开始显示新的 deadbeefcommit的diff , 而不是旧的 deadbeef分支 。 但实际上 , GitHub显示的是已删除的 deadbeef分支的历史diff , 这才是正确的 。 (事后看来 , 拉动请求的diff只有在拉动请求开放时才会更新 , 这也很合理) 。
我正准备放弃 , 去找别的问题时 , 我发现了一个奇怪的现象 。 我可以在GitHub的UI上重新打开拉动请求 。
git|窃取任意GitHub Actions敏感信息如此简单,只需要分支改个名?
文章图片

这有点奇怪 。 因为通常情况下 , 对于任何开放的拉取请求 , 拉取请求的head和基分支都需要存在 。 正如我们之前看到的 , 如果任何一个分支被删除 , 拉动请求就会立即关闭 。 但在这种情况下 , GitHub允许我们在删除基分支后重新打开拉动请求 。
为什么呢?GitHub会认为基分支 deadbeef依然存在——因为当GitHub试图查找 deadbeef分支的代码时 , 并不是什么都没查找到 , 而是查找到了有短hash deadbeef的commit 。 因此 , GitHub才允许重新打开拉动请求 。
返回无意义的拉取请求
在这一点上 , 我们有一个开放的拉取请求 , 其中的基分支指向一个带有短hash deadbeef的commit , 而不是一个实际的分支 。
这与之前提到的博文中描述的情况几乎相同 , 像这样的"无意义"拉取请求可以用来窃取GitHub Action的敏感信息 。 那篇博文中的漏洞路径在2021年被修复了 , 为"编辑基分支"端点增加了验证功能——有效防止该端点被用于创建无意义的拉取请求 。 然而 , 当时GitHub并没有在GitHub Actions后端添加分支存在的检查 。 换句话说 , GitHub Actions仍然容易受到这些无意义的拉动请求的影响 , 但人们认为已经不能再创建一个无意义的拉取请求 。
当我尝试使用GitHub Actions的这种新方法来创建无意义的拉动请求时 , 我发现使用 pull_request_targetActions的工作流程仍有可能窃取敏感信息 。
把想法组合起来
我们设定一个可行的攻击场景 。
  1. 有写入权限的人在正常的开发工作流中无意中创建了一个名为 deadbeef 的分支(或 AAAAAAA, 或 12345, 或任何其他符合特定限制条件的名称 [4] ) 。
  2. 攻击者从fork创建一个拉取请求到 deadbeef 分支 , 然后立即关闭该拉取请求(例如 , 假装它是错误创建的) 。
  3. 后来 , 有人删除了 deadbeef 分支(比如 , 在这个修改被合并到main分支之后) 。
  4. 在一个特意制作的有短hash deadbeef f的commit中 , 攻击者将类似这样的恶意GitHub Actions工作流推送到他们的fork中 。
  5. 攻击者重新打开拉动请求 。 这导致GitHub在拉动请求的基分支" deadbeef "寻找 pull_request_target 工作流 。
  6. 由于deadbeef分支已不存在 , GitHub将 deadbeef 解析为攻击者的commit , 在该commit中找到恶意的Actions工作流 , 并继续给提供储存库的敏感信息 , 以及授予攻击者储存库的写入权限的 GITHUB_TOKEN。[5]
请注意 , 这个攻击场景需要大量的用户互动(攻击者需要等待有人偶然推送一个异常命名的分支) , 所以它比之前博文中描述的攻击要轻得多 。
在我向GitHub报告这个问题后 , 他们添加了一个修复程序 , 确保 pull_request_target事件只能从分支触发 , 而不是从游离的commits中触发 , 这就防止了这种攻击的发生 。 在写这篇文章的时候 , 如果基分支的名字与储存库中的commit短hash相同 , 仍然有可能用一个被删除的基分支重新打开拉动请求 。 我 认为这不影响安全 , 但看起来很有趣 。
一些解释:(对应原文[1]-[5])
  1. git CLI也会进行相同的操作 。git CLI和GitHub在这里的行为一致 , 可能不是巧合——它们似乎都在使用 git rev-parse 的结果 。
  2. 这是我的实验第三次在发现安全漏洞方面发挥了作用 。我真的不知道该如何看待这个问题 。也许我现在可以把我所有的实验归类为"安全研究" 。(这是第一次: https://blog.teddykatz.com/2019/11/12/github-actions-dos.html 。第二次是一个低严重性的问题 , 就略过不谈) 。
  3. 具体来说 , 分支名需要能被 git rev-parse 解析为commit hash 。这可以是一个十六进制的字符串 , 比如 deadbeef, 也可以是一个 git describe 格式的字符串 , 比如 anything-123-gdeadbeef, 这仍然会解析为 deadbeef commit 。
  4. 上一篇博文发布后 , GitHub引入了大量可选择的安全功能 , 以减少这种类型攻击的范围 , 如Actions environments 。
— END—
《新程序员001-004》全面上市 , 对话世界级大师 , 报道中国IT行业创新创造
— 推荐阅读 —
? Python:Bug 官网不要了 , 全迁去 GitHub!
? 微软若“无故”解雇暴雪 CEO , 将付 1500 万美元“分手费”
【git|窃取任意GitHub Actions敏感信息如此简单,只需要分支改个名?】?马斯克:我是Rust粉丝 , 但我选择C

    推荐阅读