Lion Cipher Lab
Exploit and fix a DNS rebinding attack (the resolve-twice gap that defeats an SSRF IP allowlist)
- AppSec / DevSecOps
- Intermediate
- ~60 min
- Free · no account required
Why it matters
The standard advice for stopping SSRF (Server-Side Request Forgery -- tricking your server into fetching an attacker-chosen URL) is: before you fetch a user-supplied URL, resolve the hostname, check the IP against a denylist of private/internal ranges, and only proceed if it looks public. That check feels airtight -- and DNS rebinding walks straight through it. The trick is that there are TWO DNS lookups hiding in 'validate the IP, then connect': one when you resolve to check, and another when the HTTP client resolves to actually open the socket. An attacker who controls the authoritative DNS…
What you'll learn
- explaining DNS rebinding as a TOCTOU (time-of-check to time-of-use) attack: a hostname that resolves to a PUBLIC IP when a guard validates it, then to an INTERNAL IP (127.0.0.1 / 169.254.169.254 / an RFC 1918 address) a moment later when the client actually connects
- seeing precisely why it defeats an SSRF IP allowlist/denylist: the IP is validated at one lookup and used at a SECOND lookup, and the attacker's authoritative DNS (TTL 0) changes the answer in the gap between the two
- building a deterministic, fully-offline rebinding harness in pure Python stdlib (http.server, socketserver, socket, http.client, ipaddress) -- a real internal secret service on 127.0.0.1 plus a stub resolver that flips its answer on the second query
- running the EXPLOIT: a guard that resolves-to-validate then resolves-again-to-connect passes its own check yet reads the internal secret
- applying and PROVING the primary fix -- resolve ONCE and connect to that pinned IP, never re-resolving the name -- and watching the same attack reach the attacker's public box instead of the secret
- applying the defense-in-depth fixes: a post-connect peer-IP check (re-validate the address the socket ACTUALLY used) and a Host allowlist of expected destinations, and knowing which bug each one catches
- discovering the INCOMPLETE fix: pinning the first request but letting a redirect be followed by name re-opens the whole hole, because the HTTP client re-resolves the flipped record on the new hop
- carrying the honest trade-offs: pinning breaks round-robin/CDN DNS, redirects and re-resolving HTTP clients silently reintroduce the gap, and the real control is pin-and-reuse + post-connect peer check + an egress policy that cannot route to RFC 1918 or the metadata IP at all
Steps in this lab
- What DNS rebinding is -- and what it is NOT (SSRF, host-header injection)
- Stand up the offline lab: a real internal secret, a real attacker server, and an honest DNS stub
- Write the vulnerable guard -- the one that resolves TWICE
- Prove the flip, then run the EXPLOIT
- Fix A -- resolve ONCE and pin the IP (the one move that matters)
- Fix B -- the post-connect peer check (for code that must connect by name)
- The INCOMPLETE fix: pin the first request, then follow a redirect by name
- Fix C -- a Host allowlist of expected destinations
- The judgment: where each fix breaks, and the control that actually holds
- What you built -- and the one sentence to carry