T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/scan_competitors.py:21
- Finding
- Unrestricted Request Destinations Enable Server-Side Request Forgery## Vulnerability Details **File Location**: `scripts/scan_competitors.py`, lines 21-40 and 259-265 **Vulnerability Type**: Server-Side Request Forgery through unrestricted user-controlled domains and redirects **Risk Level**: High **Vulnerable Code**: ```python class GEOScanner: """Scan website for GEO signals.""" def __init__(self, domain, timeout=10): self.domain = domain.replace('https://', '').replace('http://', '').rstrip('/') self.base_url = f"https://{self.domain}" self.timeout = timeout self.results = { 'domain': self.domain, 'technical': {}, 'content': {}, 'entity': {}, 'citation': {} } def fetch(self, path=''): """Fetch URL.""" url = urljoin(self.base_url, path) try: resp = requests.get(url, timeout=self.timeout, allow_redirects=True) return resp except Exception as e: return None ``` ```python domains = [args.brand] + [c.strip() for c in args.competitors.split(",")] results = [] for domain in domains: scanner = GEOScanner(domain) result = scanner.run_full_scan() results.append(result) ``` ### Technical Analysis Values supplied through `--brand` and `--competitors` are directly converted into outbound HTTPS request targets. The code does not validate that a supplied hostname resolves to a publicly routable address. It therefore permits loopback, private, link-local, reserved, and internal DNS destinations. The request also uses `allow_redirects=True`. Even if an initial hostname were validated as public, an attacker-controlled public endpoint could redirect the scanner to an internal address. No validation is performed for redirect destinations or after DNS resolution. The scanner sends requests to numerous fixed paths, including `/`, `/llms.txt`, `/robots.txt`, ` ...[truncated 1719 chars]
- Remediation
- ## Remediation Suggestions 1. Parse targets using `urllib.parse` rather than modifying strings with `replace()`. 2. Accept only well-formed hostnames with an explicitly permitted scheme, preferably HTTPS. 3. Resolve every hostname before connecting and reject all loopback, private, link-local, multicast, reserved, and unspecified IPv4 and IPv6 addresses using Python's `ipaddress` module. 4. Disable automatic redirects or validate the hostname and resolved addresses of every redirect destination before following it. 5. Guard against DNS rebinding by ensuring the address used for the connection remains within the validated set. 6. Apply an outbound firewall or proxy allowlist so the scanner can reach only public web destinations. 7. Reject embedded credentials, unexpected ports, malformed hostnames, and ambiguous URL syntax. 8. Add tests covering loopback addresses, RFC 1918 networks, IPv6 local addresses, internal DNS names, encoded IP representations, and public-to-private redirects. 9. Return a clear validation error instead of silently treating blocked destinations as failed scans.
