T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/douyin_parse.py:35
- Finding
- Unrestricted User-Supplied URL Requests Enable Blind SSRF<![CDATA[ ## Vulnerability Details **File Locations**: - `scripts/douyin_parse.py:35-43` - `scripts/douyin_download.py:37-45` - `scripts/douyin_extract_text.py:42-50` **Vulnerability Type**: Server-Side Request Forgery through unrestricted URLs and redirects **Risk Level**: High ### Vulnerable Code The same vulnerable URL-processing pattern appears in all three scripts: ```python urls = re.findall( r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', share_text, ) if not urls: raise ValueError("No valid share link found in input") share_url = urls[0] share_response = requests.get(share_url, headers=HEADERS, timeout=15) video_id = share_response.url.split("?")[0].strip("/").split("/")[-1] ``` ### Technical Analysis Although the Skill declares that its input should be a Douyin share URL, the regular expression accepts any HTTP or HTTPS URL. The code does not verify: - That the hostname belongs to Douyin or ByteDance. - That HTTPS is used. - That the destination port is expected. - That the resolved IP address is public. - That the destination is not loopback, private, link-local, or reserved. - That each redirect remains within an approved domain. `requests.get()` follows HTTP redirects by default. Consequently, even an initially acceptable-looking URL could redirect the request to an internal service. The timeout limits request duration but does not prevent SSRF. The response body from the first request is not directly returned to the caller, which limits direct data extraction. Nevertheless, response timing, error behavior, and subsequent script behavior may provide a blind network oracle. More importantly, the HTTP GET itself can reach endpoints that are unavailable to the attacker directly. ### Attack Path 1. An attacker supplies a direct internal URL, such as `http://127.0.0.1:PORT/path`, as the first URL in the input. 2. Alternatively, the attacker supplies an externally controlled URL that redirects to a p ...[truncated 1330 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Parse input with `urllib.parse.urlsplit()` rather than accepting the first generic URL matched by a regular expression. 2. Require HTTPS and allow only explicitly approved Douyin share hosts, such as `v.douyin.com`. 3. Reject embedded credentials, unexpected ports, malformed hostnames, and hostname suffix tricks. 4. Resolve the hostname before connecting and reject every loopback, private, link-local, multicast, unspecified, or reserved IPv4/IPv6 address. 5. Disable automatic redirects with `allow_redirects=False`. 6. Process redirects manually with a small maximum redirect count, validating the scheme, hostname, port, and resolved IP address at every hop. 7. Validate the final URL structure and video identifier before making the second request. 8. Apply the fix consistently in all three affected scripts, preferably by moving URL validation and metadata parsing into one shared, reviewed module. 9. Where supported, enforce an outbound network policy that limits the Skill to documented Douyin and Alibaba Cloud endpoints. ]]>
