T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/clawdraw.mjs:1964
- Finding
- DNS Rebinding Bypasses Image Fetch SSRF Protection<![CDATA[ ## Vulnerability Details **File Location**: `scripts/clawdraw.mjs:1964-2052` **Vulnerability Type**: Time-of-check/time-of-use SSRF protection bypass through DNS rebinding **Risk Level**: High ### Vulnerable Code ```js async function validateImageUrl(urlStr) { const parsed = new URL(urlStr); // Block non-HTTP(S) (already checked by caller, but defense-in-depth) if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') { throw new Error('Only HTTP and HTTPS URLs are supported.'); } // Block obvious private hostnames const host = parsed.hostname.toLowerCase(); if (host === 'localhost' || host.endsWith('.local') || host.endsWith('.internal')) { throw new Error('Private/internal URLs are not allowed.'); } // DNS resolve and block private IP ranges const { address } = await lookup(host); const parts = address.split('.').map(Number); const isPrivate = parts[0] === 127 || parts[0] === 10 || (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31) || (parts[0] === 192 && parts[1] === 168) || (parts[0] === 169 && parts[1] === 254) || parts[0] === 0 || address === '::1' || address.startsWith('fe80:') || address.startsWith('fc00:') || address.startsWith('fd'); if (isPrivate) { throw new Error('Private/internal URLs are not allowed.'); } } ``` The validated address is not bound to the subsequent request: ```js const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), 30_000); let res; try { res = await fetch(url, { redirect: 'manual', signal: controller.signal, }); } finally { clearTimeout(timeout); } ``` ### Technical Analysis The code resolves the supplied hostname with `lookup()` and verifies that the returned address is not within selected private, loopback, or link-local ranges. The later `fetch()` call independently resolves the hostname again. This creates a time-of-check/time-of-use discrepancy. An attacker wh ...[truncated 2439 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Resolve and validate every A and AAAA record associated with the hostname rather than checking only one result. 2. Normalize addresses with a well-tested IP-address library before classification, including: - IPv4-mapped IPv6 addresses - IPv6 loopback and unspecified addresses - IPv6 link-local and unique-local ranges - IPv4 private, loopback, link-local, carrier-grade NAT, multicast, reserved, and documentation ranges 3. Bind the outgoing connection to an address that was already validated. Preserve the original hostname for the HTTP `Host` header and TLS SNI/certificate validation. 4. Do not allow the HTTP client to perform an uncontrolled second DNS lookup after validation. 5. Repeat the resolve, validate, and address-binding process separately for every redirect target. 6. Consider permitting image downloads only through a controlled proxy or an explicit allowlist when operationally feasible. 7. Add automated tests covering: - DNS rebinding between validation and connection - Multiple A and AAAA records - IPv4-mapped IPv6 addresses - Redirect-based rebinding - Cloud metadata, loopback, and private address ranges ]]>
