May 9, 2026·8 min read·2 visits
Newline injection in GitPython's config_writer section parameter allows attackers to override core.hooksPath and achieve Remote Code Execution.
GitPython versions prior to 3.1.50 are vulnerable to a newline injection attack in the `config_writer()` and `set_value()` methods. An incomplete fix for CVE-2026-44244 failed to sanitize the configuration section parameter, allowing an attacker to inject malicious Git configuration blocks such as `[core]` and override the `hooksPath`. This leads to unauthenticated remote code execution when subsequent Git operations trigger the injected hooks.
GHSA-MV93-W799-CJ2W identifies a high-severity newline injection vulnerability in the GitPython library, specifically affecting the config_writer() and set_value() methods. GitPython is a Python library used to interact with Git repositories, often deployed in automation scripts, CI/CD pipelines, and backend repository management services. The vulnerability arises when an application permits user-controlled data to define the section header of a Git configuration file.
This flaw is classified under CWE-94 (Improper Control of Generation of Code) and CWE-150 (Improper Neutralization of Escape, Meta, or Control Sequences). The core issue stems from an incomplete remediation of a prior vulnerability, CVE-2026-44244. While the previous patch successfully neutralized newline characters in configuration values, it neglected to apply the same sanitization logic to the section name parameters.
By supplying a crafted section name containing a newline character (\n) followed by a forged [core] section header, an attacker can manipulate the generated .git/config file. This manipulation alters the repository's configuration structure, enabling the attacker to set arbitrary Git options. The primary attack vector involves modifying the core.hooksPath directive to point to a directory under the attacker's control.
The resulting impact is unauthenticated Remote Code Execution (RCE). When a user or system process executes a subsequent Git operation that triggers a hook—such as commit, merge, checkout, or pull—the Git binary evaluates the injected configuration and executes the scripts located in the modified hook path. The malicious code runs with the privileges of the user invoking the Git command.
The root cause of this vulnerability lies in the insufficient input validation within GitConfigParser.set_value() and the underlying configuration writing mechanisms. When GitPython writes a configuration setting to .git/config, it formats the section header by wrapping the provided section name in square brackets. The function does not strip or encode newline characters present in the section string variable.
Version 3.1.49 introduced a validation check specifically for the configuration value parameter to prevent malicious multiline entries. The patch omitted the section parameter from this validation routine. Consequently, the input mysection\n[core]\nhooksPath = /tmp/evil_hooks is processed verbatim. GitPython constructs the file output by concatenating the brackets around this payload, resulting in a syntactically malformed but exploitable configuration file.
The standard Git configuration parser operates with a high degree of leniency to accommodate manual edits and varied formatting. When the Git binary reads the manipulated .git/config file, it encounters the opening bracket [mysection followed by a newline. It then parses the subsequent lines [core] and hooksPath = /tmp/evil_hooks as a valid, overriding configuration block, ignoring the malformed bracket syntax of the initial injected line.
This leniency creates a bridge between configuration injection and code execution via the core.hooksPath directive. The core.hooksPath setting is designed to override the default .git/hooks directory used for client-side and server-side hooks. By injecting this directive, the attacker instructs the Git binary to bypass the repository's standard hooks and instead execute any identically named executable files located within the designated attacker-controlled directory.
The vulnerability manifests in the way GitPython's configuration parser serializes configuration sections to disk. In vulnerable versions (prior to 3.1.50), the set_value method constructs the ini-style configuration block without scrutinizing the section argument for control characters. The implementation directly interpolates the string into the file stream.
When a developer utilizes the API call repo.config_writer().set_value(user_section, "key", "value"), the library formats the output by prepending [ and appending ]. If the user_section variable contains the payload dummy\n[core]\nhooksPath = /tmp/evil, the resulting serialization produces an ini file structure that breaks out of the intended section block.
> [!NOTE]
> The resulting .git/config file structure demonstrates the parser confusion exploit.
> ```ini
[dummy [core] hooksPath = /tmp/evil] key = value
The fix implemented in version 3.1.50 addresses this discrepancy by extending the newline sanitization logic to cover all inputs used in the configuration block construction. The patched version enforces a strict filter on both the section and the value variables, raising an error or stripping the newline characters before file serialization occurs. This mirrors the mitigation strategy previously applied to the value parameter in version 3.1.49.
By reviewing the remediation, it is evident that the complete fix requires comprehensive sanitization of all user-controllable parameters that interact with the Git configuration file syntax. Developers maintaining custom wrappers around configuration parsers must ensure that section headers, keys, and values are entirely devoid of carriage return (\r) and line feed (\n) characters to prevent similar structural injection attacks.
Exploiting this vulnerability requires the attacker to fulfill two prerequisites. First, the target application must accept external input and pass it directly to the section parameter of GitPython's config_writer().set_value() method. Second, the attacker must have the ability to stage a malicious script on the target system's filesystem, or reference an existing binary, to be executed by the Git hook mechanism.
The attack begins with the attacker placing an executable script named after a standard Git hook, such as pre-commit, in an accessible directory like /tmp/evil/. The script contains the arbitrary commands the attacker wishes to execute. The attacker then submits the carefully crafted injection string "dummy\n[core]\nhooksPath = /tmp/evil" to the vulnerable application endpoint responsible for updating repository configurations.
The vulnerable application processes the input and calls GitPython, which writes the malicious .git/config file. The repository is now weaponized. The payload remains dormant until a Git operation that evaluates hooks is executed within the context of the compromised repository. Common trigger operations include git commit -m "update", git push, or git pull.
Upon execution of the trigger operation, the Git binary reads the local .git/config file. It parses the injected [core] section and processes the hooksPath = /tmp/evil directive. Git then resolves the hook path, locates the attacker's pre-commit script, and executes it. The execution context is bound to the system user that initiated the Git command, yielding immediate and unauthenticated Remote Code Execution.
The exploitation of GHSA-MV93-W799-CJ2W results in full system compromise via Remote Code Execution. An attacker who successfully injects a malicious hooksPath directive gains the ability to execute arbitrary commands with the privileges of the user running the Git process. In CI/CD environments or automated backend systems, this user often possesses elevated permissions, access to deployment credentials, and network routing to internal infrastructure.
The vulnerability carries a CVSS 3.1 Base Score of 7.8 (High), with a vector of CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H. The Attack Vector is classified as Local (AV:L) because the attacker must supply the payload through an application interface that subsequently interacts with the local filesystem's configuration. The Attack Complexity is Low (AC:L) as the exploit relies on standard Git functionality without requiring race conditions or memory corruption primitives.
This attack methodology aligns with multiple MITRE ATT&CK techniques. The primary execution mechanism maps to T1059 (Command and Scripting Interpreter) and T1202 (Indirect Command Execution), as the attacker leverages Git's internal processes to launch external scripts. Additionally, it fulfills T1546.016 (Event Triggered Execution: Installer Packages/Hooks), representing a reliable persistence mechanism if the hook remains undetected.
Applications managing multiple tenant repositories or utilizing user-supplied strings for repository configuration structures face the highest risk. The vulnerability exposes these systems to lateral movement, data exfiltration, and supply chain poisoning if the compromised repositories are used to distribute software artifacts.
The primary and most effective remediation strategy is to upgrade the GitPython package to version 3.1.50 or later. This version implements comprehensive input validation that filters newline characters from the section parameter within the configuration writer. Development teams should audit their requirements.txt or Pipfile to ensure no version pinning restricts the update to vulnerable branches.
For systems where an immediate dependency upgrade is not feasible, security teams must implement strict input validation at the application level. Any user-supplied data intended for use as a configuration section, key, or value must be sanitized. The validation routine must explicitly reject or strip carriage return (\r) and line feed (\n) characters before passing the data to GitPython's API.
Developers should adopt the architectural best practice of avoiding direct user input in configuration paths entirely. GitPython's allow_unsafe_options=False flag (which is enabled by default) provides baseline protection against passing unsafe CLI options, but it does not protect against structural injection in the config file itself. Application logic should map user input to predefined, hardcoded configuration sections rather than directly interpolating strings.
Security operations teams can detect historical exploitation attempts by implementing configuration monitoring. Scanning .git/config files across the infrastructure for multiple [core] sections, anomalous bracket formatting, or hooksPath directives pointing to non-standard directories (such as /tmp/, /dev/shm/, or web-accessible folders) will identify weaponized repositories. Existing Security Information and Event Management (SIEM) systems should alert on unusual child processes spawning from the git binary.
| Product | Affected Versions | Fixed Version |
|---|---|---|
GitPython gitpython-developers | < 3.1.50 | 3.1.50 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-94, CWE-150 |
| Attack Vector | Local (via Application Input) |
| CVSS Base Score | 7.8 (High) |
| Exploit Status | Proof of Concept Available |
| Impact | Remote Code Execution via Git Hooks |
| Patched Version | 3.1.50 |
The software constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended software behavior.