社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Git

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

CSDN • 3 年前 • 357 次点击  

【CSDN 编者按】对于软件中存在的漏洞,工程师一般会优先修复那些影响用户数多的漏洞,比如导致服务器死机的漏洞。但一些特殊情况下才会出现且的漏洞,不易被人发现,就会长时间存在。本文就介绍了作者发现了GitHub上不易察觉的安全漏洞,通过这个漏洞可以窃取GitHub Actions上的敏感信息,并且作者还尝试用不同方法来触发漏洞看看还是否存在其他漏洞。一起来看看吧。

原文链接:https://blog.teddykatz.com/2022/02/23/ghosts-of-branches-past.html

本文由CSDN翻译,转载需注明来源出处。


译者 | 章雨铭       责编 | 屠敏
出品 | 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

GitHub也可以用类似的方式。例如,可以在github.com/someone/some-repo/tree/my-branch上查看某个分支的库的状态,或者在github.com/someone/somerepo/tree/a047be85247755cdbe0acce6f1dafc8beb84f2ac上查看某个commit hash的状态。

我们还可以自己给分支命名。如果创建一个名为

a047be85247755cdbe0acce6f1dafc8beb84f2ac的分支(与commit hash相同),会发生什么?

$ git branch a047be 85247755cdbe0acce6f1dafc8beb84f2ac$ git checkout a047be85247755cdbe0acce6f1dafc8beb84f2acwarning: refname 'a047be85247755cdbe0acce6f1dafc8beb84f2ac' is ambiguous....

$ git branch* a047be85247755cdbe0acce6f1dafc8beb84f2ac main

Git允许创建该分支,尽管会出现警告。然后在使用checkout和其他命令时,git将a047be85247755cdbe0acce6f1dafc8beb84f2ac解释为一个分支而不是一个commit hash。

另一方面,GitHub返回了一个错误:[1]

$ git pushremote: error: GH002: Sorry, branch or tag names consisting of 40 hex characters are not allowed.remote: error: Invalid branch or tag name "a047be85247755cdbe0acce6f1dafc8beb84f2ac"dafc8beb84f2ac"

这没什么意思。但如果我们用commit的短hash来命名分支,而不是用完整的hash来命名呢?

$ git branch a047be8$ git checkout a047be8$ # ...$ git push* [new branch]          a047be8 -> a047be8


    
branch '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分支实际上"影射"了带有短hasha047be8的commit,阻止了短hash提及该commit。

然而,该commit仍然存在。如果删除a047be8分支,那么a047be8这个名字就会立即被解析为a047be85247755cdbe0acce6f1dafc8beb84f2ac 的commit——即使有人不知道这个commit的存在,而只是想找到这个已经被删除的分支。这个边缘案例似乎是一个寻找bug的好方法。


“幽灵分支”


我做了一个实验,看看GitHub能否很好地处理与commit短hash相匹配的已被删除的分支。

在前面的例子中,在储存库里有一个带有特定短hash的commit,然后我们推送了一个名字与短hash相同的分支。当然,我们也可以反过来做——如果一个分支已经存在,我们可以推送一个与该分支名称相同的短hash的commit。所以我进行了以下操作。

  1. 首先,我推送了一个名为deadbeef的分支到GitHub储存库。

  2. 然后,我从另一个分支创建了一个拉动请求到deadbeef

  3. 接下来,我删除了deadbeef分支,这导致拉动请求自动关闭。

  4. 最后,我开始使用我的lucky-commit,生成了一个带有短hashdeadbeef的新commit。然后我把这个commit也推送到GitHub仓库。[3]

我在想,如果出了什么错误,可能是在显示拉动请求diff。例如,我以为UI可能会开始显示新的deadbeefcommit的diff,而不是旧的deadbeef分支。但实际上,GitHub显示的是已删除的deadbeef分支的历史diff,这才是正确的。(事后看来,拉动请求的diff只有在拉动请求开放时才会更新,这也很合理)。

我正准备放弃,去找别的问题时,我发现了一个奇怪的现象。我可以在GitHub的UI上重新打开拉动请求。

这有点奇怪。因为通常情况下,对于任何开放的拉取请求,拉取请求的head和基分支都需要存在。正如我们之前看到的,如果任何一个分支被删除,拉动请求就会立即关闭。但在这种情况下,GitHub允许我们在删除基分支后重新打开拉动请求。

为什么呢?GitHub会认为基分支deadbeef依然存在——因为当GitHub试图查找deadbeef分支的代码时,并不是什么都没查找到,而是查找到了有短hashdeadbeef的commit。因此,GitHub才允许重新打开拉动请求。


返回无意义的拉取请求


在这一点上,我们有一个开放的拉取请求,其中的基分支指向一个带有短hashdeadbeef的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. 在一个特意制作的有短hashdeadbeeff的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. 为什么GitHub不允许这种推送?我认为主要原因是为了维护permalinks和commit hash checkouts的完整性。例如,用户通常认为,github.com/someone/some-repo/blob/a047be85247755cdbe0acce6f1dafc8beb84f2ac/foo/bar.sh将始终解析为文件foo/bar.sh在指定commit hash处的不可变版本。但但如果有人成功推送了一个名为a047be85247755cdbe0acce6f1dafc8beb84f2ac的分支,那么GitHub就会开始从该分支返回foo/bar.sh的不同版本,而不是commit的原始版本。为了避免这种情况,GitHub屏蔽了正好由40个十六进制字符组成的分支名称。2019年,我给GitHub发了一份报告,说可以通过创建一个名为a047be85247755cdbe0acce6f1dafc8beb84f2ac/foo的分支,并在储存库底部放一个名为bar.sh的文件,绕开这个保护机制。由于这个问题,GitHub现在也屏蔽了以40个十六进制字符后的斜线开头的分支名。

  2. git CLI也会进行相同的操作。git CLI和GitHub在这里的行为一致,可能不是巧合——它们似乎都在使用git rev-parse的结果。

  3. 这是我的实验第三次在发现安全漏洞方面发挥了作用。我真的不知道该如何看待这个问题。也许我现在可以把我所有的实验归类为"安全研究"。

    (这是第一次:https://blog.teddykatz.com/2019/11/12/github-actions-dos.html。第二次是一个低严重性的问题,就略过不谈)。

  4. 具体来说,分支名需要能被git rev-parse解析为commit hash。这可以是一个十六进制的字符串,比如deadbeef,也可以是一个git describe格式的字符串,比如anything-123-gdeadbeef,这仍然会解析为deadbeefcommit。

  5. 上一篇博文发布后,GitHub引入了大量可选择的安全功能,以减少这种类型攻击的范围,如Actions environments。

END

 《新程序员001-004》全面上市,对话世界级大师,报道中国IT行业创新创造


《新程序员003》聚焦“云原生时代的开发者”与“全面数字化转型”两大主题,点击订阅



— 推荐阅读 —
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/127634
 
357 次点击