T09 · Insecure Skill Coding Practices
Error
- Location
- bing-search.js:151
- Finding
- Server-Side Request Forgery Through Arbitrary URL Fetching<![CDATA[ ## Vulnerability Details **File Location**: `bing-search.js:151-159` and `bing-search.js:213-215` **Vulnerability Type**: Server-Side Request Forgery (SSRF) **Risk Level**: High ### Vulnerable Code ```js async function crawlWebpage(url) { if (isBlacklisted(url)) { throw new Error('该网站在黑名单中,无法抓取'); } return new Promise((resolve, reject) => { const client = url.startsWith('https') ? https : http; const req = client.get(url, { ``` The URL is obtained directly from command-line input: ```js } else if (command === 'fetch') { const url = args[1]; const result = await crawlWebpage(url); ``` ### Technical Analysis The `fetch` operation accepts an attacker-controlled URL and passes it directly to Node.js `http.get()` or `https.get()`. It does not parse and validate the destination hostname, resolved IP addresses, destination port, or URL protocol against a security policy. The existing `isBlacklisted()` check only searches the raw URL for a small set of public website domain substrings. It does not block sensitive destinations such as: - IPv4 and IPv6 loopback addresses - Private network address ranges - Link-local addresses - Cloud instance metadata services - Internal DNS names - Services listening on nonstandard ports The network request executes with the network access available to the Node.js process. Consequently, the function can reach services that may not be directly accessible to the party controlling the input. ### Attack Path 1. An attacker causes the Skill to invoke the `fetch` command with a URL targeting an internal resource, such as a loopback service, private-network host, or cloud metadata endpoint. 2. The URL does not contain one of the public domains in `BLACKLIST`, so `isBlacklisted()` returns `false`. 3. `crawlWebpage()` passes the URL directly to `http.get()` or `https.get()`. 4. The Node.js process connects to the internal destination using its own network privileges. 5. The response is converted ...[truncated 1000 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Parse input using `new URL(url)` and reject malformed URLs. 2. Permit only exact `http:` and `https:` protocols. 3. Resolve the hostname before connecting and reject every resolved address belonging to: - IPv4 or IPv6 loopback ranges - RFC 1918 private ranges - Link-local ranges - Multicast and unspecified ranges - Reserved or documentation ranges - Known cloud metadata destinations 4. Prevent DNS rebinding by connecting only to the validated resolved address while preserving the intended hostname for TLS verification and the HTTP `Host` header. 5. Prefer an explicit destination-domain allowlist when the business requirements permit it. 6. Restrict destination ports to expected web ports unless additional ports are explicitly required. 7. Node.js does not automatically follow redirects in this implementation. If redirect support is added later, validate every redirect destination using the same rules before following it. 8. Apply outbound firewall or proxy controls so the Skill process cannot access internal networks or metadata services. 9. Add security tests covering IPv4, IPv6, integer or encoded address forms, internal DNS names, DNS rebinding, loopback services, private ranges, link-local metadata addresses, and unusual ports. ]]>
