T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/baidu_img.js:91
- Finding
- <![CDATA[Unrestricted Remote URL and Redirect Handling Enables SSRF]]><![CDATA[ ## Vulnerability Details **File Location**: `scripts/baidu_img.js:91-111` **Related Data Flow**: `scripts/baidu_img.js:181-182`, `scripts/baidu_img.js:207-212`, `scripts/baidu_img.js:318` **Vulnerability Type**: Server-Side Request Forgery through unvalidated image URLs and redirect destinations **Risk Level**: Medium ### Vulnerable Code ```js /** Follow at most 5 redirects */ function httpRequest(rawUrl, headers) { return new Promise((resolve, reject) => { let redirected = 0; const visit = (u) => { const lib = u.startsWith('https') ? https : http; const req = lib.get( u, { headers, timeout: TIMEOUT_MS }, (res) => { const status = res.statusCode || 0; if (status === 301 || status === 302) { res.resume(); if (redirected++ > 5) return reject(new Error('Too many redirects')); const loc = res.headers.location; if (!loc) return reject(new Error('Redirect missing Location')); return visit(new URL(loc, u).toString()); } resolve(res); }, ); req.on('error', reject); req.on('timeout', () => req.destroy(new Error('timeout'))); }; visit(rawUrl); }); } ``` The untrusted URL is selected from remote search-result metadata and later passed to the request function: ```js for (const it of items) { const u = pickUrl(it, source); if (!u) continue; const host = (it.fromURLHost || '').trim(); ``` ```js async function downloadOne(url, outPath, referer) { const headers = { 'User-Agent': UA, Accept: 'image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8', }; if (referer) headers.Referer = referer; let lastErr = null; for (let i = 0; i <= RETRY; i++) { try { const buf = await httpGetBinary(url, headers); if (buf.length < 256) throw new Error(`Response data too small (${buf.length}B)`); fs.writeFileSync(outPath, buf); return buf.l ...[truncated 3293 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Parse each initial and redirected destination using `new URL()` and explicitly allow only `http:` and `https:`. 2. Resolve the hostname before connecting and reject all non-public destinations, including: - IPv4 loopback, private, link-local, multicast, unspecified, and reserved ranges. - IPv6 loopback, unique-local, link-local, multicast, unspecified, and IPv4-mapped private addresses. - Cloud metadata and platform-specific internal service addresses. 3. Apply the same checks to every redirect destination rather than only to the initial URL. 4. Protect against DNS rebinding by connecting to the validated resolved address while preserving the intended TLS server name and `Host` header, or use a hardened outbound-request library or proxy that enforces destination policy. 5. Prefer an explicit host allowlist for `thumb` and `middle` modes. Treat arbitrary original-image hosts as higher risk. 6. Consider disabling redirects for original-image downloads or requiring explicit user approval before crossing to a different origin. 7. Validate that successful responses have an expected image media type before saving them. 8. Use a strict redirect counter and reject unsupported redirect status codes or malformed `Location` values safely. ]]>
