T09 · Insecure Skill Coding Practices
Warning
- Location
- wechat_article_to_markdown.py:92
- Finding
- Unrestricted Article-Controlled Image Fetching Enables SSRF<![CDATA[ ## Vulnerability Details **File Location**: `wechat_article_to_markdown.py:92-107` and `wechat_article_to_markdown.py:178-185` **Vulnerability Type**: Server-Side Request Forgery through unrestricted remote image URLs **Risk Level**: Medium ### Vulnerable Code ```python # wechat_article_to_markdown.py:92-107 async with semaphore: try: url = img_url if not img_url.startswith("//") else f"https:{img_url}" # Infer extension ext_match = re.search(r"wx_fmt=(\w+)", url) or re.search( r"\.(\w{3,4})(?:\?|$)", url ) ext = ext_match.group(1) if ext_match else "png" filename = f"img_{index:03d}.{ext}" filepath = img_dir / filename resp = await client.get( url, headers={"Referer": "https://mp.weixin.qq.com/"}, timeout=15.0, ) resp.raise_for_status() filepath.write_bytes(resp.content) ``` ```python # wechat_article_to_markdown.py:178-185 img_urls = [] seen = set() for img in content_el.find_all("img", src=True): src = img["src"] if src not in seen: seen.add(src) img_urls.append(src) ``` ### Technical Analysis The initial command-line article URL is restricted to a string beginning with `https://mp.weixin.qq.com/`, but image URLs extracted from the article DOM are not subject to equivalent validation. An article-controlled `src` or `data-src` value is passed directly to `httpx.AsyncClient.get()`. The implementation does not validate: - The URL scheme - The destination hostname - The resolved IP address - The destination port - Redirect destinations - Whether the destination is loopback, link-local, private, reserved, or multicast Consequently, content returned by a WeChat page can direct the host running the Skill to issue requests to destinations that the user could not otherwise access directly. Downloading remote article images is necessary for the declared functionality, but unrestricted a ...[truncated 1817 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Permit only `https` image URLs. 2. Maintain an explicit allowlist of WeChat image CDN hostnames required by the application. 3. Reject URLs containing embedded credentials, nonstandard ports, malformed hostnames, or unsupported schemes. 4. Resolve the hostname before connecting and reject all loopback, private, link-local, reserved, multicast, and unspecified IPv4 and IPv6 addresses. 5. Disable automatic redirects or validate the scheme, hostname, port, and resolved address of every redirect destination. 6. Account for DNS rebinding by ensuring that the validated address is the address used for the connection. 7. Consider using the browser’s already-authorized article resources rather than performing unrestricted secondary requests. 8. Add tests covering loopback addresses, private IPv4 and IPv6 ranges, cloud metadata addresses, protocol-relative URLs, redirects, encoded IP addresses, and malicious DNS resolution. ]]>
