Apr 27, 2026·7 min read·4 visits
GitPython < 3.1.45 fails to properly filter dangerous Git options when supplied via Python keyword arguments. Attackers can bypass security checks by using underscores instead of hyphens (e.g., `upload_pack` instead of `upload-pack`), resulting in arbitrary remote code execution via the underlying Git executable.
GitPython versions prior to 3.1.45 are vulnerable to a command injection flaw due to an architectural logic error in how keyword arguments are sanitized. The library attempts to block dangerous Git options like `--upload-pack` but performs this validation before applying Pythonic underscore-to-hyphen normalization. This allows attackers to bypass the blocklist using underscore-formatted arguments, leading to arbitrary command execution when the underlying Git binary is invoked.
GitPython is a Python library used to interact with Git repositories, providing an object-oriented wrapper around the underlying Git command-line interface. Developers frequently use this library to programmatically clone, fetch, pull, and manipulate repositories. The vulnerability, identified as GHSA-RPM5-65CW-6HJ4, exists within the parameter processing pipeline where Python keyword arguments are translated into Git command-line options.
To bridge Python conventions and POSIX command-line flags, GitPython allows developers to pass keyword arguments (kwargs) using underscores, which the library automatically normalizes into hyphens. For example, passing upload_pack='...' in Python translates to --upload-pack='...' at the command line. This normalization is a core feature designed to maintain Python code readability while adhering to Git's expected argument syntax.
GitPython maintains a security blocklist to prevent the injection of dangerous Git options that natively support command execution, such as --upload-pack, --receive-pack, and --extcmd. The vulnerability occurs because the library executes this security validation step before performing the underscore-to-hyphen normalization. Consequently, an attacker can supply the dangerous arguments in their pre-normalized underscore form to bypass the security filter entirely.
Once the validation step completes successfully, GitPython normalizes the malicious keyword argument and passes the hyphenated flag to the Git subprocess. This results in the execution of arbitrary operating system commands with the privileges of the Python process invoking the Git wrapper.
The root cause of GHSA-RPM5-65CW-6HJ4 is an architectural sequencing error in the input sanitization pipeline. Effective input validation mandates that data must be normalized to its final, canonical representation before any security checks are applied. GitPython violates this principle by evaluating the input in its raw form rather than its normalized execution form.
When a developer invokes a Git command via the GitPython API, the library packages the provided keyword arguments into a dictionary. The security function iterates over the keys in this dictionary, checking against a hardcoded list of restricted strings. The blocklist specifically contains the standard Git command-line formats, specifically upload-pack, receive-pack, and extcmd.
If the dictionary key is provided as upload_pack, the string comparison against the blocklist fails, and the security check permits the argument. Following this validation, the library's command builder processes the dictionary to construct the actual shell command string. During this phase, it replaces all underscores in the dictionary keys with hyphens to match Git's parameter requirements.
The normalized dictionary keys are then concatenated with their respective values and appended to the Git binary path. The resulting command array is passed to Python's subprocess execution module. By processing the validation phase prematurely, GitPython guarantees that the command builder will subsequently mutate the validated input into an unsafe state, completely nullifying the intended security controls.
Understanding the vulnerability requires analyzing the exact data flow of the keyword arguments. The flaw exists in the method responsible for transforming the **kwargs dictionary into the final list of arguments passed to the subprocess.Popen call.
The implementation flaw is evident in the execution order. The Python dictionary {'upload_pack': 'touch /tmp/pwned'} is passed to the validation routine. The routine checks for the literal string upload-pack. Since upload_pack (with an underscore) does not match upload-pack (with a hyphen), no exception is raised.
After the validation loop completes, a separate processing loop handles the command string assembly. This loop takes upload_pack, runs a string replace operation for underscores, and yields --upload-pack. It then appends the provided value, resulting in --upload-pack=touch /tmp/pwned.
The patch for this vulnerability involves shifting the validation logic to occur after the normalization step, or explicitly checking both the underscore and hyphenated variations of the dangerous parameters. By normalizing the dictionary keys to their canonical hyphenated state first, the security check correctly identifies the prohibited arguments regardless of how they were initially formatted in the Python code.
Exploitation of this vulnerability requires the application using GitPython to accept user-controlled input and apply it as a keyword argument to a Git command execution function. This pattern is increasingly common in automated CI/CD pipelines, repository management tools, and "agentic" AI systems that dynamically generate operational commands based on user prompts.
An attacker crafts a malicious payload targeting a remote Git operation, such as clone, fetch, pull, or push. The payload specifies the upload_pack argument, utilizing the underscore format to ensure the application processes it without triggering the built-in GitPython exception. The value of this argument contains the actual operating system commands the attacker wishes to execute.
import git
# The attacker controls this input variable via a web form or API endpoint
dangerous_option = "touch /tmp/pwned"
repo = git.Repo.init("/tmp/test-repo")
# The developer passes the user input as a keyword argument
# The underscore bypasses the GitPython security blocklist
repo.git.fetch(upload_pack=dangerous_option)When the vulnerable application processes this code, GitPython constructs and executes the command git fetch --upload-pack='touch /tmp/pwned'. The git fetch operation natively supports the --upload-pack parameter to specify the path to the git-upload-pack binary on the remote end. However, Git executes the provided string as a shell command to initialize the pack transfer, resulting in immediate arbitrary code execution on the local host processing the fetch request.
The vulnerability enables arbitrary OS command execution, representing a critical risk to systems utilizing vulnerable versions of GitPython. The injected commands execute with the identical permissions and environment variables of the Python application hosting the GitPython library.
In environments where GitPython operates as part of a backend web service or API, successful exploitation yields a full remote shell or direct access to the underlying filesystem. Attackers can leverage this foothold to exfiltrate sensitive data, manipulate internal databases, or extract environment variables containing secondary authentication tokens, such as AWS credentials or database passwords.
This vulnerability is particularly consequential for AI-driven development tools and automated repository management systems. These platforms frequently parse user instructions or arbitrary repository data and map them into dynamically constructed Git commands. If these inputs dictate the keyword arguments passed to the GitPython wrapper, the system is immediately vulnerable to complete compromise without requiring authentication bypasses in the primary web application.
The definitive remediation for this vulnerability is upgrading GitPython to version 3.1.45 or later. The patched release restructures the validation logic to accurately identify and block dangerous Git options, even when they are supplied using Python's underscore-based keyword argument syntax. Organizations should update dependencies via pip, poetry, or their respective package managers immediately.
If upgrading is not operationally feasible in the short term, developers must implement strict input validation at the application layer. Before passing any dynamic dictionary or user-controlled input to GitPython via **kwargs, the application must manually inspect all keys. Any keys matching upload_pack, receive_pack, extcmd, or their hyphenated equivalents must be explicitly rejected.
Furthermore, developers should adopt a strict allowlist approach when mapping untrusted data to system command interfaces. Avoid dynamically expanding untrusted dictionaries directly into function calls. Instead, map specific, expected user inputs to hardcoded arguments within the application logic, ensuring that arbitrary Git options cannot be introduced into the command pipeline regardless of library-level validation failures.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
GitPython gitpython-developers | < 3.1.45 | 3.1.45 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-78: Improper Neutralization of Special Elements used in an OS Command |
| Attack Vector | Network / Context Dependent |
| Impact | Remote Code Execution (RCE) |
| CVSS Score | 8.8 (Estimated) |
| Exploit Status | Proof of Concept Available |
| Vulnerable Component | Git command wrappers (`repo.git.*`) |
The software constructs all or part of an OS command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended OS command when it is sent to a downstream component.