Untrusted search path on Windows systems leading to arbitrary code execution
Description
GitPython is a python library used to interact with Git repositories. When resolving a program, Python/Windows look for the current working directory, and after that the PATH environment. GitPython defaults to use the git command, if a user runs GitPython from a repo has a git.exe or git executable, that program will be run instead of the one in the user's PATH. This is more of a problem on how Python interacts with Windows systems, Linux and any other OS aren't affected by this. But probably people using GitPython usually run it from the CWD of a repo. An attacker can trick a user to download a repository with a malicious git executable, if the user runs/imports GitPython from that directory, it allows the attacker to run any arbitrary commands. There is no fix currently available for windows users, however there are a few mitigations. 1: Default to an absolute path for the git program on Windows, like C:\\Program Files\\Git\\cmd\\git.EXE (default git path installation). 2: Require users to set the GIT_PYTHON_GIT_EXECUTABLE environment variable on Windows systems. 3: Make this problem prominent in the documentation and advise users to never run GitPython from an untrusted repo, or set the GIT_PYTHON_GIT_EXECUTABLE env var to an absolute path. 4: Resolve the executable manually by only looking into the PATH environment variable.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
GitPython on Windows may execute a malicious 'git.exe' from the current working directory, enabling arbitrary command execution when a user runs the library from an untrusted repository.
Vulnerability
Overview
CVE-2023-40590 is a path search vulnerability in GitPython, a Python library for interacting with Git repositories. On Windows systems, Python's subprocess module searches the current working directory (CWD) before the system PATH when resolving executable names [1]. GitPython defaults to invoking the git command without an absolute path, so if a user runs GitPython from a repository containing a malicious git.exe or git executable, that file will be executed instead of the legitimate Git installation [2].
Exploitation
An attacker can exploit this by tricking a user into cloning or downloading a repository that contains a malicious executable named git.exe (or git). When the user subsequently runs GitPython from that repository's directory—a common workflow—the library will inadvertently launch the attacker-controlled binary. No special privileges or authentication are required beyond the user's ability to clone and navigate into the repository [2].
Impact
Successful exploitation allows the attacker to execute arbitrary commands with the privileges of the user running GitPython. This can lead to full compromise of the user's system, including data theft, installation of malware, or lateral movement within a network [2].
Mitigation and
Fix
At the time of disclosure, no official patch was available for Windows users. Recommended mitigations include setting the GIT_PYTHON_GIT_EXECUTABLE environment variable to an absolute path (e.g., C:\Program Files\Git\cmd\git.EXE), or avoiding the use of GitPython from untrusted repositories [2]. A subsequent pull request (PR #1636) addresses the issue by setting the NoDefaultCurrentDirectoryInExePath environment variable during subprocess calls on Windows, preventing the CWD from being searched [4]. Users are advised to update to a version containing this fix or apply the mitigations.
AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
GitPythonPyPI | < 3.1.33 | 3.1.33 |
Affected products
2- Range: <= 3.1.32
Patches
18b75434e2c8aMerge pull request #1636 from EliahKagan/cve-2023-40590
2 files changed · +52 −18
git/cmd.py+21 −17 modified@@ -5,7 +5,7 @@ # the BSD License: http://www.opensource.org/licenses/bsd-license.php from __future__ import annotations import re -from contextlib import contextmanager +import contextlib import io import logging import os @@ -14,6 +14,7 @@ import subprocess import threading from textwrap import dedent +import unittest.mock from git.compat import ( defenc, @@ -963,8 +964,11 @@ def execute( redacted_command, '"kill_after_timeout" feature is not supported on Windows.', ) + # Only search PATH, not CWD. This must be in the *caller* environment. The "1" can be any value. + patch_caller_env = unittest.mock.patch.dict(os.environ, {"NoDefaultCurrentDirectoryInExePath": "1"}) else: cmd_not_found_exception = FileNotFoundError # NOQA # exists, flake8 unknown @UndefinedVariable + patch_caller_env = contextlib.nullcontext() # end handle stdout_sink = PIPE if with_stdout else getattr(subprocess, "DEVNULL", None) or open(os.devnull, "wb") @@ -980,21 +984,21 @@ def execute( istream_ok, ) try: - proc = Popen( - command, - env=env, - cwd=cwd, - bufsize=-1, - stdin=istream or DEVNULL, - stderr=PIPE, - stdout=stdout_sink, - shell=shell is not None and shell or self.USE_SHELL, - close_fds=is_posix, # unsupported on windows - universal_newlines=universal_newlines, - creationflags=PROC_CREATIONFLAGS, - **subprocess_kwargs, - ) - + with patch_caller_env: + proc = Popen( + command, + env=env, + cwd=cwd, + bufsize=-1, + stdin=istream or DEVNULL, + stderr=PIPE, + stdout=stdout_sink, + shell=shell is not None and shell or self.USE_SHELL, + close_fds=is_posix, # unsupported on windows + universal_newlines=universal_newlines, + creationflags=PROC_CREATIONFLAGS, + **subprocess_kwargs, + ) except cmd_not_found_exception as err: raise GitCommandNotFound(redacted_command, err) from err else: @@ -1144,7 +1148,7 @@ def update_environment(self, **kwargs: Any) -> Dict[str, Union[str, None]]: del self._environment[key] return old_env - @contextmanager + @contextlib.contextmanager def custom_environment(self, **kwargs: Any) -> Iterator[None]: """ A context manager around the above ``update_environment`` method to restore the
test/test_git.py+31 −1 modified@@ -4,10 +4,12 @@ # # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php +import contextlib import os +import shutil import subprocess import sys -from tempfile import TemporaryFile +from tempfile import TemporaryDirectory, TemporaryFile from unittest import mock from git import Git, refresh, GitCommandError, GitCommandNotFound, Repo, cmd @@ -20,6 +22,17 @@ from git.compat import is_win +@contextlib.contextmanager +def _chdir(new_dir): + """Context manager to temporarily change directory. Not reentrant.""" + old_dir = os.getcwd() + os.chdir(new_dir) + try: + yield + finally: + os.chdir(old_dir) + + class TestGit(TestBase): @classmethod def setUpClass(cls): @@ -75,6 +88,23 @@ def test_it_transforms_kwargs_into_git_command_arguments(self): def test_it_executes_git_to_shell_and_returns_result(self): self.assertRegex(self.git.execute(["git", "version"]), r"^git version [\d\.]{2}.*$") + def test_it_executes_git_not_from_cwd(self): + with TemporaryDirectory() as tmpdir: + if is_win: + # Copy an actual binary executable that is not git. + other_exe_path = os.path.join(os.getenv("WINDIR"), "system32", "hostname.exe") + impostor_path = os.path.join(tmpdir, "git.exe") + shutil.copy(other_exe_path, impostor_path) + else: + # Create a shell script that doesn't do anything. + impostor_path = os.path.join(tmpdir, "git") + with open(impostor_path, mode="w", encoding="utf-8") as file: + print("#!/bin/sh", file=file) + os.chmod(impostor_path, 0o755) + + with _chdir(tmpdir): + self.assertRegex(self.git.execute(["git", "version"]), r"^git version\b") + def test_it_accepts_stdin(self): filename = fixture_path("cat_file_blob") with open(filename, "r") as fh:
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
9- github.com/advisories/GHSA-wfm5-v35h-vwf4ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2023-40590ghsaADVISORY
- docs.python.org/3/library/subprocess.htmlghsax_refsource_MISCWEB
- github.com/gitpython-developers/GitPython/commit/8b75434e2c8a082cdeb4971cc6f0ee2bafec45bcghsaWEB
- github.com/gitpython-developers/GitPython/issues/1635ghsaWEB
- github.com/gitpython-developers/GitPython/pull/1636ghsaWEB
- github.com/gitpython-developers/GitPython/releases/tag/3.1.33ghsaWEB
- github.com/gitpython-developers/GitPython/security/advisories/GHSA-wfm5-v35h-vwf4ghsax_refsource_CONFIRMWEB
- github.com/pypa/advisory-database/tree/main/vulns/gitpython/PYSEC-2023-161.yamlghsaWEB
News mentions
0No linked articles in our index yet.