VYPR
High severityNVD Advisory· Published Aug 28, 2023· Updated Oct 2, 2024

Untrusted search path on Windows systems leading to arbitrary code execution

CVE-2023-40590

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.

PackageAffected versionsPatched versions
GitPythonPyPI
< 3.1.333.1.33

Affected products

2

Patches

1
8b75434e2c8a

Merge pull request #1636 from EliahKagan/cve-2023-40590

https://github.com/gitpython-developers/GitPythonSebastian ThielSep 1, 2023via ghsa
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

News mentions

0

No linked articles in our index yet.