SSRF Against the Cloud Metadata Service to Read the IAM Role
The challenge
This image proxy fetches whatever URL you hand it and returns the body - with no allow-list of where it may go. It runs on a cloud server that exposes an internal metadata service at 169.254.169.254. Point the proxy at that service's IAM credentials path, send it, and read the name of the IAM role it leaks.
What you'll learn
- Recognize server-side request forgery (SSRF) when a server fetches a user-supplied URL with no allow-list
- Reach the cloud instance metadata service at the link-local address 169.254.169.254
- Navigate the IAM security-credentials path to read the attached role name
- Understand how the leaked role name leads to short-lived cloud credentials
- Explain why an outbound allow-list and IMDSv2 are the correct defenses
Skills tested
Prerequisites
- Basic understanding of HTTP requests and URLs
- Familiarity with the idea that cloud servers run inside an instance with an attached role
- Awareness that link-local addresses (169.254.0.0/16) are reachable only from the host itself
How it works
Server-side request forgery (SSRF) occurs when an application fetches a URL that the user supplies, and the attacker can make it request a destination the application never intended. A feature like an image proxy, a webhook tester, or a link unfurler is the classic vehicle: it takes a URL and dutifully retrieves it from the server. Because the request originates from the server, it can reach addresses the attacker cannot touch from the outside - internal services, admin panels, and the cloud metadata endpoint.
On a cloud instance, the instance metadata service (IMDS) answers at the link-local address 169.254.169.254. It is only reachable from the instance itself, so anything that runs there - including a vulnerable proxy - can query it. The path /latest/meta-data/iam/security-credentials/ lists the IAM role attached to the box, and appending that role name returns live, short-lived AWS access keys. An SSRF that can reach IMDS therefore converts directly into cloud credentials with the instance's permissions.
The flaw is not the metadata service; it is that the proxy fetched an arbitrary URL with no restriction on the destination. Without an allow-list, the server is a confused deputy: it carries the attacker's intent with its own network position and privileges.
Common mistakes
- Assuming the proxy can only reach public CDNs, when it will fetch any address the server itself can route to, including internal and link-local ones.
- Targeting
localhostor127.0.0.1first and missing that the cloud secrets live at the separate link-local metadata address169.254.169.254. - Stopping at the credentials directory listing without realizing the role name it returns is the answer (and the next hop to live keys).
- Treating SSRF as a parsing bug rather than the real flaw: there is no allow-list constraining where the server may send requests.
How to defend against it
The fix is to constrain where the server may send outbound requests and to harden the metadata service itself. Never fetch a user-supplied URL without validating the resolved destination against an allow-list.
- Allow-list permitted hosts/domains for the proxy and reject everything else; resolve the hostname and block private, loopback, and link-local ranges (including
169.254.0.0/16) after resolution to defeat DNS rebinding. - Enforce IMDSv2 (session-token required) so a simple GET-based SSRF can no longer read credentials, and set the metadata hop limit to 1.
- Scope the instance IAM role to least privilege so a leaked credential is far less useful.
- Log and alert on outbound fetches to internal or link-local addresses, so SSRF attempts are visible.