T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/media_gen_client.py:119
- Finding
- Unrestricted User-Controlled URL Fetch Enables Blind SSRF<![CDATA[ ## Vulnerability Details **File Location**: `scripts/media_gen_client.py`, lines 119–121 **Vulnerability Type**: Server-Side Request Forgery (SSRF) **Risk Level**: High ### Vulnerable Code ```python if args.image.startswith("http"): image_content = urllib.request.urlopen(args.image, context=_get_ssl_context()).read() image_url = args.image if image_content else None ``` ### Technical Analysis The `--image` argument is supplied by the user and is fetched directly with `urllib.request.urlopen`. The only validation is a case-sensitive string-prefix check for `http`; there is no URL parsing, destination allowlist, resolved-IP validation, redirect validation, response-size limit, or explicit timeout. Fetching a user-provided image URL is related to the Skill's declared functionality, but downloading the resource locally is not necessary merely to pass its URL to the video service. This preliminary request therefore grants more network access than the minimum required. An attacker can cause the process to issue GET requests to destinations reachable from the execution environment, including loopback interfaces, private networks, link-local services, and cloud metadata endpoints. Redirects can also undermine checks performed only on the original URL. Although the response body is not printed, request timing, errors, and success behavior provide a blind SSRF oracle. GET endpoints with side effects may also be triggered. Calling `.read()` without a size limit additionally permits memory exhaustion from a very large or endless response. ### Attack Path 1. An attacker supplies an image argument such as `http://127.0.0.1:8080/admin/action`, a private-network host, or a link-local metadata address. 2. The value passes `startswith("http")`. 3. `urllib.request.urlopen` sends a GET request from the Skill execution environment. 4. The attacker observes differences in completion time or error behavior to infer service availability. 5. If an internal GET ...[truncated 783 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Remove the local prefetch entirely when the remote service can accept the original image URL. - If validation is required, parse the URL with `urllib.parse.urlsplit` and allow only `https`. - Resolve the hostname and reject loopback, private, link-local, multicast, reserved, and unspecified IP ranges for both IPv4 and IPv6. - Disable redirects or validate every redirect destination after DNS resolution. - Apply an explicit short connection/read timeout. - Read only a small bounded amount of data needed for format validation rather than calling unbounded `.read()`. - Enforce an image content-type allowlist and validate image magic bytes. - Prefer a trusted image-host allowlist where operationally possible. ]]>
