GHSA-6P8H-3WGX-97GF
Vulnerability from github – Published: 2026-07-24 16:42 – Updated: 2026-07-24 16:42Summary
GitPython's unsafe_git_clone_options denylist omits --template. git clone --template=<dir> copies <dir>/hooks/ into the new repository and runs them (post-checkout fires during clone), so a caller who can influence clone options can achieve arbitrary command execution in the default allow_unsafe_options=False configuration.
Root Cause
base.py:145-152 defines unsafe_git_clone_options = ["--upload-pack","-u","--config","-c"] — --template is absent. The guard candidate ['--template'] passes check_unsafe_options (verified). git copies the hook directory and executes post-checkout at checkout time. git's protocol.allow/GIT_ALLOW_PROTOCOL do not gate --template; the incomplete denylist is the only defense.
Impact
Arbitrary OS command execution during clone (default config). Requires an attacker-readable directory containing an executable hook — a genuine second precondition (realistic via shared filesystems, upload dirs, /tmp, or attacker-writable network paths), reflected as AC:H.
Proof of Concept
# 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)
Attack Chain
- Setup: attacker stages
<dir>/hooks/post-checkout(chmod +x). Guard: n/a (filesystem). - Entry:
Repo.clone_from(url, path, template='<dir>'). Guard:check_unsafe_options(candidates=['--template'], unsafe=unsafe_git_clone_options). Bypass proof:--templatenot on the denylist -> passes (verified candidate['--template'], no error). - Sink: git copies the hook and executes
post-checkoutat checkout. Impact: ACE, default config (verified marker created).
Bypass Evidence
Live-verified on HEAD (tag 3.1.53): guard candidate ['--template'] passed with no error; staged post-checkout hook executed during clone_from, creating the marker. Independent of the value-smuggle bypass (--template is a legitimate long option that survives any single-char-value fix). Not covered by any existing advisory.
Affected Versions
<= 3.1.53
Suggested Fix
Add --template (and audit for other hook/exec-influencing options) to unsafe_git_clone_options.
Reported by zx (Jace) — GitHub: @manus-use
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 3.1.53"
},
"package": {
"ecosystem": "PyPI",
"name": "GitPython"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "3.1.54"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-184",
"CWE-78"
],
"github_reviewed": true,
"github_reviewed_at": "2026-07-24T16:42:09Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "## Summary\nGitPython\u0027s `unsafe_git_clone_options` denylist omits `--template`. `git clone --template=\u003cdir\u003e` copies `\u003cdir\u003e/hooks/` into the new repository and runs them (`post-checkout` fires during clone), so a caller who can influence clone options can achieve arbitrary command execution in the default `allow_unsafe_options=False` configuration.\n\n## Root Cause\n`base.py:145-152` defines `unsafe_git_clone_options = [\"--upload-pack\",\"-u\",\"--config\",\"-c\"]` \u2014 `--template` is absent. The guard candidate `[\u0027--template\u0027]` passes `check_unsafe_options` (verified). git copies the hook directory and executes `post-checkout` at checkout time. git\u0027s `protocol.allow`/`GIT_ALLOW_PROTOCOL` do not gate `--template`; the incomplete denylist is the only defense.\n\n## Impact\nArbitrary OS command execution during clone (default config). Requires an attacker-readable directory containing an executable hook \u2014 a genuine second precondition (realistic via shared filesystems, upload dirs, `/tmp`, or attacker-writable network paths), reflected as AC:H.\n\n## Proof of Concept\n```python\n# attacker stages \u003cdir\u003e/hooks/post-checkout (chmod +x)\nfrom git import Repo\nRepo.clone_from(src, dst, template=\u0027\u003cdir\u003e\u0027) # post-checkout hook executes -\u003e marker created (verified)\n```\n\n## Attack Chain\n1. Setup: attacker stages `\u003cdir\u003e/hooks/post-checkout` (chmod +x). Guard: n/a (filesystem).\n2. Entry: `Repo.clone_from(url, path, template=\u0027\u003cdir\u003e\u0027)`. Guard: `check_unsafe_options(candidates=[\u0027--template\u0027], unsafe=unsafe_git_clone_options)`. Bypass proof: `--template` not on the denylist -\u003e passes (verified candidate `[\u0027--template\u0027]`, no error).\n3. Sink: git copies the hook and executes `post-checkout` at checkout. Impact: ACE, default config (verified marker created).\n\n## Bypass Evidence\nLive-verified on HEAD (tag 3.1.53): guard candidate `[\u0027--template\u0027]` passed with no error; staged `post-checkout` hook executed during `clone_from`, creating the marker. Independent of the value-smuggle bypass (`--template` is a legitimate long option that survives any single-char-value fix). Not covered by any existing advisory.\n\n## Affected Versions\n`\u003c= 3.1.53`\n\n## Suggested Fix\nAdd `--template` (and audit for other hook/exec-influencing options) to `unsafe_git_clone_options`.\n\n---\nReported by **zx (Jace)** \u2014 GitHub: @manus-use",
"id": "GHSA-6p8h-3wgx-97gf",
"modified": "2026-07-24T16:42:09Z",
"published": "2026-07-24T16:42:09Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-6p8h-3wgx-97gf"
},
{
"type": "WEB",
"url": "https://github.com/gitpython-developers/GitPython/pull/2180"
},
{
"type": "WEB",
"url": "https://github.com/gitpython-developers/GitPython/commit/ffcb5359e87619f4fe4a70a4aff5f08c5580ba97"
},
{
"type": "PACKAGE",
"url": "https://github.com/gitpython-developers/GitPython"
},
{
"type": "WEB",
"url": "https://github.com/gitpython-developers/GitPython/releases/tag/3.1.54"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "GitPython: Incomplete unsafe_git_clone_options denylist omits --template enabling arbitrary command execution via clone hooks"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.