SSRF reconnaissance via verbose network error leakage in /api/fetch
info-leak · server.js:72-72
15b34d5cc6 by 42nights on 2026-06-03Summary
The /api/fetch endpoint at server.js:72 attaches an http error handler that returns the raw Node.js error message to the client via `send(res, 502, `fetch error: ${e.message}`)`. Node's socket/DNS error messages embed details such as the resolved IP, port, and low-level error code (e.g. ECONNREFUSED, EHOSTUNREACH, ETIMEDOUT, ENOTFOUND). An attacker can submit arbitrary internal URLs and read these messages to map the internal network instead of receiving a generic, opaque error.
Impact. An unauthenticated attacker can use the endpoint as an oracle to scan and fingerprint the internal network. By varying the `url` parameter and observing the returned error string (open vs refused vs timeout vs DNS-fail vs TLS-fail), they can enumerate live hosts, open ports, hostnames, and IPs reachable from the server — classic SSRF reconnaissance that typically precedes attacks on internal admin panels, metadata services, or databases.
Vulnerable code — server.js
🔴 .on("error", (e) => send(res, 502, `fetch error: ${e.message}`));
Working exploit
curl -s 'http://127.0.0.1:4600/api/fetch?url=http://127.0.0.1:1/'
Exploit transcript
HTTP 502 Content-Type: text/plain fetch error: connect ECONNREFUSED 127.0.0.1:1
Confirmed: HTTP 502 body: 'fetch error: connect ECONNREFUSED 127.0.0.1:1'
CVSS v3.1
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:N→ 5.8 (medium)Recommended fix
AI-suggested fix — review before applying (derived from analysis of untrusted repo content):
Return a generic error to the client and log the detail server-side only.
--- a/server.js
+++ b/server.js
@@
- .on("error", (e) => send(res, 502, `fetch error: ${e.message}`));
+ .on("error", (e) => {
+ console.error("upstream fetch error", { url, code: e.code, message: e.message });
+ send(res, 502, "upstream fetch failed");
+ });
Additionally, enforce an allow-list of hosts/schemes and block RFC1918, loopback, link-local, and metadata IPs before dispatching the request.Ship the fix
Rook found it and proved it. Hand the finding — with its working exploit as a failing test — to Otis to write the fix PR.