T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/verify_refs.py:164
- Finding
- Arbitrary Reference URLs Enable Server-Side Request Forgery<![CDATA[ ## Vulnerability Details **File Location**: `scripts/verify_refs.py:164-172`, with attacker-controlled input reaching the request at `scripts/verify_refs.py:219-239` **Vulnerability Type**: Server-Side Request Forgery through unrestricted outbound HTTP requests **Risk Level**: Medium ### Vulnerable Code ```python def check_url(url: str, timeout: float) -> tuple: """返回 (reachable, status, note)。reachable 以 2xx/3xx/429(反爬) 计。""" req = urllib.request.Request(url, method="HEAD", headers={ "User-Agent": "Mozilla/5.0 (compatible; cite-holmes/1.0; +verified-deep-research)", "Accept": "*/*", }) opener = urllib.request.build_opener(urllib.request.HTTPRedirectHandler()) for method in ("HEAD", "GET"): try: req.method = method with opener.open(req, timeout=timeout) as resp: return True, resp.status, f"{method} {resp.status}" ``` The URL originates from a user-controlled reference record: ```python url = ref_to_url(ref) if not url: out.update(verdict="invalid", note="缺少 url 且无可解析的 doi/pmid" + ("(pmid 须为 6-9 位数字)" if pmid else "")) return out if not re.match(r"^https?://", url): out.update(verdict="invalid", note=f"url 非 http(s) 格式: {url[:60]}") return out if not out["tier"]: out["tier"] = classify_tier(url, medical) out["url"] = url if medical and out["tier"] in ("community", "social", "blog"): out["note"] = "医学模式:社区/社交/博客层来源不得支撑医学结论(仅作线索)" if offline: out["note"] = (out["note"] + ";" if out["note"] else "") + "offline 模式未做可达性检查" else: reachable, status, note = check_url(url, timeout) ``` ### Technical Analysis The verifier accepts arbitrary HTTP or HTTPS URLs from reference JSON and sends outbound `HEAD` requests, followed by `GET` requests when necessary. It does not resolve and reject loopback, private, link-local, reserved, multicast, or unspecified IP addresses. It also enables automatic redirects witho ...[truncated 1877 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Resolve the destination hostname before connecting and reject every address in loopback, private, link-local, multicast, reserved, and unspecified ranges for both IPv4 and IPv6. 2. Permit only HTTPS by default; if HTTP is required, make it an explicit opt-in. 3. Restrict destination ports to 80 and 443. 4. Disable automatic redirects or implement a custom redirect handler that revalidates the scheme, hostname, resolved addresses, and port at every hop. 5. Limit the number of redirects and reject URL credentials and unusual hostname representations. 6. Apply DNS rebinding protections by validating the address immediately before connection and, where possible, binding the request to the validated address. 7. Consider a public-domain allowlist or an isolated outbound proxy for citation checks. 8. Add regression tests covering loopback, RFC 1918 ranges, link-local addresses, IPv6 local ranges, encoded IP addresses, and redirects from public hosts to private destinations. ]]>
