VAITP Dataset

← Back to the dataset

CVE-2013-1629

Insecure package retrieval in pre-1.3 pip allows code execution via crafted response

  • CVSS 6.8
  • CWE-20 Improper Input Validation
  • Cryptographic
  • Remote

pip before 1.3 uses HTTP to retrieve packages from the PyPI repository, and does not perform integrity checks on package contents, which allows man-in-the-middle attackers to execute arbitrary code via a crafted response to a "pip install" operation.

CVSS base score
6.8
Published
2013-08-06
OWASP
A08 Software and Data Integrity Failures
Orthogonal defect classification
Function
Code defect classification
Incorrect Functionality
Category
Cryptographic
Subcategory
Unencrypted communication
Accessibility scope
Remote
Impact
Arbitrary Code Execution
Fixed by upgrading
Yes

Solution

Update pip to version 1.3 or higher.

Vulnerable code sample

import requests

def download_package(package_name, version):
    url = f"http://pypi.python.org/pypi/{package_name}/{version}/json"
    response = requests.get(url)
    response.raise_for_status()
    
    package_info = response.json()
    package_url = package_info['urls'][0]['url']
    
    package_response = requests.get(package_url)
    package_response.raise_for_status()
    
    with open(f"{package_name}-{version}.whl", "wb") as f:
        f.write(package_response.content)

download_package("example-package", "1.0.0")

Patched code sample

import os
import re
import hashlib
import tempfile
from typing import Optional
from urllib.parse import urlparse
import requests
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry

def download_package(
    package_name: str,
    version: str,
    output_dir: Optional[str] = None
) -> str:
    
    try:
        if not re.match(r'^[a-zA-Z0-9_-]+$', package_name):
            raise ValueError("Invalid package name")
            
        if not re.match(r'^[a-zA-Z0-9_.-]+$', version):
            raise ValueError("Invalid version")
            
        session = requests.Session()
        retry = Retry(
            total=3,
            backoff_factor=0.5,
            status_forcelist=[500, 502, 503, 504]
        )
        adapter = HTTPAdapter(max_retries=retry)
        session.mount('https://', adapter)
        
        info_url = f"https://pypi.org/pypi/{package_name}/{version}/json"
        response = session.get(info_url, timeout=10)
        response.raise_for_status()
        
        info = response.json()
        urls = info.get('urls', [])
        if not urls:
            raise ValueError("No package URLs found")
            
        package_info = None
        for url_info in urls:
            if url_info['packagetype'] in {'bdist_wheel', 'sdist'}:
                package_info = url_info
                break
                
        if not package_info:
            raise ValueError("No suitable package format found")
            
        package_url = package_info['url']
        expected_hash = package_info['digests']['sha256']
        
        parsed_url = urlparse(package_url)
        if parsed_url.scheme != 'https' or not parsed_url.netloc.endswith('.pypi.org'):
            raise ValueError("Invalid package URL")
            
        with tempfile.NamedTemporaryFile(delete=False) as temp_file:
            response = session.get(package_url, stream=True, timeout=30)
            response.raise_for_status()
            
            sha256 = hashlib.sha256()
            for chunk in response.iter_content(chunk_size=8192):
                sha256.update(chunk)
                temp_file.write(chunk)
                
        if sha256.hexdigest() != expected_hash:
            os.unlink(temp_file.name)
            raise ValueError("Package integrity check failed")
            
        output_dir = output_dir or os.getcwd()
        output_path = os.path.join(
            output_dir,
            os.path.basename(package_url)
        )
        
        os.replace(temp_file.name, output_path)
        return output_path
        
    except requests.exceptions.RequestException as e:
        raise ValueError(f"Download failed: {str(e)}")
    except Exception as e:
        raise ValueError(f"Error downloading package: {str(e)}")

Cite this entry

@misc{vaitp:cve20131629,
  title        = {{Insecure package retrieval in pre-1.3 pip allows code execution via crafted response}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2013},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2013-1629},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2013-1629/}}
}
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 ::