# GitPython: Incomplete unsafe_git_clone_options denylist omits --template enabling arbitrary command execution via clone hooks

GitPython's unsafe_git_clone_options denylist omits --template, so clone_from(template=...) copies attacker hooks and executes post-checkout, yielding OS command execution.

- Payout: Research
- Severity: high
- Program: GitPython
- Found by: manus-use
- Disclosed: 2026-07-24
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-6p8h-3wgx-97gf
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-6p8h-3wgx-97gf
- 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 Repo.clone_from option handling (base.py check_unsafe_options)
**Root cause:** unsafe_git_clone_options lists only --upload-pack/-u/--config/-c, omitting --template; git clone --template copies <dir>/hooks into the repo and runs post-checkout during checkout, so the denylist is the only defense and it fails.
**Steps:**
1. Stage attacker directory `<dir>/hooks/post-checkout` with executable bit set.
2. Call `Repo.clone_from(src, dst, template='<dir>')`.
3. Guard `check_unsafe_options(candidates=['--template'], unsafe=unsafe_git_clone_options)` passes because --template is not denylisted.
4. git copies hooks and executes `post-checkout` at checkout, creating the marker.
**How found:** Audited the unsafe_git_clone_options denylist for missing git options that influence hook execution; hypothesized --template survives the guard and verified the candidate passes check_unsafe_options and that post-checkout fires during clone.
**Exploitation:** Attacker needs an attacker-readable/writable directory containing an executable post-checkout hook (shared filesystem, upload dir, /tmp, network path). Passing template=<dir> to clone_from bypasses the denylist and git executes the hook during clone, giving arbitrary command execution in default allow_unsafe_options=False config.
**Insight:** Denylists of dangerous git flags must cover every option that can inject executable content (--template, hook paths, config includes); test each candidate against check_unsafe_options directly rather than assuming the guard is exhaustive.

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

**Python PoC clone_from with template hook**

```
# attacker stages <dir>/hooks/post-checkout (chmod +x)
from git import Repo
Repo.clone_from(src, dst, template='<dir>')   # post-checkout hook executes -> marker created (verified)
```

