← All research
GuideMarch 4, 2026·2 min read

How an agent chained an SSRF into cloud-metadata credentials

A walk through the reasoning chain, the exploit, and the responsible-disclosure timeline, start to finish, no human in the loop.

By The Rift team

Worked example

A worked example on a synthetic target, written to show how the agent reasons. Our first real coordinated disclosures will carry a CVE and a credited researcher.

Most scanners stop at "this endpoint reflects a URL, possible SSRF." Proving it matters is where the work actually is. Here's how a Rift agent took a low-signal suspicion and turned it into a confirmed, credential-stealing exploit chain.

The suspicion

During recon the agent mapped an image-proxy endpoint that fetched a caller-supplied URL:

POST /api/preview HTTP/1.1
Host: app.example.com
Content-Type: application/json

{ "url": "https://example.com/logo.png" }

A signature tool flags this as a possible SSRF and moves on. The agent instead asked a sharper question: can this reach anything that matters?

Proving reach

It probed the link-local metadata address and watched the response timing and body shape rather than trusting status codes alone:

curl -s app.example.com/api/preview \
  -d '{"url":"http://169.254.169.254/latest/meta-data/"}'

The proxy happily returned the metadata index: reachable, unauthenticated.

Safe-by-default exploitation

The agent confirmed reachability and enumerated the metadata tree, but stopped short of exfiltrating live credentials. Proof of impact, not a breach.

The chain

  1. Recon: discovered the proxy and fingerprinted the cloud environment.
  2. Reason: hypothesized a metadata-service path from the host's tech stack.
  3. Exploit: confirmed the SSRF reached 169.254.169.254.
  4. Validate: an independent validator reproduced it from a clean session.

Only after the validator agreed did this surface as a finding, with the request, the response, and a one-line fix.

The fix

Allowlist outbound hosts and block link-local ranges at the proxy:

const BLOCKED = ["169.254.169.254", "metadata.google.internal"];
if (BLOCKED.includes(new URL(url).hostname)) throw new Error("blocked host");

Disclosure timeline

DayEvent
0Finding validated, vendor notified
2Vendor acknowledged
9Patch shipped
30Public write-up (this post)

The whole find-to-proof loop ran autonomously; a human only reviewed the validated finding before disclosure.