SSRF: POST /recorders/probe accepts arbitrary URLs and probes raw TCP/UDP on internal network #104

Closed
opened 2026-05-26 18:18:38 -04:00 by zgaetano · 2 comments
Owner

Fixed in 04ce096. POST /recorders/probe now:

  • Restricts URL schemes to srt, rtmp, rtmps, rtsp, udp, rtp.
  • Rejects private / loopback / link-local / RFC1918 / AWS-metadata hosts for non-admin users.
  • Blocks common service ports (22, 25, 53, 80, 443, 5432, 6379, 9000, 9100, 9229) for non-admins.
  • Validates the URL before forwarding to the capture service so even the proxy path can't be abused.
Fixed in 04ce096. `POST /recorders/probe` now: - Restricts URL schemes to `srt`, `rtmp`, `rtmps`, `rtsp`, `udp`, `rtp`. - Rejects private / loopback / link-local / RFC1918 / AWS-metadata hosts for non-admin users. - Blocks common service ports (22, 25, 53, 80, 443, 5432, 6379, 9000, 9100, 9229) for non-admins. - Validates the URL before forwarding to the capture service so even the proxy path can't be abused.
Author
Owner

🔧 Fix plan incoming — token verified, posting all plans now...

🔧 Fix plan incoming — token verified, posting all plans now...
Author
Owner

Fix Plan — #104 SSRF via POST /recorders/probe

Root cause: recorders.js:698-744 forwards user-supplied URL to capture sidecar, falls back to raw net.connect/dgram against any host/port. No hostname validation.

Fix (3 steps):

  1. Allow-list protocols at top of handler:
const ALLOWED = new Set(["srt:", "rtmp:", "rtsp:"]);
if (!ALLOWED.has(parsedUrl.protocol))
  return res.status(400).json({ error: "Protocol not allowed" });
  1. Block internal addresses — reject RFC1918, link-local, loopback, cloud metadata:
const BLOCKED = ["10.","172.16.","192.168.","127.","169.254.","fd","::1","fe80:","100.64."];
if (BLOCKED.some(r => hostname.startsWith(r)))
  return res.status(403).json({ error: "Internal addresses blocked" });
  1. Strict body validationnew URL(), reject non-string/missing fields.

Files: src/routes/recorders.js:698-744
Effort: ~2h
**Priority: P0 — security

## Fix Plan — #104 SSRF via POST /recorders/probe **Root cause:** `recorders.js:698-744` forwards user-supplied URL to capture sidecar, falls back to raw `net.connect`/`dgram` against any host/port. No hostname validation. **Fix (3 steps):** 1. **Allow-list protocols** at top of handler: ```js const ALLOWED = new Set(["srt:", "rtmp:", "rtsp:"]); if (!ALLOWED.has(parsedUrl.protocol)) return res.status(400).json({ error: "Protocol not allowed" }); ``` 2. **Block internal addresses** — reject RFC1918, link-local, loopback, cloud metadata: ```js const BLOCKED = ["10.","172.16.","192.168.","127.","169.254.","fd","::1","fe80:","100.64."]; if (BLOCKED.some(r => hostname.startsWith(r))) return res.status(403).json({ error: "Internal addresses blocked" }); ``` 3. **Strict body validation** — `new URL()`, reject non-string/missing fields. **Files:** `src/routes/recorders.js:698-744` **Effort:** ~2h **Priority: P0 — security
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: WildDragonLLC/dragonflight#104
No description provided.