LC-152-TOCTOU: pin outbound connections to the SSRF-validated IP (close the DNS-rebind re-resolve window) #286
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem (tracking ticket for a documented accepted-residual)
The LC-152 SSRF guard validates a destination host by resolving it and checking every address against the public-IP filter, then lets the HTTP client (
reqwest/ureq) connect. The client performs its OWN DNS resolution to connect. Between the guard's resolve and the client's resolve there is a TOCTOU window: a hostile DNS server can answer public on the first lookup and private on the second (classic DNS rebinding), so a host that passed the filter can still connect to an internal address.This is a KNOWN, accepted residual, not a new finding. It is documented in prose (
docs/protocol-bridges.md:57) and is shared by every outbound path that uses the guard. This ticket exists only so the residual is tracked on the board rather than living solely in a code-adjacent comment, and so any future "is SSRF fully closed?" question has a ticket to point at.Where (paths sharing the residual)
server/src/http_client.rs- the LC-152 two-layer guard (URL-input validation + custom resolver). The custom resolver narrows but does not fully eliminate the window because the literal connect still re-resolves.outgoing.rs(LC-75 webhook delivery),routes/unfurl.rs,routes/slash.rs,bridge_avatar.rs(LC-78 avatar proxy),push/mod.rs.desktop/src/net_guard.rshas aPublicOnlyResolverthat closes the resolve-then-connect window for hostnames on its guarded agent (it routes the actual connect through the validated resolution), so the desktop self-updater path is the closest existing example of the mitigation shape.Failure mode / cost
An attacker who controls DNS for a hostname they can get the server to fetch (a webhook URL, an unfurl target, a bridge avatar URL) can rebind it to an internal address after the guard's check, reaching internal services the public-IP filter was meant to block. Exploitation requires attacker-controlled DNS and a same-host fetch the server initiates; the impact is internal SSRF.
Proposed scope
PublicOnlyResolverapproach, for every outbound path.http_client.rsso the address the filter approved is the address connected to, for all callers.Acceptance
Either the guarded client connects only to the address the SSRF filter validated (no second independent resolve), with a test that a rebind-after-check answer is refused; OR a recorded accept-the-risk decision referencing this ticket.
Anchored to LC-152 (server-side SSRF/TOCTOU). Severity: security, low likelihood (needs attacker-controlled DNS), known + documented.