You're on an API assessment and you find an endpoint that fetches a URL server-side: PUT /api/v1/users/me/avatar with a JSON body of {"avatar_url": "https://..."}. The server downloads whatever's at that URL and stores it as the user's picture. First instinct, point it at http://169.254.169.254/latest/meta-data/iam/security-credentials/ and see what comes back.
You get a 400. invalid host. There's an allowlist.
That's not the end of the test. That's where it actually starts.
Most people who find an SSRF sink stop at "the obvious payload got blocked" and move on. That's the wrong instinct, because a hostname allowlist that only checks the string you sent is checking the wrong thing, and there are several ways to hand it a string that passes validation and resolves somewhere else entirely.
Open redirects hand you the bypass for free. If the app or any third-party service it trusts has an open redirect, point the fetcher at that instead of the internal target directly: avatar_url: https://trusted-partner.com/redirect?to=http://169.254.169.254/.... A filter checking the hostname in the request never sees the internal IP, because the internal IP only shows up after the server-side fetcher follows the 302 it was never told to distrust.
IP encoding breaks string-match filters that never learned there's more than one way to write an address. 127.0.0.1 also parses as the decimal integer 2130706433, the octal 0177.0.0.1, the hex 0x7f000001, and the IPv6-mapped ::ffff:127.0.0.1. A filter built around a regex for dotted-quad notation lets every one of those through, and most HTTP client libraries resolve all of them to the same address.
DNS rebinding attacks the gap between the check and the fetch. If validation resolves the hostname once to confirm it's "safe" and the actual fetch resolves it again later, point the domain at a DNS record with a five-second TTL: safe IP on the first lookup, 169.254.169.254 on the second. The two resolutions don't have to agree, and most naive SSRF filters assume they will.
URL parser disagreement is the one that breaks the most real filters. http://expected-trusted-host.com@169.254.169.254/ parses expected-trusted-host.com as userinfo and 169.254.169.254 as the actual host under the URL spec, but a filter written with a sloppy regex that just checks whether the trusted hostname appears anywhere in the string will happily pass it. This single class of bug, the validating code and the fetching code parsing the same URL two different ways, is behind a disproportionate share of real-world SSRF findings in APIs specifically, because APIs pass URLs through more layers, proxies, SDKs, internal microservices, than a browser-facing form field ever does.
And if none of that gets you a response you can see, the bug isn't dead, it's blind. Point the avatar_url at a domain you control with a unique subdomain per test and watch your own DNS logs or an out-of-band tool for the callback. A hit proves the server fetched your URL even when the response never comes back to you. Blind SSRF is still SSRF, and it's still a path to everything behind that internal fetch.
Once you're actually reaching internal addresses, the payoff isn't "I can make the server request other things," it's what's sitting at those addresses. Cloud metadata endpoints (169.254.169.254 on AWS, the GCP and Azure equivalents) hand out temporary IAM credentials to anything that can reach them from inside the VPC, no auth required, because the whole point of the endpoint is that instances trust their own network. An SSRF that reaches metadata isn't a medium-severity "server made an unintended request" finding. It's full cloud account compromise wearing an avatar-upload feature as a disguise.
This is the exact chain Codelivly's API Hacking Book for Beginners walks through in full: finding SSRF sinks in real API surfaces, proving blind SSRF with out-of-band techniques, defeating allowlists the way above, and turning that access into the metadata and internal-service compromise that makes an SSRF finding worth reporting as critical instead of informational. If you want the hands-on rep before the reading, the free SSRF lab and SSRF room on codelivly.com put you in front of exactly this bypass chain.
Top comments (0)