T09 · Insecure Skill Coding Practices
- Location
scripts/scraper.py:8- Finding
Unrestricted URL Fetching Enables Server-Side Request Forgery
- Content
View full analysis
Vulnerability Details
File Location:
scripts/main.py:31-42,scripts/scraper.py:8-18, andscripts/images.py:28-37, 47-58
Vulnerability Type: Server-Side Request Forgery (SSRF)
Risk Level: HighVulnerable Code
scripts/main.py:31-42:python article_url = sys.argv[1] output_dir = sys.argv[2] if len(sys.argv) > 2 else DEFAULT_OUTPUT_DIR # 创建输出目录 os.makedirs(output_dir, exist_ok=True) print(f"📥 抓取文章:{article_url}") # 1. 抓取文章 html = fetch_article(article_url)scripts/scraper.py:8-18:python def fetch_article(url: str) -> Optional[str]: """抓取文章 HTML 内容""" headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8', } try: response = requests.get(url, headers=headers, timeout=15)scripts/images.py:47-58:python def extract_images(html_content: str, base_url: str) -> List[Tuple[str, str]]: """从 HTML 中提取所有图片 URL""" from bs4 import BeautifulSoup soup = BeautifulSoup(html_content, 'lxml') images = [] # 查找所有 img 标签 for img in soup.find_all('img'): src = img.get('src') or img.get('data-src') if src: # 处理相对 URL full_url = urljoin(base_url, src) images.append((full_url, img))scripts/images.py:28-37:python def download_image(url: str, save_path: str, timeout: int = 10) -> bool: """下载单张图片""" headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'Referer': 'https://mp.weixin.qq.com/' } try: response = requests.get(url, headers=headers, timeout=timeout ...[truncated 2542 chars]- Remediation
View remediation
Remediation Suggestions
- Permit only the schemes required by the Skill, preferably HTTPS, and reject URLs containing credentials or unsupported ports.
- Enforce an explicit hostname allowlist for article pages, such as the intended WeChat article domain, and a narrowly defined list of approved image CDN domains.
- Resolve each hostname before connecting and reject every resolved IPv4 and IPv6 address in loopback, private, link-local, multicast, unspecified, reserved, and other non-public ranges.
- Disable automatic redirects or validate the scheme, hostname, port, and resolved addresses of every redirect target before following it.
- Apply the same validation to the initial article URL, all image URLs, and all redirect destinations.
- Protect against DNS rebinding by ensuring that validation and connection use consistent resolution results or by enforcing destination controls at an outbound proxy or firewall.
- Run the downloader in a sandbox with outbound network access restricted to the approved domains.
- Add tests covering direct private IP addresses, IPv6 loopback, alternative numeric IP representations, user-information URL syntax, DNS names resolving to private addresses, and redirects to prohibited destinations.
