AppSec · Jun 19, 2026 · 6 min read

SSRF that reaches cloud metadata is critical, not 'maybe'

A server-side request that returns the cloud instance role credentials is not a medium - it is a confirmed path to your whole account.

Most scanners rate server-side request forgery as a flat medium. The reason is structural: they prove only that the server made an outbound request, mark the checkbox, and move on. But "the server fetched a URL we chose" and "the server handed back temporary cloud credentials" are not the same finding, and rating them the same is how a real account-takeover path ends up buried in a backlog next to a missing security header.

The thing that decides severity is not the request, it is the destination. An SSRF that can reach an internal admin port is one risk. An SSRF that reaches the cloud metadata endpoint at 169.254.169.254 and reads the instance role credentials is a confirmed path to your entire account. Same vulnerability class, two different incidents.

01The severity is in the destination, not the request

Reachability and impact are separate facts. Reachability says the parameter will fetch a URL an attacker controls. Impact says what that fetch returned. A tool that stops at the first fact has to guess at the second, and guessing pushes everything to the safe middle - medium - because it cannot tell a blocked request to an unrouted host apart from a successful read of the IAM role.

This is the same trap as testing the render layer instead of the data layer. What the server attempts looks identical whether the boundary held or failed; a fetch to a blocked host and a fetch that drains the IAM role both start as the same outbound call. The truth is in the response body: did it carry instance-identity metadata, a file read over file://, or an internal banner that should never be reachable from the edge? That content, not the outbound attempt, is the signal, and it is the only thing we let decide how loud the finding gets to be.

02The escalation ladder: reachable to likely to confirmed

So we grade SSRF along a ladder, and a finding climbs a rung only when the response earns it. Each step up is a stronger claim backed by stronger evidence, and the top rung is the only one that carries the critical rating:

app endpoint ?url=<attacker> ──► the server fetches a URL we chose reachable the param is an SSRF lever ──► http://169.254.169.254/ internal-only host answers from the edge likely the metadata host is in range ──► /latest/meta-data/iam/security- response carries the role + AccessKeyId, confirmed CRITICAL: account credentials credentials/<role> SecretAccessKey, Token in-band leaked, masked in the report
Each rung is a different fact. Only the third - credentials actually returned in-band - flips the finding to confirmed critical. The first two are real gaps worth fixing, not incidents.

The jump from likely to confirmed is the one that matters, and it is strict. We do not call it confirmed because the metadata host responded; a response can be a redirect, a timeout page, or a WAF block. It is confirmed only when the body contains the thing that proves impact - the role name and the temporary key material that the metadata service returns and nothing else does. In-band proof is what flips the bit.

03How we probe without weaponizing it

Proving an SSRF must never mean running one. The boundary test our engine uses for unsafe upstream consumption sends the URL-bearing parameter a benign, non-routable marker - a host under the reserved .invalid suffix that can never resolve, so no real outbound request ever lands anywhere. The app simply tries to fetch it and fails.

The finding is confirmed in-band, with no out-of-band callback service required: the response has to echo our unique token back inside an upstream-fetch error - a resolver failure, a connection-refused, a name-not-known message from whichever stack the server runs. That pairing - our token plus a genuine fetch failure - is what proves the server forwarded an attacker-supplied URL to an HTTP client with no allow-list. It is read-only, it sends no destructive verb, and the optional OAST path stays off by default. The proof is that the boundary failed, demonstrated without crossing it.

RungWhat the response provedexploit_statusSeverity
ReachableThe parameter forwarded our benign marker to a server-side HTTP client - it is an SSRF lever, but nothing internal was read.reachablemedium
LikelyAn internal-only host that should be unreachable from the edge answered the coerced request, with no sensitive content returned yet.likelyhigh
ConfirmedThe body carried instance-identity metadata, IAM role credentials, or a file read over file:// - data only the vulnerability explains.confirmedcritical

The ladder keeps two promises at once. It never under-rates: a confirmed metadata or file-read SSRF is critical, full stop, because temporary role credentials are a complete account compromise. And it never over-claims: a parameter that merely forwards a request is reachable, not a breach, and we say so rather than inflating it to look thorough.

04What the on-call engineer actually receives

A confirmed cloud-metadata SSRF lands with the PoC request that triggered it and the leaked response attached as evidence, credentials masked so the report itself is not a second exposure. The engineer opens the finding and sees the role name and the shape of the key material the endpoint returned - proof, not a category label that says "SSRF, medium, investigate."

That difference changes the clock. A confirmed critical carries the tightest SLA we set, because an attacker holding instance role credentials can pivot from one vulnerable parameter to your storage, your databases, and your other services in the same account, often within the lifetime of a single temporary token. Rating it medium does not make it less urgent - it just guarantees it gets worked late, after the header warnings and the verbose-error notes that happened to share its severity band.

Separating reachable from likely from confirmed is not bureaucratic bookkeeping. It is what lets a team trust the top of the queue: every critical on the list is a leak someone can reproduce from the attached request, and every reachable is an honest gap rather than a guess dressed up as a breach. Severity you can defend line by line is the only kind that survives an enterprise audit.

Evidence over heuristicsReachable beats foundMonitor → Block: rolling out a gate without slowing teamsIaC-grounded threat modelingAir-gapped AppSec with no phone-homeOne platform vs three tools
See it on your own app →