T05 · Unauthorized Access and Privilege Escalation
- Location
- scripts/utils/validator.js:3
- Finding
- Arbitrary URL Fetching Enables SSRF and External Disclosure of Internal Responses<![CDATA[ ## Vulnerability Details **File Location**: `scripts/utils/validator.js:3-7`, `scripts/video2text/index.js:98-108`, `scripts/utils/download.js:483-493`, `scripts/video2text/index.js:140-154` **Vulnerability Type**: Server-Side Request Forgery (SSRF) with external data transfer **Risk Level**: High ### Vulnerable Code ```js // scripts/utils/validator.js:3-7 function isUrl(url) { try { const parsedUrl = new URL(url); return parsedUrl.protocol === "http:" || parsedUrl.protocol === "https:"; } catch (_) { return false; } } ``` ```js // scripts/video2text/index.js:98-108 if (validator.isUrl(file)) { const filepath = utils.downloadPath(); try { await fs.promises.mkdir(filepath, { recursive: true }); } catch (error) { utils.printError("Temporary download directory creation failed: " + (error.message || String(error))); process.exit(1); } try { const downloadResult = await helper.download(file, filepath); ``` ```js // scripts/utils/download.js:483-493 if (this.__isRequireRedirect(response)) { this.__redirectCount++; if (this.__redirectCount > this.__opts.maxRedirects) { const err = new Error("Too many redirects"); this.__setState(this.__states.FAILED); this.emit("error", err); return reject(err); } const redirectedURL = /^https?:\/\//.test(response.headers.location) ? response.headers.location : new URL(response.headers.location, this.url).href; this.__isRedirected = true; this.__initProtocol(redirectedURL); this.emit("redirected", redirectedURL, this.url); return this.__start(); } ``` ```js // scripts/video2text/index.js:140-154 const presignedUrl = await video.getPresignedUrl(tokenValue, file); if (!presignedUrl || !presignedUrl?.url || presignedUrl.url === "") { throw new Error("Failed to obtain presigned URL; report this to the developer"); } utils.printInfo("Uploading file to secure storage..."); await upload.uploadFileToOSS(file, presignedUrl.url, presignedUrl. ...[truncated 2610 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Prefer an explicit allowlist of supported public video-hosting domains. 2. If arbitrary public URLs are required, resolve the hostname before connecting and reject: - IPv4 loopback, private, link-local, multicast, carrier-grade NAT, reserved, and documentation ranges. - IPv6 loopback, unique-local, link-local, multicast, IPv4-mapped private addresses, and other reserved ranges. - Known cloud metadata addresses and hostnames. 3. Apply the same validation to every redirect destination. 4. Limit redirects and reject redirects that change to a disallowed protocol, host, address class, or port. 5. Protect against DNS rebinding by connecting to the validated resolved address while preserving the original hostname for TLS verification and the HTTP `Host` header. 6. Reject URLs containing embedded credentials and reject nonstandard ports unless explicitly required. 7. Validate the response MIME type and media signature before storing or uploading it. 8. Consider processing remote video URLs server-side in an isolated network with no access to private address ranges. ]]>
