T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/funda_gateway.py:239
- Finding
- Upstream-Controlled Image URLs Enable Server-Side Request Forgery## Vulnerability Details **File Location**: `scripts/funda_gateway.py`, lines 178-181 and 239-246 **Vulnerability Type**: Server-Side Request Forgery (SSRF) and unbounded response download **Risk Level**: Medium ### Vulnerable Code ```python photo_urls = sorted(listing.get("photo_urls") or []) if not photo_urls: return {"id": id, "count": 0, "previews": []} photo_ids_to_urls = {extract_id(url): url for url in photo_urls} ``` ```python for index, url in enumerate(urls_to_download, start=1): photo_id = extract_id(url) try: request = urllib.request.Request( url, headers={"User-Agent": "Mozilla/5.0"} ) with urllib.request.urlopen(request, timeout=funda_timeout) as response: content = response.read() ``` ### Technical Analysis Image URLs obtained from the upstream listing data are passed directly to `urllib.request.urlopen`. The implementation does not validate: - The URL scheme. - The destination hostname. - The resolved IP address. - Redirect destinations. - Whether the destination is loopback, private, link-local, reserved, or multicast. - The response content length before reading it into memory. The local API caller cannot directly provide an arbitrary URL. Exploitation therefore requires control over, or compromise of, the upstream listing response or a dependency that supplies `photo_urls`. Nevertheless, the code crosses a documented trust boundary: `SKILL.md` states that external data must be treated as untrusted. The use of `response.read()` without a byte limit also permits an upstream server to return a very large response, potentially causing excessive memory consumption. The configured timeout limits waiting time but does not limit the number of downloaded bytes. ### Attack Path 1. An attacker compromises or influences the upstream listing-data source, or a dependency involved in constructing `photo_urls`. 2. The attacker supplies a URL targeting an address reachable from ...[truncated 1457 chars]
- Remediation
- ## Remediation Suggestions 1. Parse every image URL using `urllib.parse.urlsplit` and accept only HTTPS URLs. 2. Maintain an explicit allowlist of expected Funda image hostnames. Perform exact hostname or controlled subdomain matching rather than substring matching. 3. Resolve the hostname before connecting and reject every address that is loopback, private, link-local, reserved, multicast, or unspecified. 4. Disable automatic redirects or validate the scheme, hostname, and resolved IP address at every redirect hop. 5. Stream the response in bounded chunks and stop after a conservative maximum size rather than calling an unrestricted `response.read()`. 6. Check the declared content type and accept only supported image media types, while continuing to verify the actual file through Pillow. 7. Apply outbound network restrictions at the process or container level so the gateway can reach only required Funda services. 8. Log rejected destinations without recording sensitive query strings or response content.
