# GitPython: git-config OPTION-name injection via =/#/whitespace bypasses name validator, enabling forged core.sshCommand/hooksPath (RCE)

GitPython's option-name validator only blocks CR/LF/NUL, so an option name containing '=' and '#' forges core.sshCommand, yielding RCE on the next git operation.

- Payout: Research
- Severity: high
- Program: GitPython
- Found by: manus-use
- Disclosed: 2026-08-07
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-jm78-9fvv-mhgr
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-jm78-9fvv-mhgr
- Tags: area:oss domain:oss impact:rce kind:research platform:ghsa sev:high tech:python technique:chained technique:incomplete-fix technique:source-review vuln:command-injection

**Target:** GitPython config writer (git/config.py set_value / write_section)
**Root cause:** `_assure_config_name_safe` applies the bracket/quote state machine only when label == "section"; for the "option" label it only rejects [\r\n\x00], so `=`, `#`, `;`, brackets and whitespace pass. `write_section` then writes the option name verbatim into `"\t%s = %s\n"`.
**Steps:**
1. Call `repo.config_writer().set_value("core", "sshCommand = touch /tmp/RCE #", "x")`.
2. Validator `_assure_config_name_safe(option, "option")` at config.py:897 only matches `[\r\n\x00]`; `=`, `#`, space pass with no ValueError.
3. `write_section` (config.py:702) emits `\tsshCommand = touch /tmp/RCE # = x\n`.
4. `git config --get core.sshCommand` returns `touch /tmp/RCE` (rc=0); next ssh git operation executes it.
5. Same primitive works for `core.hooksPath` via GitConfigParser and repo.config_writer().
**How found:** Read the fix commit and noticed the bracket/quote checks sit inside `if label == "section"`, leaving the `"option"` label covered only by the CR/LF/NUL regex; then tested whether `=`/`#`/space in an option name survive into the rendered line.
**Exploitation:** Requires the embedding application to forward a caller-influenced option name into the config writer. The injected `core.sshCommand` fires on any subsequent ssh git operation with no staged file needed; `core.hooksPath` variant needs a staged hook.
**Escalation:** Chaining the forged directive to `core.sshCommand` or `core.hooksPath` converts a config-write primitive into arbitrary command execution.
**Insight:** When a validator is gated on a label/context parameter, enumerate every other label that reaches the same sink — sibling advisories fixed section-name newline/bracket injection but left the option-name field with a weaker regex, and the `#` comment trick lets you truncate the intended value.

## Proof of concept (copied exactly from the original report)

**PoC set_value option-name injection**

```
with repo.config_writer() as cw:
    cw.set_value("core", "sshCommand = touch /tmp/RCE #", "x")
# git config --get core.sshCommand  ->  touch /tmp/RCE
```

