二、Git工作原理
三、.git攻击技巧
四、总 结
main-repo
,此时我们在其中创建文件test.txt
,test.txt
中包含内容:
在未commit的时候,此文件结构如下:
.git
目录下多出了很多的内容。.git
目录就是git工作最关键的一个文件夹,里面会存储以下内容:
- 每一次commit的相关操作
- 临时修改的内容
- 修改文件的索引
- git基础配置
- 服务端和客户端的钩子事件
其中,我们此时提交的commit 如下:
fe
,而文件名正好就是fe后面的一串hash,可以看到这些目录和log对应关系如下:
Blob(Binary Large Object)
:存储文件的内容;Tree
:存储目录结构和文件名到 blob 引用的映射;Commit
:存储指向 tree 对象的引用,以及提交信息(如作者、日期、父提交等);Tag
:可以指定一些特殊的commit。
这些文件我们可以使用指令:
fe
目录下的文件,可以看到内容为:
commit
。每一个commit
文件中会记录一个叫做tree
的对象,用于记录当前commit中修改后的文件。
每一个tree
正好也对应了当前修改的目录和文件,尝试访问可以得到如下结果:
tree
中会记录一个到多个blob
,表示对一个blob
的引用,我们最后查看对应的blob
:
git cat-file -p
打印的内容。同样的,我们也可以获得对应的blob
文件的内容:
我们可以总结出这些文件的特征:
有些时候,我们可能要在一个仓库中引用另一个仓库的内容,这个库可能是一个基础库,会在多个库中被使用,例如压缩,日志打印等等,为了能够正确处理上述的场景,在git中,支持将另一个仓库作为
submodule
引入到当前库中。
- 子模块的名字,体现在
--name
参数上,我们这里写作<name>
; - 子模块的路径,这个为倒数第二个参数,这里写作
<submodule_repo>
; - 子模块在主仓库中的名字,这里写作
<submodule_path>
。
之后我们会反复使用这三个概念来描述不同的术语。
例如我们有另一个库,叫做submodule-repo
,里面有一个文件叫做submodule.txt
,内容如下:
此时文件结构如下:
1. 将其作为一个叫做submodule
的库,添加到当前的库中:
- name:x/y
- submodule_repo:../submodule-repo
- submodule_path: submodule
此时我们再次检查main-repo
的目录,结果如下:
main-repo
目录中新增了如下内容:
- 根据
submodule_path
创建的submodule
目录,里面包含了submodule-repo
的内容,其中这里的.git
为符号链接,指向../.git/modules/submodule
,也就是<target_repo>/.git/modules/<name>
这个路径; .gitmodules
文件;.git
目录中新增了modules
,里面包含了一个由<name>
命名的submodule
的目录,如果此处使用了--add name
,此时目录名字会被替换成name
;在这个例子中,目录被替换成了二级目录x/y
,同时这个目录中包含的是submodule-repo
中.git
的全部内容。
这里的.gitmodules
文件记录了当前submodule
的基本情况:
- 引号部分记录的正是参数
--add name
后方的<name>
也即是x/y
; - path 中记录了模块在这个仓库中的路径
<submodule_path>
,也就是我们最后跟着的参数,这个是【submodule实际的存放路径,以及检出后存放的路径】; - url 中则记录了对应的路径
<submodule_repo>
,是倒数第二个参数。
此时,.git/config
下的文件也会发生变化:
同时我们也注意到,此时git会将子项目目录中的.git放到当前目录的.git中,存放规则为:
<name>
的命名支持为多级路径,例如如果命名为path1/path2
,则此时存放路径就会变为:
submodule
在正常clone阶段,它是不会被拷贝下来,而是作为一个文件目录存在。当我们需要将其一并拷贝下来的时候,通常需要添加使用指令:
整个submodule
的clone
过程,根据逆向分为两个部分:
1. 尝试将对应仓库的.git
单独clone
下来,但是不进行checkout
,根据分析代码,其指令大致如下:
之前的展示中特意跳过了hooks
这个目录,这个目录中有很多脚本的样例:
pre-push
这个名字的脚本会在git执行push指令前执行,commit-msg
则是在commit阶段会执行。我们之后的攻击中会涉及一个叫做post-checkout
的脚本,这个脚本会在checkout
操作后执行。
当我们执行git clone
操作的时候,实际上执行了以下几个操作:
- 创建指定的仓库名字
mkdir -p <path>
; - 初始化git仓库
git init
; - 添加远程仓库
git remote add origin <url>
; - 下载对象引用
git fetch origin
; - 创建远程跟踪分支
git branch --track <branch> origin/<branch>
; - 检出默认分支
git checkout <branch>
。
实际上,代码文件在第4步就会被下载下来,并且存放在.git
文件中,之后由对应的branch
和checkout
操作来进行编辑组合。
.git
其实为一个非常完整的文件系统,因此可以将对文件系统的攻击思路迁移到上面,一个常规的思路就是.git
文件泄露源码,由于非常常规,这里就不多提了。然而实际上,很多人可能没注意的是,git在作为客户端使用的时候,依然有这里要介绍的是git指令在访问恶意repo的时候,可能会遭受的恶意攻击。
submodule
进行的攻击。网上可以找到<a ""="" class="weapp_text_link js_weapp_entry" href="">对应的exp。这个问题的本质源于一个我们刚刚提到的有趣的点:<name>
可以被命名为多级目录。那么,如果这个多级目录被命名为..
,那会发生什么呢?实际上,这个漏洞就是利用这一点。
假设我们在添加目录的时候,<name>
写作../../test
,那么实际上,添加的目录就变成了:
.gitmodules
中找到submodule
对应的url
,从那处开始拷贝文件,然后将文件放置到如下位置:
1. 我们此时可以将一个远端仓库的文件写入任意目录
2. 远端仓库的文件名和文件内容是可以任意决定的
那么结合git提供的各种特性,不难想到此时可以利用hooks
中的各种文件进行rce操作。
然而要如何让我们的文件落入到指定的hooks目录中呢?那么此时就要考虑到另一个特性:
3. submodule.name
会决定我们的submodule
拷贝的时候会拷贝到哪个目录
换句话说,实际上选择路径:
.gitmodules
中找到submodule_path
,并且再从本地找到对应的:
git clone
访问到最终的路径。
<submodule_path>
中对应的.git
文件,让其指向一个适合的位置,这个位置中包含大部分普通的submodule git
中的正常内容,以及一个被篡改过的hook
文件,此时按照这个模式来构建git repo
,当受害者尝试进行对应repo
的submodule
更新的时候,就能实现劫持攻击。
这里我们<a ""="" class="weapp_text_link js_weapp_entry" href="">参考的攻击脚本中,由于涉及两个repo的操作(利用第二个repo触发漏洞),它将其中一个(evil)repo中的.git改向了伪造后的fake_dir/modules/submod/.git
,主要是为了保证git commit
能够工作,从而让提交能够成功。我们这边就完全按照它的exp来模拟整个攻击:
1. 创建一个fakegit
目录作为伪造的文件夹,同时为了保持git的目录结构,其内容一定要为:
submod
为之后将要进行clone操作的submodule_path
。
2. 添加两个准备用于触发的子模块,第一个用于布置漏洞,第二个用于触发hook:
submod
和aaa
即为之前提到的submodule_path
3. 将此时生成好的.git/modules/submod
拷贝到fakegit/modules/submod
:
post-checkout
):
.gitmodule
,将其中的submodule.name
由submod
改为../../fakegit/modules/submod
fakegit/modules/submod
视为.git/modules/submod
,从而方便我们劫持。
- 为了保障git的一致性,修改fakegit/.git中的内容,使其指向
fakegit/modules/submod
fakegit/modules/submod
为真正的子目录。
5. 提交修改,commit,完成所有操作;
6. 当受害者尝试拷贝git repo内容的时候,最终会因为识别到错误的目录,最终触发对应的post-checkout
文件,实现RCE。
submodule
的路径,确认其是否是一个../
等有害路径,防止路径穿越。
submodule
再次出现了类似的漏洞,这一次其影响范围相较之前变小了不少,这次它只影响 Windows 和 Mac 操作系统。
这次的漏洞依然发生在git clone阶段,并且同样是操作submodule
模块。利用方式和之前类似,不过这次通过劫持.git目录,从而导致文件写入的发生。
3.2.1 漏洞成因
这个漏洞git的官方仓库中给了测试用例,用来检测漏洞是否存在:
hook
的仓库,这个仓库添加完以后目录结构如下:
hooks/post-checkout
,当然,由于这个脚本本身并未放在本仓库的.git
目录中,当这个仓库被clone的时候脚本并不会被触发。
后半段为漏洞的主要成因,其首先创建了一个叫做captain
的仓库,然后调用了这个指令:
<name>
:x/y
<submodule_repo>
:$hook_repo_path
<submodule_path>
:A/modules/x
当调用这个指令之后,git会做如下的事情:
- 在
captain
目录中创建一个叫做A/modules/x
的子目录,这个目录将会存放来自"$hook_repo_path"
(也就是前面添加的hook仓库)中的所有内容; - 上述步骤中,拷贝到
captain
仓库的hook
仓库中的.git
文件被替换成符号链接,指向../../../.git/modules/x/y
,也就是<target_repo>/.git/modules/<name>
的路径,这里会存放真正的hook
的.git
目录; - 在
captain
的.git
目录中的modules
目录下,创建x/y
目录,并且往其中拷贝所有的hooks/.git
的内容; - 创建
.gitmodule
目录。
此时,captain中比较重要的文件结构如下:
1. 实际存放了hook
仓库中.git
的路径
hook
仓库内容的路径:
captain
的视角上看,子模块hook
仓库中存放post-checkout
的路径:
captain
的视角上看,子模块hook
仓库.git
中hooks
的路径为:
A
和.git
部分,这就是这个漏洞攻击的一个前提。在完成了布置之后,脚本会执行如下的逻辑:
.git
。这个操作会存放在git的索引中,而不会直接在目录中存在。实际上,这样操作完之后,目录结构如下:
.git
的对象索引中,所以粗略一看是无法找到有问题的部分的。但是,当我们在对captain
仓库进行clone的时候,这个符号链接a
就会被释放出来。
最后执行:
此时,我们可以模拟以下整个攻击流程:
当我们在进行clone的时候,程序首先尝试将captain
目录拷贝下来,执行ed64559167
的操作,此时根据顺序,首先会创建这样的目录(tree)
a
(Blob),此时由于大小写不敏感的特点,此时目录会变成:
41eaba3
中的对象进行释放,整个对象指向的为:
A
目录指向的内容一点点进行释放,此时的释放路径变为:
A
此时被a
顶替,a
指向了.git
,所以此时释放的路径改变为:
captain
仓库中的.git/modules/x/y/hooks/post-checkout
就成为了原本存放在hook目录中的一个脚本。而当完成了clone之后,最终captain
目录中的git
会尝试将hook
的内容进行checkout
操作,此操作最终就会诱发对应的post-checkout
,导致脚本被执行!
dir_contains_only_dotgit
的函数:
clone_data_path
实际上为<target_repo>/<name>
可以看到,在clone_submodule
阶段,程序会保证本地路径满足以下条件才会进行clone操作:
- 当进行clone操作前,目的地址为空;
- 完成预备环境准备后(
safe_create_leading_directories_const
)和submodule
的clone
(但是不立即检出check-out
工作目录中的文件)(run_command(&cp)
)后,程序检查目标目录中是否仅包含.git
文件。
此时这里的submodule
的clone
操作实际上执行的。
![]()
这个指令会将submodule的内容拷贝到根目录,但是不进行检出,也就是说此时 .git 中已经存放了 submodule 的 .git,但是还未发生检出(check-out)动作。
.git
中的内容放置在.git/modules/x/y
这个路径下。
而根据我们之前的漏洞分析,clone_data_path
,也就是<target_repo>/<name>
,target_repo/A/modules/x/
。如果未有漏洞的影响下,此时的路径实际上是:
Git 类的漏洞代表了非常典型的一种类型逻辑漏洞:较为复杂的功能之下容易掩盖一些被人们忽略的攻击面。在研究这个漏洞之前,笔者也未在意过这类工具底层实现的细节。在使用工具的时候,可以额外关注工具的实现细节,往往会发现一些意想不到的完全问题。
2、<a ""="" class="weapp_text_link js_weapp_entry" href="">CVE-2018-11235 git RCE
未经作者同意,不得转载
天工实验室安全研究员,Datacon 2023 出题人,主攻二进制漏洞挖掘。
原文始发于微信公众号(破壳平台):Git中出乎意料的攻击面
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论