T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/pdf2md.py:7
- Finding
- Unrestricted Remote PDF Retrieval Enables Server-Side Request Forgery and Resource Exhaustion<![CDATA[ ## Vulnerability Details **File Location**: `scripts/pdf2md.py`, lines 7-12 **Vulnerability Type**: Server-side request forgery, unsafe network access, and unbounded response buffering **Risk Level**: High ### Vulnerable Code ```python def download(u): if u.startswith('http'): r = requests.get(u, timeout=60, headers={'User-Agent':'Mozilla/5.0'}) return r.content with open(u, 'rb') as f: return f.read() ``` ### Technical Analysis The source URL is accepted directly from the command line and passed to `requests.get()` without validating its destination. The implementation does not: - Restrict requests to approved hosts. - Require HTTPS. - Resolve and reject loopback, private, link-local, multicast, or reserved IP addresses. - Validate redirect destinations. - Limit the maximum response size. - Verify the HTTP status using `raise_for_status()`. - Verify the response content type or PDF file signature. The check `u.startswith('http')` is not a meaningful destination security control. Moreover, `requests` follows redirects by default, so even an initially trusted public URL could redirect the request to an internal address. The complete response is accessed through `r.content`, causing it to be buffered in memory. It is subsequently written to a temporary file for PDF processing. A malicious or compromised endpoint can therefore return an extremely large response and consume substantial memory and disk space. ### Attack Path 1. An attacker supplies a URL to `scripts/pdf2md.py`, either directly or through a system that exposes this conversion function. 2. The URL points to a loopback, private-network, link-local, or attacker-controlled service. Alternatively, it points to a public endpoint that redirects to such a destination. 3. The process sends the request using its own network privileges. 4. If the target returns a PDF, its text can be extracted and included in the generated Markdown output. 5. If the target retu ...[truncated 969 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Permit only `https://` URLs unless insecure HTTP access is explicitly required. 2. Maintain an explicit allowlist of trusted hostnames. 3. Resolve the hostname before connecting and reject loopback, private, link-local, multicast, unspecified, and reserved addresses for both IPv4 and IPv6. 4. Disable automatic redirects or validate every redirect target using the same hostname and IP-address controls. 5. Use streaming retrieval with a strict maximum download size: ```python with requests.get( url, stream=True, timeout=(5, 60), allow_redirects=False, headers={"User-Agent": "pdf-skills/1.0"}, ) as response: response.raise_for_status() ``` 6. Validate `Content-Length` when present and independently count streamed bytes so chunked responses cannot bypass the limit. 7. Accept only expected content types and verify that the downloaded data begins with a valid PDF signature. 8. Apply outbound firewall or sandbox controls so the process cannot reach internal or metadata networks. 9. Handle download and parsing failures without leaving partial output files. ]]>
