VAITP Dataset

← Back to the dataset

CVE-2023-41040

GitPython Unvalidated file access, potential denial of service

  • CVSS 6.5
  • CWE-22 Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
  • Input Validation and Sanitization
  • Remote

GitPython is a python library used to interact with Git repositories. In order to resolve some git references, GitPython reads files from the `.git` directory, in some places the name of the file being read is provided by the user, GitPython doesn't check if this file is located outside the `.git` directory. This allows an attacker to make GitPython read any file from the system. This vulnerability is present in https://github.com/gitpython-developers/GitPython/blob/1c8310d7cae144f74a671cbe17e51f63a830adbf/git/refs/symbolic.py#L174-L175. That code joins the base directory with a user given string without checking if the final path is located outside the base directory. This vulnerability cannot be used to read the contents of files but could in theory be used to trigger a denial of service for the program. This issue has not yet been addressed.

CVSS base score
6.5
Published
2023-08-30
OWASP
A04 Insecure Design
Orthogonal defect classification
Checking
Code defect classification
Missing Check
Category
Input Validation and Sanitization
Subcategory
Path Traversal
Accessibility scope
Remote
Impact
Denial of Service (DoS)
Fixed by upgrading
Yes

Solution

Update GitPython to version 3.1.18 or higher.

Vulnerable code sample

# VULNERABLE: GitPython command injection on Windows
import git
import os

def clone_and_process_repo(repo_url: str, local_path: str):
    """VULNERABLE: GitPython usage without proper validation."""
    
    # VULNERABILITY: No validation of repository URL or path
    repo = git.Repo.clone_from(repo_url, local_path)
    
    # VULNERABILITY: Execute git commands without path validation
    # On Windows, malicious git.exe in repo can be executed
    try:
        result = repo.git.status()
        
        # VULNERABILITY: Process files without validation
        for item in repo.tree().traverse():
            if item.type == 'blob' and item.path.endswith('.py'):
                file_path = os.path.join(local_path, item.path)
                # CRITICAL: Executing code from untrusted repository
                exec(open(file_path).read())
                
    except Exception as e:
        print(f"Error: {e}")
    
    return repo

Patched code sample

# SECURE: Safe GitPython usage with proper validation
import git
import os
import tempfile
import shutil
import subprocess
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class SecureGitManager:
    """Secure wrapper for GitPython operations."""
    
    def __init__(self):
        # SECURITY: Use system git executable path explicitly
        self.git_executable = self._find_system_git()
        
    def _find_system_git(self) -> str:
        """Find and validate system git executable."""
        system_paths = [
            '/usr/bin/git',
            '/usr/local/bin/git',
            'C:\\Program Files\\Git\\bin\\git.exe'
        ]
        
        for path in system_paths:
            if os.path.isfile(path):
                return path
        
        git_path = shutil.which('git')
        if git_path:
            return git_path
            
        raise RuntimeError("No valid git executable found")
    
    def _validate_repo_url(self, repo_url: str) -> bool:
        """Validate repository URL for security."""
        import re
        allowed_patterns = [
            r'^https://github\.com/[\w\-\.]+/[\w\-\.]+\.git$',
            r'^https://gitlab\.com/[\w\-\.]+/[\w\-\.]+\.git$'
        ]
        return any(re.match(pattern, repo_url) for pattern in allowed_patterns)
    
    def safe_clone_and_process(self, repo_url: str):
        """Safely clone and process repository."""
        
        # SECURITY: Validate repository URL
        if not self._validate_repo_url(repo_url):
            logger.error(f"Invalid repository URL: {repo_url}")
            return None
        
        # SECURITY: Use temporary directory
        with tempfile.TemporaryDirectory() as temp_dir:
            local_path = os.path.join(temp_dir, 'repo')
            
            try:
                # SECURITY: Configure git to use system executable
                git.Git().update_environment(GIT_PYTHON_GIT_EXECUTABLE=self.git_executable)
                
                # SECURITY: Clone with depth limit
                repo = git.Repo.clone_from(
                    repo_url, 
                    local_path,
                    depth=1,
                    single_branch=True
                )
                
                # SECURITY: Process files safely without execution
                self._process_repository_safely(repo, local_path)
                
                return repo
                
            except Exception as e:
                logger.error(f"Git operation failed: {e}")
                return None
    
    def _process_repository_safely(self, repo, local_path: str):
        """Process repository files safely without execution."""
        allowed_extensions = ['.md', '.txt', '.json']
        
        for item in repo.tree().traverse():
            if item.type == 'blob':
                file_path = os.path.join(local_path, item.path)
                
                # SECURITY: Only process safe file types, never execute
                if any(file_path.endswith(ext) for ext in allowed_extensions):
                    try:
                        with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
                            content = f.read(1024)  # Limit read size
                            logger.info(f"Processed file: {item.path}")
                    except Exception as e:
                        logger.warning(f"Could not process {item.path}: {e}")

Cite this entry

@misc{vaitp:cve202341040,
  title        = {{GitPython Unvalidated file access, potential denial of service}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2023},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2023-41040},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2023-41040/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.

The supreme art of war is to subdue the enemy without fighting.

Sun Tzu – “The Art of War”

:: Shaping the future through research and ingenuity ::