Reading an Internal Network Through a 400 vs a 504
The fetcher wouldn't return a response body — but the difference between a fast error and a slow timeout was all the answer I needed.
Target anonymized · an AI media-generation platform
Server-side request forgery is easiest when the response comes back to you. This one didn't — and it's a nice example of how a "blind" bug is still very much a bug when the server leaks state through how it fails.
The spark
An editor feature fetched an asset from a URL I provided. It refused internal IP addresses on direct input, so I pointed it at a server I controlled that issued a redirect to wherever I actually wanted it to go. The fetch followed the redirect — past the filter that only ever looked at my original URL.
Digging in
No response body ever came back, so at first this felt like a dead end. Then I noticed the failures weren't identical. Aiming the redirect at an internal address and port that was alive returned one status quickly; a dead one returned a different status after a delay. That's an oracle.
Blind SSRF plus a distinguishable failure mode is just SSRF with extra steps. A fast
400and a slow504are two different answers to "is this host up?"
The exploit
Point the redirect at internal ip:port combinations and read the response code and timing as a yes/no. Sweep the ranges and you've mapped which internal services are listening — from the outside, through a public feature, with no credentials on the internal network.
submit → https://attacker.example/redirect?to=http://10.x.x.x:PORT
fast 4xx = reachable / listening
slow 5xx = not
Impact
Internal network and port reconnaissance that should be impossible from outside — a strong foothold and the classic first step toward reaching metadata services or unauthenticated internal APIs.
The fix
Resolve and re-validate the final destination after each redirect, deny internal and link-local ranges on every hop, and normalise error responses and timing so the fetcher can't be used as an oracle.
All identifiers, targets and payloads in this post are anonymized or defanged. Findings were reported and resolved through responsible disclosure.