T09 · Insecure Skill Coding Practices
Error
- Location
- save_article_to_obsidian.py:302
- Finding
- Unrestricted URL Fetching Enables Server-Side Request Forgery<![CDATA[ ## Vulnerability Details **File Location**: `save_article_to_obsidian.py:113-136` and `save_article_to_obsidian.py:302-349` **Vulnerability Type**: Server-Side Request Forgery (SSRF) **Risk Level**: High ### Vulnerable Code ```python def download_image(img_url: str, article_hash: str, img_index: int) -> str: """下载图片到本地""" try: url_hash = get_url_hash(img_url) ext = get_file_extension(img_url) filename = f"{article_hash}_{img_index:03d}_{url_hash}{ext}" subfolder = os.path.join(ATTACHMENTS_DIR, article_hash) os.makedirs(subfolder, exist_ok=True) filepath = os.path.join(subfolder, filename) if os.path.exists(filepath) and os.path.getsize(filepath) > 100: return f"../attachments/{article_hash}/{filename}" cmd = [ "curl", "-s", "-L", "--max-time", "30", "-H", "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)", "-H", "Referer: https://mp.weixin.qq.com/", "-o", filepath, img_url ] result = subprocess.run(cmd, capture_output=True, text=True) ``` ```python def fetch_with_retry(url: str, max_retries: int = 3) -> str: """带重试的抓取,针对不同站点使用不同策略""" last_error = None site_type = detect_site_type(url) wechat_ua = "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.43" for attempt in range(max_retries): try: if attempt > 0: print(f" 🔄 第 {attempt + 1} 次尝试...") time.sleep(1) if site_type == 'wechat': cmd = [ "curl", "-s", "-L", "--max-time", "30", "-H", f"User-Agent: {wechat_ua}", "-H", "Accept: text/html,application/xhtml+xml", "-H", "Accept-Language: zh-CN", "-H", f"Referer: https://mp.weixin.qq.com/", ...[truncated 3478 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Accept only explicitly supported schemes, normally `http` and `https`. 2. Reject URLs containing user information or malformed authority components. 3. Resolve the destination hostname before connecting and reject every resolved address in loopback, private, link-local, multicast, unspecified, reserved, and other non-public ranges. 4. Explicitly block known metadata destinations, including link-local metadata addresses. 5. Disable automatic redirects or validate the scheme, hostname, and resolved IP address at every redirect hop. 6. Apply the same validation to article URLs and all image URLs extracted from remote content. 7. Consider using a strict hostname allowlist when the supported publishing platforms are known. 8. Enforce response-size and content-type limits before storing downloaded data. 9. Run the skill with restricted network access so it cannot reach private networks or metadata services. 10. Add tests covering direct private addresses, DNS names resolving to private addresses, mixed public/private DNS answers, IPv6 loopback and private ranges, and public-to-private redirects. ]]>
