T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/find_channel_id.py:17
- Finding
- Arbitrary URL Fetch Enables Server-Side Request Forgery## Vulnerability Details **File Location**: `scripts/find_channel_id.py`, lines 17–24 and 28–30 **Vulnerability Type**: Server-Side Request Forgery (SSRF) caused by inadequate URL validation **Risk Level**: Medium ### Vulnerable Code ```python if channel_input.startswith('@'): url = f"https://www.youtube.com/{channel_input}" elif channel_input.startswith('UC'): return channel_input # Already a channel ID elif 'youtube.com/' in channel_input or 'youtu.be/' in channel_input: url = channel_input else: # Assume it's a channel name url = f"https://www.youtube.com/{channel_input}" try: # Fetch channel page req = Request(url, headers={'User-Agent': 'Mozilla/5.0'}) response = urlopen(req, timeout=10) html = response.read().decode('utf-8') ``` ### Technical Analysis The utility accepts an input as a YouTube URL whenever the raw string contains `youtube.com/` or `youtu.be/`. A substring match does not establish that the destination hostname belongs to YouTube. For example, `http://127.0.0.1:8080/youtube.com/` satisfies the substring check even though its destination is the local host. The complete attacker-controlled value is then passed to `urlopen()`. The implementation does not enforce HTTPS, validate the parsed hostname or port, reject local and private addresses, or constrain redirects. Python's URL opener follows HTTP redirects by default, so even an initially permitted destination could redirect the request elsewhere. This outbound-fetch capability exceeds the utility's minimum requirement, which only requires requests to known YouTube hosts. ### Attack Path 1. An attacker supplies a crafted argument to `find_channel_id.py`, directly or through an agent workflow that forwards user-provided channel input. 2. The argument contains a permitted substring but names an unintended destination, for example: ```text http://127.0.0.1:8080/youtube.com/ ``` 3. The condition at lines 21–22 accepts the entire string as a va ...[truncated 1156 chars]
- Remediation
- ## Remediation Suggestions 1. Prefer accepting only YouTube handles and syntactically valid channel IDs. Avoid accepting arbitrary URLs unless necessary. 2. Parse URLs with `urllib.parse.urlparse()` and require: - Scheme exactly equal to `https` - No embedded username or password - No unexpected port - Hostname exactly equal to an explicit allowlisted hostname, such as `www.youtube.com`, `youtube.com`, `m.youtube.com`, or `youtu.be` 3. Do not use suffix-only or substring-based hostname checks. A value such as `youtube.com.attacker.example` must not be accepted. 4. Resolve the hostname and reject loopback, private, link-local, multicast, reserved, and unspecified IP addresses using Python's `ipaddress` module. 5. Disable automatic redirects or validate every redirect target against the same scheme, hostname, port, and resolved-address policy. 6. Apply a response-size limit before reading the body to reduce memory-exhaustion risk. 7. Keep the existing timeout and consider separate connection and read limits where the HTTP client supports them. 8. Add regression tests for malicious inputs, including: ```text http://127.0.0.1/youtube.com/ http://169.254.169.254/youtube.com/ https://youtube.com.attacker.example/ https://attacker.example/youtube.com/ https://user@attacker.example/youtube.com/ ``` 9. For the main fetcher, continue restricting API calls to Google's documented HTTPS endpoint. Restrict the YouTube API key to YouTube Data API v3 and redact query-string credentials from diagnostics and intermediary logs.
