CVE-2026-69097 Overview
CVE-2026-69097 affects GitPython versions before 3.1.53. The library fails to properly escape section names when writing git config files. Attackers can craft malicious submodule names that inject arbitrary configuration directives into a victim's .git/config. When injected keys such as core.sshCommand are triggered by subsequent git operations, they cause arbitrary command execution on the host running GitPython.
The flaw is categorized under CWE-74: Improper Neutralization of Special Elements in Output. Exploitation requires user interaction, typically cloning or updating a repository that contains a hostile submodule.
Critical Impact
A malicious submodule name can plant core.sshCommand in .git/config, leading to arbitrary command execution when git performs any SSH operation such as fetch, pull, or push.
Affected Products
- GitPython versions prior to 3.1.53
- Applications invoking Repo.create_submodule on attacker-controlled input
- Applications invoking Repo.clone_from against attacker-controlled repositories containing submodules
Discovery Timeline
- 2026-08-03 - CVE-2026-69097 published to the National Vulnerability Database
- 2026-08-05 - Last updated in NVD database
Technical Details for CVE-2026-69097
Vulnerability Analysis
GitPython writes git configuration files through its own config writer. When adding or updating a submodule, the library uses the submodule name as part of a [submodule "<name>"] section header. The library does not sanitize or escape newline, quote, or bracket characters supplied in that name.
An attacker who controls the submodule name can terminate the section header and append arbitrary configuration keys. Git treats subsequent lines as valid directives once the file is loaded. Directives such as core.sshCommand execute shell commands whenever git initiates an SSH transport, converting a parsing flaw into local code execution.
The issue reaches production code through two primary entry points: create_submodule, which writes a new section directly, and clone_from, which recursively materializes submodules declared in .gitmodules from a remote repository. Both paths inherit the same lack of escaping.
Root Cause
The root cause is missing input neutralization in the config serialization routine. Section names are concatenated into the on-disk config format without validating that they contain only permitted characters. A newline embedded in a submodule name breaks out of the header context and injects a new key-value pair under an attacker-chosen section.
Attack Vector
Exploitation follows a repository-poisoning pattern. An attacker publishes a repository whose .gitmodules file declares a submodule with a crafted name containing embedded newlines and a core.sshCommand directive. A victim invokes Repo.clone_from(...) with recursion enabled, or calls create_submodule with attacker-supplied input. GitPython writes the malicious content into .git/config. The next git operation that uses SSH triggers execution of the injected command.
The vulnerability manifests entirely through configuration writing logic; there is no memory corruption, and no network-facing daemon is required. See the GitHub Security Advisory GHSA-3rp5-jjmw-4wv2 and the VulnCheck Advisory for reproduction details.
Detection Methods for CVE-2026-69097
Indicators of Compromise
- Presence of unexpected core.sshCommand, core.editor, or core.pager keys inside .git/config of repositories cloned by automated tooling.
- .gitmodules entries where the submodule name contains newline characters, escape sequences, or embedded = and [ characters.
- Child processes such as sh, bash, or cmd.exe spawned from Python processes running GitPython during clone or fetch operations.
- Git operations that invoke unexpected binaries in place of the standard ssh client.
Detection Strategies
- Scan repositories and CI workspaces for .git/config files containing directives outside the expected [core], [remote], [branch], and [submodule] schema.
- Inventory installed Python packages across build agents and developer workstations, flagging any GitPython version below 3.1.53.
- Parse .gitmodules files with a strict validator that rejects submodule names containing control characters.
Monitoring Recommendations
- Monitor process ancestry for Python interpreters spawning shell interpreters immediately after git subprocess invocation.
- Alert on writes to .git/config that add core.sshCommand or core.hooksPath outside of authorized administrative activity.
- Log outbound SSH connections initiated by CI runners and correlate against expected git remotes.
How to Mitigate CVE-2026-69097
Immediate Actions Required
- Upgrade GitPython to version 3.1.53 or later across all Python environments, including virtualenvs, container images, and CI runners.
- Audit existing .git/config files in shared clone caches and CI workspaces for injected directives, and purge affected repositories.
- Disable recursive submodule cloning in automated pipelines that consume untrusted repositories until upgrades are verified.
Patch Information
The upstream fix is included in GitPython 3.1.53. The patch validates submodule section names and rejects characters that would allow escaping the config header. Application maintainers should pin the minimum version in requirements.txt, pyproject.toml, or equivalent dependency manifests. Refer to the GitHub Security Advisory GHSA-3rp5-jjmw-4wv2 for the authoritative fix reference.
Workarounds
- Validate submodule names against a strict allowlist such as ^[A-Za-z0-9._/-]+$ before passing them to create_submodule.
- Clone untrusted repositories without --recurse-submodules, then inspect .gitmodules and .git/config before initializing submodules.
- Run GitPython-based tooling under least-privilege service accounts with restricted outbound network access to limit post-exploitation impact.
# Pin the fixed GitPython release
pip install --upgrade 'GitPython>=3.1.53'
# Verify the installed version
python -c "import git; print(git.__version__)"
# Audit a workspace for suspicious injected config keys
grep -RInE 'sshCommand|hooksPath|pager|editor' \
$(find . -type f -path '*/.git/config')
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

