Описание
GitPython: Command Injection via git long-option prefix abbreviation bypass of CVE-2026-42215 blocklist
Command injection via long-option prefix abbreviation bypassing check_unsafe_options (incomplete fix of CVE-2026-42215 / GHSA-rpm5-65cw-6hj4)
Component: gitpython-developers/GitPython (PyPI: GitPython)
Affected: all versions carrying the 3.1.47 blocklist fix, through current main (verified at commit 20c5e275, 3.1.50-42)
CWE: CWE-184 (Incomplete List of Disallowed Inputs) → CWE-78 (OS Command Injection)
Severity: inherits the parent CVE-2026-42215 surface; estimated High, ~8.8 (AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H) — final scoring deferred to maintainer/CNA, mirroring the parent.
Reporter: hackkim
Summary
The 3.1.47 fix for CVE-2026-42215 blocks dangerous git options (--upload-pack, --config, -c, -u for clone; --upload-pack for fetch/pull; --receive-pack, --exec for push) so callers cannot reach command-executing options unless they pass allow_unsafe_options=True.
The fix canonicalizes an option name along one axis (underscore→hyphen via dashify) and checks it against an exact-match dict. It does not account for git's unambiguous long-option prefix abbreviation. Git accepts any unambiguous prefix of a long option (--upload-p, --upload-pa, --upload-pac all resolve to --upload-pack). So a kwarg key like upload_p canonicalizes to upload-p, misses the blocklist dict, and is emitted to git as --upload-p=<value> → executed as --upload-pack=<value> → command injection, in the default allow_unsafe_options=False configuration.
The asymmetry (root cause)
The guard normalizes only _→- and does exact dict membership. Git's CLI parser accepts a broader grammar (prefix abbreviation) than the guard models, so abbreviated keys slip through and reach git as the blocked option.
Affected code (commit 20c5e275)
| Location | Role |
|---|---|
git/cmd.py:948-960 _canonicalize_option_name | canonicalizer — no prefix expansion |
git/cmd.py:963-974 check_unsafe_options | exact-match dict lookup (the incomplete guard) |
git/cmd.py:1511 transform_kwarg | emits --<dashify(name)>=<value> to the CLI |
git/repo/base.py:1411,1413 | clone call sites |
git/remote.py:1074,1128,1201 | fetch / pull / push call sites |
Bypass keys (verified)
| kwarg key | git resolves to | path | weaponizable |
|---|---|---|---|
upload_p, upload_pac | --upload-pack | clone / fetch / pull | Yes — direct RCE |
receive_p | --receive-pack | push | Yes — direct RCE |
exe | --exec | push | Yes — direct RCE |
conf, confi | --config | clone | bypasses option blocklist; RCE needs an additional config vector (see note) |
Minimal PoC
Self-contained, no network egress (a local bare repo acts as the "remote"). Tested on current main (git 2.50.1):
Equivalent at the shell: git clone --upload-p=/tmp/evil.sh src out runs evil.sh.
Confirmed behavior:
upload_pack(exact) → blocked;upload_p(abbrev) → passes guard, reaches git, executes. The fix works for the form it models but not the abbreviated form.allow_unsafe_options=Trueopt-out behaves as documented (out of scope).
Honest scope note
Like the parent CVE, exploitation requires a host application that flows attacker-controlled kwarg keys into a GitPython clone/fetch/pull/push. Where the host passes only fixed/validated keys, this is not reachable — the vulnerability is in the library's documented defense-in-depth control (allow_unsafe_options=False), which this variant defeats.
On the --config family: conf bypasses the option blocklist, but weaponizing --config protocol.ext.allow=always via an ext:: URL is independently blocked by GitPython's protocol allowlist (allow_unsafe_protocols=False). The directly weaponizable family is upload-pack / receive-pack / exec. Reported transparently — not claiming Critical.
Suggested remediation (any one)
- Prefix-aware matching: reject any option whose canonical name is an unambiguous prefix of a blocked option (≈
startswithon the blocked canonical name, afterdashify). - Disable abbreviation at the sink: pass
--end-of-optionsor invoke git in a way that disables long-option abbreviation. - Allowlist option names on security-sensitive subcommands instead of a blocklist.
Remediation should also cover the -c/--config family abbreviations, even though the ext:: route is currently gated by the protocol allowlist.
Ссылки
- https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-2f96-g7mh-g2hx
- https://github.com/gitpython-developers/GitPython/pull/2161
- https://github.com/gitpython-developers/GitPython/commit/56806080c1348749b07daa4a2024ce47b3cad285
- https://github.com/gitpython-developers/GitPython/releases/tag/3.1.51
Пакеты
GitPython
<= 3.1.50
3.1.51
8.8 High
CVSS3
Дефекты
8.8 High
CVSS3