VAITP Dataset

← Back to the dataset

CVE-2019-9636

Improper Handling of Unicode Encoding in Python 2.7.x through 2.7.16 and 3.x through 3.7.2

  • CVSS 9.8
  • CWE-346: Origin Validation Error
  • Information Leakage
  • Remote

Python 2.7.x through 2.7.16 and 3.x through 3.7.2 is affected by: Improper Handling of Unicode Encoding (with an incorrect netloc) during NFKC normalization. The impact is: Information disclosure (credentials, cookies, etc. that are cached against a given hostname). The components are: urllib.parse.urlsplit, urllib.parse.urlparse. The attack vector is: A specially crafted URL could be incorrectly parsed to locate cookies or authentication data and send that information to a different host than when parsed correctly.

CVSS base score
9.8
Published
2019-03-08
OWASP
A05 Security Misconfiguration
Orthogonal defect classification
Checking
Code defect classification
Missing Check
Category
Information Leakage
Subcategory
Information Disclosure
Accessibility scope
Remote
Impact
Information Disclosure
Fixed by upgrading
Yes

Solution

Upgrade Python to version v2.7.17, v2.7.17rc1, v2.7.18, v2.7.18rc1; v3.5.10, v3.5.10rc1, v3.5.7, v3.5.8, v3.5.8rc1, v3.5.8rc2, v3.5.9; v3.6.10, v3.6.10rc1, v3.6.11, v3.6.11rc1, v3.6.12, v3.6.9, v3.6.9rc1; v3.7.3, v3.7.3rc1, v3.7.4, v3.7.4rc1, v3.7.4rc2, v3.7.5, v3.7.5rc1, v3.7.6, v3.7.6rc1, v3.7.7, v3.7.7rc1, v3.7.8, v3.7.8rc1, v3.7.9, or later.

Vulnerable code sample

def process_user_data(user_input):
    """Process user data - VULNERABLE to multiple issues"""
    # VULNERABILITY: No input validation or sanitization
    
    try:
        # Dangerous: No type checking
        if user_input:
            # Vulnerable: Unsafe string operations
            processed = str(user_input) * 100  # Can cause DoS
            
            # No length limits
            if len(processed) > 0:
                # Unsafe data processing
                result = processed.upper().lower().strip()
                
                # Information disclosure in response
                return {
                    "result": result,
                    "length": len(result),
                    "input_type": type(user_input).__name__,
                    "system_info": "Python 3.x"  # Information leakage
                }
        
        return {"error": "No input provided"}
    
    except Exception as e:
        # Vulnerable error handling - information leakage
        return {
            "error": f"Processing failed: {e}",
            "input": str(user_input),
            "traceback": str(e.__traceback__)
        }

def unsafe_file_operation(filename):
    """File operation - VULNERABLE"""
    # No path validation
    try:
        with open(filename, 'r') as f:
            content = f.read()  # No size limits
        return content
    except Exception as e:
        return f"Error: {e}"

# Vulnerable usage examples that could be exploited

Patched code sample

import os
import re
from pathlib import Path
from typing import Any, Dict, Optional

class SecureDataProcessor:
    """Secure data processor with comprehensive validation"""
    
    def __init__(self, max_input_size: int = 10000, max_output_size: int = 50000):
        self.max_input_size = max_input_size
        self.max_output_size = max_output_size
        self.allowed_file_dir = Path('./safe_files').resolve()
    
    def process_user_data_secure(self, user_input: Any) -> Optional[Dict[str, Any]]:
        """Securely process user data with comprehensive validation"""
        try:
            # Input validation
            if user_input is None:
                return {"error": "Input is required"}
            
            # Type validation
            if not isinstance(user_input, (str, int, float)):
                return {"error": "Invalid input type"}
            
            # Size validation
            input_str = str(user_input)
            if len(input_str) > self.max_input_size:
                return {"error": "Input too large"}
            
            # Content sanitization
            sanitized_input = self._sanitize_input(input_str)
            
            # Safe processing with limits
            processed = self._process_safely(sanitized_input)
            
            # Output validation
            if not self._validate_output(processed):
                return {"error": "Output validation failed"}
            
            # Return secure result (no sensitive information)
            return {
                "result": processed,
                "status": "success",
                "length": len(processed) if processed else 0
            }
            
        except Exception:
            # Secure error handling - no information leakage
            return {"error": "Processing failed", "status": "error"}
    
    def safe_file_operation(self, filename: str) -> Optional[str]:
        """Safe file operation with path validation"""
        try:
            # Input validation
            if not filename or not isinstance(filename, str):
                return None
            
            # Sanitize filename
            if not self._is_safe_filename(filename):
                return None
            
            # Resolve file path safely
            file_path = (self.allowed_file_dir / filename).resolve()
            
            # Ensure file is within allowed directory
            if not str(file_path).startswith(str(self.allowed_file_dir)):
                return None
            
            # Check if file exists and is readable
            if not file_path.exists() or not file_path.is_file():
                return None
            
            # Read file with size limit
            try:
                with open(file_path, 'r', encoding='utf-8') as f:
                    content = f.read(self.max_input_size)  # Size limit
                
                return content
                
            except (IOError, UnicodeDecodeError):
                return None
                
        except Exception:
            return None
    
    def _sanitize_input(self, input_str: str) -> str:
        """Sanitize input string"""
        # Remove control characters
        sanitized = re.sub(r'[\x00-\x1f\x7f-\x9f]', '', input_str)
        
        # Remove potentially dangerous patterns
        sanitized = re.sub(r'[<>&"\']', '', sanitized)
        
        # Normalize whitespace
        sanitized = re.sub(r'\s+', ' ', sanitized).strip()
        
        return sanitized[:1000]  # Length limit
    
    def _process_safely(self, data: str) -> str:
        """Process data safely with bounds checking"""
        if not data:
            return ""
        
        # Safe processing - no multiplication that could cause DoS
        processed = data.upper()
        
        # Apply length limit
        if len(processed) > 5000:
            processed = processed[:5000]
        
        return processed
    
    def _validate_output(self, output: Any) -> bool:
        """Validate output for security"""
        if output is None:
            return True
        
        if isinstance(output, str):
            # Check output size
            if len(output) > self.max_output_size:
                return False
            
            # Check for suspicious content
            if any(char in output for char in '<>&"\''):
                return False
        
        return True
    
    def _is_safe_filename(self, filename: str) -> bool:
        """Validate filename for safety"""
        if not filename or len(filename) > 255:
            return False
        
        # Check for path traversal
        if '..' in filename or filename.startswith('/'):
            return False
        
        # Only allow safe characters
        if not re.match(r'^[a-zA-Z0-9._-]+$', filename):
            return False
        
        return True

# Secure usage:
processor = SecureDataProcessor()
result = processor.process_user_data_secure("safe input data")
file_content = processor.safe_file_operation("safe_file.txt")

Cite this entry

@misc{vaitp:cve20199636,
  title        = {{Improper Handling of Unicode Encoding in Python 2.7.x through 2.7.16 and 3.x through 3.7.2}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2019},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2019-9636},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2019-9636/}}
}
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 ::