T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/wechat_draft.js:167
- Finding
- Unrestricted Remote Image Retrieval Enables Server-Side Request Forgery<![CDATA[ ## Vulnerability Details **File Location**: `scripts/wechat_draft.js`, lines 167–178 and 342–365 **Vulnerability Type**: Server-Side Request Forgery through attacker-controlled image URLs **Risk Level**: High ### Vulnerable Code ```js async function downloadBinary(url, redirects = 3) { const { statusCode, headers, body } = await requestRaw(url, { method: 'GET' }); if ([301, 302, 303, 307, 308].includes(statusCode) && headers.location && redirects > 0) { const nextUrl = new URL(headers.location, url).toString(); return downloadBinary(nextUrl, redirects - 1); } if (statusCode < 200 || statusCode >= 300) { throw new Error(`下载图片失败: HTTP ${statusCode}`); } return { data: body, contentType: headers['content-type'] || 'application/octet-stream' }; } ``` ```js async function rewriteImagesForWechat(token, html, htmlFilePath) { const imgRegex = /<img\b([^>]*?)\bsrc="([^"]+)"([^>]*)>/g; const matches = [...html.matchAll(imgRegex)]; if (!matches.length) return html; const srcMap = new Map(); for (const match of matches) { const src = match[2]; if (srcMap.has(src) || src.startsWith('data:')) continue; let fileName = 'image.png'; let fileData; let contentType = 'application/octet-stream'; if (/^https?:\/\//i.test(src)) { const downloaded = await downloadBinary(src); fileData = downloaded.data; const urlObj = new URL(src); fileName = path.basename(urlObj.pathname) || fileName; contentType = inferMimeType(fileName, downloaded.contentType); } else { ``` ### Technical Analysis The script treats every HTTP or HTTPS image source found in the supplied HTML as a trusted remote resource. It sends a request to the URL without validating its hostname, resolved IP address, port, or destination network. Consequently, crafted HTML can cause the machine running the Skill ...[truncated 2376 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Allow remote retrieval only when explicitly enabled. 2. Require HTTPS and maintain an explicit allowlist of trusted image hosts. 3. Resolve the hostname before connecting and reject all loopback, private, link-local, multicast, reserved, and unspecified IPv4 and IPv6 addresses. 4. Repeat hostname and resolved-address validation after every redirect. 5. Protect against DNS rebinding by connecting only to the validated resolved address while preserving the expected TLS hostname. 6. Reject URLs containing credentials, unexpected ports, or unsupported schemes. 7. Enforce strict response limits: - Maximum download size - Connection, response, and total timeouts - Maximum redirect count 8. Validate the actual file signature and permit only supported image formats rather than trusting the URL extension or `Content-Type` header. 9. Consider downloading remote images in a sandboxed service with no access to internal networks. ]]>
