# GitPython: Unguarded git read-tree option forwarding in IndexFile.from_tree/reset/merge_tree enables arbitrary file overwrite

GitPython's IndexFile.from_tree/reset/merge_tree pass caller-controlled treeish to git read-tree without a `--`, letting `--index-output` overwrite arbitrary files with an index blob.

- Payout: Research
- Severity: high
- Program: GitPython
- Found by: manus-use, bhaswanthc
- Disclosed: 2026-08-07
- CVE: CVE-2026-76219
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-4gmw-gg2m-w46p
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-4gmw-gg2m-w46p
- Tags: area:oss domain:oss impact:dos impact:integrity kind:research platform:ghsa sev:high tech:python technique:incomplete-fix technique:regression technique:source-review vuln:command-injection

**Target:** Python library wrapper around git read-tree (IndexFile methods)
**Root cause:** from_tree, reset and merge_tree append caller-influenced treeish positionally to `repo.git.read_tree(*arg_list)` with no check_unsafe_options guard and no `--` separator, so a treeish beginning with `--index-output=` is parsed as an option.
**Steps:**
1. Call `IndexFile.from_tree(repo, "--index-output=/home/victim/.bashrc")` (or reset(commit=…) / merge_tree(base=…, rhs=…)) with attacker-controlled treeish.
2. No unsafe-option check runs; argv becomes `['git','read-tree','--index-output=<tmp>','--index-output=/…/victim']`.
3. Last-occurrence-wins: git writes the index to the attacker path, overwriting existing content with a `DIRC` index blob.
**How found:** Reviewed the fix commit 3af0c251 for GHSA-3f7w-8rr8-f37f, noticed it guarded only checkout_index and tag, and that read_tree was an acknowledged-but-unfixed unguarded call site; then traced from_tree/reset/merge_tree to the read_tree sink and tested the last-wins `--index-output` override.
**Exploitation:** Any application passing attacker-influenced treeish/commit/ref strings into these IndexFile methods lets the attacker choose an arbitrary writable path; git read-tree's internal `--index-output=<tmp>` is overridden by the injected later occurrence, clobbering the target file with a valid git-index blob at host process privileges.
**Insight:** When a prior advisory's fix sweeps unsafe-option sinks, enumerate every remaining `repo.git.<cmd>(*args)` call site the fix skipped — and check whether the command has a path-taking option whose later occurrence overrides an internally supplied one (last-wins), turning a benign-looking temp path into an arbitrary-write primitive.

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

**from_tree call with injected --index-output**

```
IndexFile.from_tree(repo, "--index-output=/home/victim/.bashrc")
# target overwritten with a valid git-index blob (DIRC...)
```

**Observed argv with duplicate --index-output**

```
['git','read-tree','--index-output=<tmp>','--index-output=/…/victim']
```

**Reproduction showing victim overwritten with DIRC blob**

```
IndexFile.from_tree(repo,'--index-output=<victim>')` → victim overwritten; before=`IMPORTANT ORIGINAL CONTENT`, after starts `DIRC\x00\x00\x00\x02…`
```

