T09 · Insecure Skill Coding Practices
- Location
scripts/scuttle.py:76- Finding
SSRF Protection Can Be Bypassed Through DNS Rebinding
- Content
View full analysis
None: parsed = urllib.parse.urlparse(url) if parsed.scheme not in {"http", "https"}: raise ScuttleError(f"Scheme '{parsed.scheme}' is not allowed. Only http/https.") hostname = parsed.hostname if not hostname: raise ScuttleError("URL must include a hostname.") # 1. Block obviously local hostnames if hostname.lower() in {"localhost", "metadata.google.internal"} or hostname.endswith(".local") or hostname.endswith(".localhost"): if not allow_private: raise ScuttleError(f"Blocked host: '{hostname}' is a local or internal address.") # 2. Resolve DNS and check all returned IPs try: addr_info = socket.getaddrinfo(hostname, parsed.port or (80 if parsed.scheme == "http" else 443)) except socket.gaierror as e: raise ScuttleError(f"DNS resolution failed for '{hostname}': {e}") for entry in addr_info: ip = entry[4][0] if not _is_safe_ip(ip, allow_private): raise ScuttleError(f"Blocked host: '{hostname}' resolves to a restricted IP: {ip}") ``` ### Technical Analysis The application validates a hostname by resolving it with `socket.getaddrinfo()`, but it does not bind the subsequent HTTP connection to one of the validated IP addresses. The Requests/urllib3 networking stack performs another DNS resolution when `super().request()` opens the connection. This creates a time-of-check/time-of-use gap. An attacker controlling DNS can return a ...[truncated 1698 chars]- Remediation
View remediation
