T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/extract_colors.py:11
- Finding
- Unvalidated Website Fetch Enables Server-Side Request Forgery and Unbounded Response Consumption<![CDATA[ ## Vulnerability Details **File Location**: `scripts/extract_colors.py`, lines 11–21 and 57–64 **Vulnerability Type**: Server-Side Request Forgery and uncontrolled resource consumption **Risk Level**: High ### Vulnerable Code ```python def extract_colors(url): """Extract hex and rgb colors from website CSS/HTML.""" try: req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'}) with urllib.request.urlopen(req, timeout=15) as response: html = response.read().decode('utf-8', errors='ignore') except Exception as e: print(f"Error fetching {url}: {e}") sys.exit(1) ``` ```python def main(): if len(sys.argv) < 2: print("Usage: python extract_colors.py <website_url>") sys.exit(1) url = sys.argv[1] if not url.startswith('http'): url = 'https://' + url print(f"Extracting colors from {url}...\n") colors, rgb_colors = extract_colors(url) ``` ### Technical Analysis The script accepts an arbitrary URL and passes it directly to `urllib.request.urlopen`. It does not strictly validate the URL scheme, resolve and inspect the destination address, reject private or reserved networks, or validate the destination again after an HTTP redirect. Consequently, an attacker who can control the script argument can cause requests to loopback addresses, link-local services, private network hosts, or other endpoints reachable from the execution environment. Public endpoints that redirect to internal addresses can also bypass checks because redirect targets are not inspected. The call to `response.read()` reads the complete response into memory. The 15-second timeout limits waiting time but does not impose a maximum response size. A remote server can therefore return an excessively large or continuously streamed response and cause significant memory consumption. ### Attack Path 1. An attacker supplies a URL such as an internal HTTP service, loopback endpoint, o ...[truncated 1256 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Parse URLs with `urllib.parse.urlsplit` and allow only exact `http` and `https` schemes. 2. Reject URLs containing credentials or malformed hostnames. 3. Resolve every hostname before connecting and reject all loopback, private, link-local, multicast, unspecified, and reserved IPv4/IPv6 addresses using Python's `ipaddress` module. 4. Disable automatic redirects or validate every redirect target with the same policy. 5. Defend against DNS rebinding by ensuring the validated resolved address is the address actually used for the connection. 6. Use a strict allowlist when the expected set of target domains is known. 7. Read responses incrementally and abort after a defined limit, such as 5–10 MB. 8. Limit redirect count, enforce connection/read timeouts, and verify that the response content type is appropriate HTML or CSS. 9. Run website-fetching logic in a restricted network sandbox without access to internal services or cloud metadata endpoints. ]]>
