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
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.
The agent confirmed reachability and enumerated the metadata tree, but stopped short of exfiltrating live credentials. Proof of impact, not a breach.
The chain
- Recon: discovered the proxy and fingerprinted the cloud environment.
- Reason: hypothesized a metadata-service path from the host's tech stack.
- Exploit: confirmed the SSRF reached
169.254.169.254. - 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
| Day | Event |
|---|---|
| 0 | Finding validated, vendor notified |
| 2 | Vendor acknowledged |
| 9 | Patch shipped |
| 30 | Public write-up (this post) |
The whole find-to-proof loop ran autonomously; a human only reviewed the validated finding before disclosure.