T09 · Insecure Skill Coding Practices
Error
- Location
- skill.py:215
- Finding
- Unrestricted Remote Media Fetching Enables SSRF and External Data Relay<![CDATA[ ## Vulnerability Details **File Location**: `skill.py:215-262` **Vulnerability Type**: Server-Side Request Forgery (SSRF), unrestricted network access, and unbounded data transfer **Risk Level**: High ### Vulnerable Code ```python def upload_url_media(media_url, token): """Download a remote media file and re-upload it to OSS.""" print(f"Downloading remote media: {media_url} ...") req = urllib.request.Request(media_url) with urllib.request.urlopen(req, timeout=60) as resp: media_data = resp.read() ct = resp.headers.get("Content-Type", "") ext = ".jpg" for suffix, mime in CONTENT_TYPES.items(): if mime in ct: ext = suffix break url_path = urllib.parse.urlparse(media_url).path if "." in url_path.split("/")[-1]: ext = "." + url_path.split("/")[-1].rsplit(".", 1)[-1].lower() filename = f"upload{ext}" encoded_filename = urllib.parse.quote(filename) print(f"Re-uploading to OSS as {filename} ...") resp = api_request("GET", f"{API_BASE}/image/getUploadUrl?filename={encoded_filename}", token) if not resp.get("success"): print(f"Error: Failed to get upload URL: {json.dumps(resp, ensure_ascii=False)}", file=sys.stderr) sys.exit(1) upload_url = resp["data"]["uploadUrl"] # Build public URL from upload_url (strip query params) parsed = urllib.parse.urlparse(upload_url) public_url = f"{parsed.scheme}://{parsed.netloc}{parsed.path}" put_req = urllib.request.Request(upload_url, data=media_data, method="PUT") put_req.add_header("Content-Type", "application/octet-stream") with urllib.request.urlopen(put_req, timeout=120) as _: pass width, height = get_image_size(media_data) print(f"Upload complete: {public_url}") return public_url, width, height ``` ### Technical Analysis The `media_url` value originates from the command-line options `--image_url`, `--end_image_url`, and `--video_url`. It ...[truncated 2525 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Permit only `https` URLs. Reject `file`, `ftp`, `data`, and all other schemes. 2. Resolve the hostname before connecting and reject every address in loopback, private, link-local, multicast, unspecified, documentation, and reserved ranges for both IPv4 and IPv6. 3. Disable automatic redirects or implement a redirect handler that validates every destination before following it. 4. Protect against DNS rebinding by ensuring the validated address is the address used for the connection. 5. Introduce an explicit allowlist of trusted media hosts where feasible. 6. Enforce a strict maximum download size using `Content-Length` where available and a bounded streaming loop regardless of that header. 7. Validate the response MIME type and verify the file signature before uploading it. 8. Reject non-image content for image parameters and non-video content for video parameters. 9. Stream validated content to bounded temporary storage rather than loading the entire response into memory. 10. Require clear user confirmation before fetching a URL outside an approved domain set. 11. Apply outbound network controls at the sandbox or container level to block access to internal and metadata networks. ]]>
