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 exploitedPatched 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 ::
