SSRF in Cloud Environments: Why Metadata Endpoints Still Hand Out Crown Jewels - TECHNOLOGYWAY9411

SSRF in Cloud Environments: Why Metadata Endpoints Still Hand Out Crown Jewels

Server-Side Request Forgery used to be a relatively mild bug. You could maybe scan internal ports, read a few internal pages, occasionally pull a config file. Then everyone moved to the cloud, and SSRF turned into one of the most consequential bug classes on the modern web.

The reason is the metadata service. Every major cloud provider runs an HTTP endpoint inside each virtual machine that hands out instance information, including, in many setups, temporary credentials that can act on behalf of the instance's IAM role. If an attacker can convince a server-side process to make an HTTP request to that endpoint, the attacker walks away with cloud credentials.


This post is about what those endpoints look like, how exploitation actually goes in 2026, and the bypass techniques that still defeat lazy defenses.

The Metadata Endpoints

AWS

The original endpoint lives at http://169.254.169.254/latest/meta-data/. Browsing the tree gives you instance type, hostname, security groups, and crucially, IAM role credentials at iam/security-credentials/<role-name>.

Those credentials are temporary, usually rotating every few hours, and they grant exactly the permissions of the IAM role attached to the instance. In a well-configured environment, that might be limited. In a typical environment, it is enough to read S3 buckets, query DynamoDB, invoke Lambda functions, or pivot to other services.

AWS introduced IMDSv2 in 2019 specifically to stop trivial SSRF from grabbing these credentials. IMDSv2 requires a PUT request first to obtain a session token, then subsequent GET requests must include that token in a header. The PUT and the token requirement together make it harder for vanilla SSRF, which usually only does GETs, to talk to the service.

This is good in theory. In practice, IMDSv1 is still enabled on a huge percentage of instances. AWS only began enforcing IMDSv2-by-default on new instances relatively recently, and existing fleets carry the old configuration forward.

Azure

Azure's metadata service sits at http://169.254.169.254/metadata/instance and requires the Metadata: true header. The credential endpoint is /metadata/identity/oauth2/token.

The header requirement is mildly inconvenient for SSRF that only allows you to control the URL, but plenty of SSRF primitives let you set arbitrary headers, or the application itself is helpfully forwarding your headers along.

GCP

GCP uses http://metadata.google.internal/ or http://169.254.169.254/, and requires the header Metadata-Flavor: Google. Same dynamic as Azure, the header trips up the most basic SSRF but is often bypassed.

Kubernetes

If the application runs in a pod, you may also be able to reach the Kubernetes API server. Service account tokens are mounted into pods at /var/run/secrets/kubernetes.io/serviceaccount/token. SSRF that can read local files or hit the kube API at https://kubernetes.default.svc can grant cluster-level access depending on the service account's role bindings.

Finding the SSRF

Anywhere the server makes an HTTP request based on user input is a candidate. Common patterns:

  • Webhook URLs in account or integration settings
  • "Fetch URL" features in admin panels
  • Image or document import functionality
  • URL preview generators (these are everywhere now)
  • PDF generation services that fetch resources from URLs
  • OAuth callback URLs that the server validates by hitting them
  • XML parsers that resolve external entities
  • SVG rendering services
  • Any avatar or profile picture URL the server downloads
  • WebSocket or SSE proxy endpoints

The PDF generators are particularly fertile ground because they almost always run with a full browser engine, follow redirects, execute JavaScript, and treat any URL as fair game.

The Cleanest Exploitation Path

Once you have an SSRF primitive on AWS with IMDSv1 enabled, the path is mechanical:

  1. Hit http://169.254.169.254/latest/meta-data/iam/security-credentials/ to list role names.
  2. Hit http://169.254.169.254/latest/meta-data/iam/security-credentials/<role-name> to get the JSON blob with AccessKeyIdSecretAccessKey, and Token.
  3. Export those into your shell and start poking with aws CLI commands.

I usually start with aws sts get-caller-identity to confirm the credentials work and see what identity I have. Then aws iam list-attached-role-policies --role-name <role> if I have iam read permissions. Then I look at what services the role can touch and start enumerating from there.

The pattern I see most often: a role intended for "read access to a specific S3 bucket" turns out to also have s3:ListAllMyBuckets, or a misconfigured wildcard that lets you read every bucket the account owns.

Bypass Techniques That Still Work

Defenses against SSRF usually try to block requests to private IP ranges or to specific dangerous hosts. Almost all of these defenses can be defeated.

Alternative IP Representations

169.254.169.254 can also be written as:

  • 2852039166 (decimal)
  • 0xa9fea9fe (hexadecimal)
  • 0251.0376.0251.0376 (octal)
  • 169.254.43518 (mixed)
  • 169.0xfea9fe (partial hex)

Any URL parser that treats the host as a string before resolving it may not normalize these representations, but the underlying socket library will happily resolve them all to the same IP.

DNS Rebinding

If the defense resolves the hostname once, checks that the resulting IP is allowed, and then makes the HTTP request, you can race the two resolutions. Use a DNS server you control that returns a public IP on the first query and 169.254.169.254 on the second. Services like rebind.network exist to make this easy.

Redirect Chains

Many URL filters check only the initial URL. Host a redirect on a domain you control that 302s to the metadata endpoint. If the HTTP client follows redirects without re-validating the destination, you win.

Localhost Aliases

localhost127.0.0.1127.10.0.0.0[::1][::ffff:127.0.0.1], and 0 all reach the local machine on most systems. Blocklists that only target one spelling miss the others.

If the target is an internal service on the same host rather than the metadata endpoint, every localhost alias is fair game.

Protocol Smuggling

Some libraries accept exotic URL schemes. file:// reads local files. gopher:// lets you craft arbitrary TCP payloads, which has been used to talk to Redis, memcached, and SMTP from SSRF. dict:// opens up similar possibilities.

gopher:// is the dangerous one. If the underlying HTTP library is built on libcurl and the application does not restrict schemes, you can construct gopher URLs that send raw bytes to any reachable service. Redis without auth, common in poorly secured environments, is a one-shot path to remote code execution via cron job injection.

URL Parser Confusion

Different parsers disagree about what part of a URL is the host. http://expected-host.com@evil.com/ is treated as visiting evil.com with userinfo expected-host.com by RFC-compliant parsers, but some validation code splits on the first slash or the first colon and thinks the host is expected-host.com.

Other tricks include adding fragments, URL-encoding characters in the host, using IDN punycode, or abusing differences between how the validator parses the URL and how the HTTP client parses it. James Kettle's research on URL parsing inconsistencies is required reading.

What Good Defense Looks Like

Since this is supposed to be useful and not just a how-to-attack, here is what actually stops these bugs:

  • Force IMDSv2 across every instance in your AWS environment, and set a hop limit of 1 so the metadata service is unreachable from containers running on the host.
  • Block egress from application servers to the metadata IP at the network layer, not just in code.
  • Use a dedicated HTTP client for any feature that fetches user-supplied URLs. The client should refuse to follow redirects, refuse non-HTTP schemes, resolve DNS itself and check the resulting IP against an allowlist, and have a tight timeout.
  • Never trust the application's own URL parser for security decisions. The library that actually makes the request decides the destination.
  • Run application servers with IAM roles that have the least possible privilege. If your role can do anything other than the exact API calls it needs, fix that first.

Closing Thought

SSRF is one of the few bug classes where understanding the technique matters more than memorizing payloads. The payloads change as parsers and filters change. The underlying idea, that the server will make a request on your behalf and trust where you send it, is the part you actually need to internalize.

Pick a target. Find a feature that fetches URLs. Make it talk to itself. That is the whole methodology.

No comments:

Post a Comment