T09 · Insecure Skill Coding Practices
- Location
main.py:34- Finding
SSRF Through Weak Host Validation and Unrestricted Redirect Following
- Content
View full analysis
Vulnerability Details
File Location:
main.py:34-37andmain.py:56-61
Vulnerability Type: Server-Side Request Forgery (SSRF)
Risk Level: HighVulnerable Code
python def is_wechat_url(self, url: str) -> bool: """检查是否为微信公众号链接""" parsed = urlparse(url) return 'mp.weixin.qq.com' in parsed.netlocpython response = self.session.get( url, headers=headers, timeout=self.timeout, allow_redirects=True )Technical Analysis
The hostname check performs a substring search against
parsed.netlocrather than an exact comparison against the normalized hostname. Consequently, a URL whose authority contains the trusted string can pass validation even when its actual destination is not WeChat. Examples accepted byis_wechat_url()include authorities such as:text mp.weixin.qq.com.attacker.example mp.weixin.qq.com@127.0.0.1The higher-level
extract_url()function imposes a more restrictive pattern on URLs extracted from messages, butfetch_article()is a public method and can be called directly by other integration code. More importantly, requests to initially valid WeChat URLs are made withallow_redirects=True. The final redirect destination is not revalidated, so a permitted URL that returns or is manipulated into returning a redirect can cause the server to contact an arbitrary destination.No controls reject loopback, private, link-local, reserved, or cloud metadata addresses. DNS resolution is also not checked for private-address results.
Attack Path
- An attacker supplies a URL directly to
fetch_article()whose authority containsmp.weixin.qq.comas a substring, or supplies an accepted URL that redirects to an attacker-selected destination. is_wechat_url()approves the initial URL because the trusted hostname appears somewhere innetloc.requests.Session.get()sends the request and automatically follows r ...[truncated 1092 chars]
- An attacker supplies a URL directly to
- Remediation
View remediation
Remediation Suggestions
-
Permit only HTTPS URLs with an exact normalized hostname comparison:
python parsed = urlparse(url) if ( parsed.scheme != "https" or parsed.hostname != "mp.weixin.qq.com" or parsed.username is not None or parsed.password is not None or parsed.port not in (None, 443) ): reject_url() -
Disable automatic redirects with
allow_redirects=False. -
Follow redirects manually only after applying the same scheme, hostname, credential, port, and IP-address validation to every destination.
-
Resolve the hostname before connecting and reject loopback, private, link-local, multicast, unspecified, and reserved IP addresses for both IPv4 and IPv6.
-
Protect against DNS rebinding by ensuring that the validated address is the address used for the connection.
-
Keep the restrictive checks inside
fetch_article()rather than relying only on the message URL extractor. -
Add tests for malicious subdomains, user-information authorities, encoded hostnames, unusual ports, IPv6 addresses, redirects to private addresses, and redirect chains.
-
