An SSRF filter that blocks 127.0.0.1 and the private ranges is standard. Most researchers hit that wall and move on. The way around it isn't a payload, it's a timing gap between when the application validates the URL and when it actually fetches it.
The shape of the attack:
You submit a URL with a domain you control. Let's say my-ssrf-test.com. The backend validates it by resolving the domain and checking the IP address. Your DNS record points to a public IP at that moment, so the validation passes. The URL gets marked as safe and stored in the system.
;; Before submission (public, passes the allowlist)
my-ssrf-test.com. 5 IN A 203.0.113.42After the validation passes, you change the DNS A record for your domain. Point it to 127.0.0.1 or any internal IP you want to target. The domain stays the same. The URL stays the same. But where it resolves to has changed.
;; After validation (re-pointed inside the perimeter)
my-ssrf-test.com. 5 IN A 127.0.0.1If the application only checked the IP during the initial validation but resolves the domain again every time it makes a request, your previously validated "safe" URL now points to localhost or internal services.
The backend trusts the domain because it passed validation. It doesn't recheck where that domain actually points before making subsequent requests. The SSRF filter gets bypassed through timing rather than exploitation.
This is a race condition of sorts. You're racing to change the DNS record after validation but before the actual request gets made. In some cases, there's enough time to do this manually. In others, you need to automate it or use DNS services that support fast TTL changes.
Why does this work? Two reasons.
First, many applications separate validation from execution. They check the IP when you submit the URL, store it as safe, then trust that validation for future requests without rechecking. The assumption is that a domain won't change where it points. That assumption is wrong.
Second, DNS caching behavior varies. Some applications cache DNS lookups aggressively. Others don't cache at all and resolve the domain fresh every time. You're looking for the latter. If the application doesn't cache DNS results but also doesn't revalidate IPs on every request, you have a window.
The impact depends on what's running internally. Localhost services. Internal APIs. Cloud metadata endpoints. Anything accessible from the server's perspective becomes accessible to you through the SSRF.
I've used this technique to access:
- Internal admin panels running on localhost
- Cloud metadata services (169.254.169.254)
- Internal microservices not exposed to the internet
- Database management interfaces
- Internal monitoring dashboards
Each time, the initial SSRF looked blocked. Standard filters caught direct attempts to hit localhost or private IPs. But the DNS rebinding bypass worked because the validation logic didn't account for DNS records changing after the fact.
This is an edge case. Most SSRF filters either cache DNS lookups properly or revalidate on every request. When you find an application that does neither, the technique turns a blocked SSRF into an internal network pivot.
The fix is straightforward. Applications should either:
- Resolve and validate the IP on every request, not just the first one
- Cache DNS lookups and use the cached IP for the actual request
- Implement both validation and execution using the same resolved IP
Most don't. They validate once, trust the domain and resolve it again later. That gap is what makes this work.
Testing for this requires some setup. You need a domain you control with the ability to change DNS records quickly. Some DNS providers support very low TTL values and instant propagation. Use those. Submit your URL, wait for validation, change the DNS, then trigger the request that uses the stored URL.
Timing matters. If the application caches DNS for any meaningful duration, this won't work. If it revalidates IPs before every request, this won't work. You're looking for the specific case where validation happens once but DNS resolution happens multiple times without revalidation.
When it does work, document it carefully. Show the initial validation with the public IP. Show the DNS change. Show the subsequent request hitting the internal target. Make it clear that the vulnerability exists because of the gap between validation and execution.
It doesn't work often. When it does, the impact is full internal-network reach from a parameter that looked safely sandboxed. Keep it in the toolkit for SSRF findings where the filter blocks the obvious localhost and private-IP attempts.
