DNS Rebinding vs SSRF Filters: When Hostname Checks Lie
SSRF filters often resolve a hostname, decide it is “safe,” then fetch later. DNS rebinding exploits that gap: the first lookup returns a public address the filter accepts; a later lookup returns 127.0.0.1 or an internal IP. Your controlled name still looks friendly in the request log.
This is not the same bug as classic SSRF with a raw IP. Programs that block RFC1918 literals still fall over when validation and fetch use different resolutions.
How the race works
- Attacker owns
evil.exampleand serves DNS with a very short TTL (or alternating A records). - App validates: resolve → public IP → allow.
- App fetches (or a second worker fetches) after TTL expiry.
- New resolution points at loopback, metadata, or an internal service.
Pinning the IP after the first resolve kills most of this. Caching the hostname string without the IP does not.
Where hunters actually see it
- Webhooks and “fetch URL” preview features
- PDF/HTML renderers that pull remote assets
- Image proxies and link unfurlers
- CI runners that clone or curl user-supplied URLs
- Browser-side twists: rebinding against a victim’s LAN (different impact story — keep scopes separate)
Server-side URL fetchers are the usual bounty surface. Browser rebinding against corporate LANs is messier ethically and often out of scope.
Practical lab proof
Use a rebinding helper (or your own authoritative DNS) that flips from a public IP to 127.0.0.1. Point the app’s URL parameter at your hostname. Watch for:
- Responses that echo local service banners
- Timing that matches an internal hop
- Error strings unique to localhost listeners
On cloud targets, try rebinding toward link-local metadata ranges only if the program’s SSRF policy allows it — many require special care or forbid metadata entirely.
Filter mistakes that keep paying
- Allowlist by hostname suffix (
.corp.cdn.com) without pinning IPs - Blocklist of string IPs while accepting A records that resolve later
- DNS resolve in the edge, fetch in a sidecar with its own resolver
- “Safe” schemes only (
https://) with no post-connect IP check - Trusting redirects after the initial allow decision
Redirect chains plus rebinding compound nicely: first hop passes, second hop lands inside.
Report language triagers accept
State the validation path vs the fetch path. Show two DNS answers with timestamps/TTLs. Include the exact URL parameter and the internal indicator you received. Cite SSRF impact (internal port scan lite, admin panel on loopback, cloud metadata) without dumping unrelated host inventories.
Map severity to what you actually reached. “Could rebind” alone is weak; “fetched http://127.0.0.1:8080/ health JSON” is strong.
Fixes worth recommending
- Resolve once, pin the IP, connect only to that address, re-check after redirects
- Deny private, link-local, and metadata ranges at connect time
- Prefer allowlists of known hosts with pinned addresses over denylists
- Disable following redirects for user-controlled URLs when possible
For taxonomy, pair PortSwigger SSRF with CWE-918 and OWASP’s SSRF prevention notes.
If the app still “trusts the name,” assume DNS will disagree with the filter twice.