T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/ddg_fetch.py:78
- Finding
- Unrestricted URL Fetching Enables Server-Side Request Forgery<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ddg_fetch.py`, lines 78-103 **Vulnerability Type**: Server-Side Request Forgery (SSRF) **Risk Level**: High ### Vulnerable Code ```python # Validate URL parsed = urllib.parse.urlparse(url) if not parsed.scheme: # Add https if missing url = 'https://' + url parsed = urllib.parse.urlparse(url) if not parsed.netloc: result["error"] = "Invalid URL" return result # Build request req = urllib.request.Request( url, headers={ 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.0 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.0', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language': 'en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7', 'Accept-Encoding': 'gzip, deflate', 'DNT': '1', 'Connection': 'keep-alive', } ) # Make request with urllib.request.urlopen(req, timeout=timeout) as response: ``` ### Technical Analysis The URL validation only verifies that the parsed URL contains a network location. It does not: - Restrict the scheme to `http` or `https`. - Reject loopback addresses such as `127.0.0.1` or `::1`. - Reject private, link-local, reserved, or unspecified IP address ranges. - Block cloud metadata endpoints such as `169.254.169.254`. - Resolve hostnames and validate their resulting IP addresses. - Validate redirect destinations before following them. Python's `urllib.request.urlopen` follows HTTP redirects by default. Consequently, validating only the initial hostname would remain insufficient because a public endpoint could redirect the request to an internal address. ### Attack Path 1. An attacker supplies a URL through the Skill's documented fetch operation. 2. The URL identifies an internal service, loopback interface, private network host, or cloud metadata endpoint. 3. The parser accepts the URL because it has a scheme and network location. 4. `urllib. ...[truncated 1018 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Permit only explicit `http` and `https` schemes. 2. Reject URLs containing embedded credentials or malformed hostnames. 3. Resolve the hostname before connecting and inspect every returned IPv4 and IPv6 address. 4. Reject loopback, private, link-local, multicast, reserved, and unspecified addresses using Python's `ipaddress` module. 5. Explicitly deny cloud metadata addresses and environment-specific internal domains. 6. Disable automatic redirects or implement a redirect handler that repeats the full scheme, hostname, and resolved-address validation for every destination. 7. Protect against DNS rebinding by connecting to a previously validated address while preserving the intended HTTP host and TLS hostname verification. 8. Consider using an outbound proxy or network policy that prevents the Skill process from reaching internal networks. 9. Apply an allowlist of trusted domains when arbitrary Internet fetching is not required. ]]>
