The 2019 Capital One breach is the canonical case study. A misconfigured WAF allowed a server-side request forgery against the EC2 instance metadata service. The metadata service returned temporary IAM credentials. The credentials had s3:ListAllMyBuckets and read access to a customer-data bucket. 100 million records exfiltrated. A federal indictment, a $190M settlement, and a Senate hearing followed.
If you read the indictment carefully, the more uncomfortable detail is buried in paragraph 27: both the SSRF-vulnerable WAF rule and the over-privileged IAM role had been flagged in prior internal assessments. Not as critical findings. As routine ones, owned by separate teams, in separate ticket queues, with no link between them. Each, on its own, looked like a P3. Together they were a Senate hearing.
Six and a half years later, the same pattern is the single most common chain we surface across our customer base. This post is the data, the mechanism, and what to do about it.
What happened
In Q1 2026 — January, February, and March — CELVEX Group scanned 612 distinct customer environments across our Sentry and Fortress tiers. Each scan runs both web-application tests (the lineage of OWASP Top 10, plus our custom checks) and cloud-configuration tests (IAM, networking, metadata service exposure, KMS posture). The two streams produce findings into the same chain-correlation engine.
Aggregate numbers from those 612 scans:
- 89 environments (14.5%) produced both a web SSRF finding and a cloud "metadata service accessible to over-privileged role" finding within the same scan window.
- 71 of those 89 (80%) showed the SSRF actually reaching the metadata endpoint when our chain engine simulated the end-to-end path. That is — not just both findings present, but a verified, walking-the-graph kill chain.
- 64 of those 71 (90%) had been previously identified by the customer's own internal teams as the two separate findings, ticketed separately, never linked. Web team filed P3 ("SSRF, but no clear data-access impact"). Cloud team filed P3 ("instance role broader than needed, but defense-in-depth via WAF").
- 17 of those 64 had been ignored long enough to age out of the customer's tracking system entirely. The web ticket was closed "won't fix" because no impact was demonstrated. The cloud ticket was closed "accepted risk" because no exploitation path was demonstrated. Each team was right in isolation. Both teams were wrong together.
Of the 71 with verified end-to-end paths, the median time between the SSRF finding and a hypothetical cloud breach — the time it would take an attacker, given the SSRF, to reach the metadata service, exfiltrate credentials, and use them — was under 8 minutes in our test harness. Most of that was waiting for the metadata service rate limit to reset.
Why it kept working
Three structural reasons, all organizational rather than technical.
Separate teams, separate tools. The web team uses Burp, ZAP, or one of the dynamic application security testing platforms. The cloud team uses a CSPM tool, the cloud provider's native posture management, or both. The web team's findings live in one ticket queue tagged "AppSec." The cloud team's findings live in another tagged "CloudSec." There is rarely a join key between them. SSRF findings list "external host" or "internal IP." Cloud findings list "IAM role ARN." Neither field crosses the boundary.
Severity calculated in isolation. Both findings are P3 in the absence of a demonstrated chain. SSRF without confirmed exfil is "needs investigation." Over-privileged IAM without confirmed reachable attack surface is "best practice violation." The CVSS calculator is not designed to multiply two P3s into a P1. Each finding gets its bucket, the bucket gets prioritized against everything else in that bucket, and the chain is invisible.
The handoff is broken by design. When the web team does want to escalate "this SSRF feels worse than P3, can someone in cloud confirm?", the request bounces. The cloud engineer is not staffed to investigate web findings. They open the ticket, see "SSRF in /api/v2/preview," and have no idea what role the application runs as, what IPs the application can reach, or what credentials are mounted. They reply "please attach a network path diagram." The web engineer doesn't have one. The thread dies.
The structural issue is that SIEM-style correlation works for incident response — after the breach has happened — but the same logic almost never runs in a preventive posture. Preventive scanning produces siloed findings because the scanning tools were built siloed. Customers who have invested years in remediation discipline still have this gap, because the gap is between products, not inside any one product.
What to check today
The remediation here is half technical, half operational.
- Inventory your existing findings as graph nodes, not tickets. Pull every open AppSec ticket and every open CloudSec ticket from the last twelve months. Tag each with the asset it touches: which application, which IAM role, which subnet, which data store. Build a join — even a spreadsheet join — between web findings on application X and cloud findings on application X's role and X's subnet. Anywhere both columns are non-empty is a candidate chain.
- Force IMDSv2 on every cloud instance. AWS has supported it since 2019. Azure has the equivalent guard via the metadata-service token requirement. GCP supports metadata header restriction. None of these block SSRF outright, but all of them require the SSRF to also bypass header-injection protections — which most blind SSRF cannot do. This is the single highest-leverage technical mitigation for the canonical chain in this post.
- Right-size instance roles to what runs on the instance, today. Not what might run someday. Not what was needed during the last data migration. Today. Audit every instance role for unused permissions using the cloud provider's IAM access analyzer. If the instance hasn't called
s3:GetObjectin 90 days, removes3:GetObjectfrom the role. Repeat for every action. - Run a joint AppSec/CloudSec review monthly. Not quarterly. Monthly. Forty-five minutes. The agenda is exactly one item: walk through every web application that has any cloud-side finding open against its hosting role, subnet, or data store, and decide together whether the combined risk is higher than the individual buckets indicate. This meeting did not exist in any of the 64 customer environments where we found stale chains.
- Egress-filter your application servers. The metadata-service endpoint is a fixed IP (
169.254.169.254on AWS and Azure,metadata.google.internalon GCP). Most application code has no business calling it directly — that's what the SDK does, with its own credential caching layer. Block egress to that IP from application security groups except via explicit allowlist. The SDK still works because it talks to the metadata endpoint via the local API; SSRF originating from your application traffic does not.
How CELVEX Group tests for this
Our chain-correlation engine is the product surface that turns the silo into a graph. The relevant test is CHAIN-WEB-CLOUD-SSRF-METADATA-001, defined in core/test_catalog/_supplement_chains_2026-03.py. The test executes in three phases, each gated on the previous phase succeeding:
- Phase 1 — SSRF discovery. Standard SSRF probes against every URL-accepting parameter in the application, including the post-2024 generation of "blind via DNS callback" detection that catches request-forgery vectors that don't reflect content. Output: enumerated SSRF entry points with the attacker-controlled host header position.
- Phase 2 — Metadata endpoint reachability. For every confirmed SSRF, the test probes whether the SSRF can reach the cloud-provider-specific metadata endpoint. We test IMDSv1 (AWS legacy), IMDSv2 (where token bypass via header injection is sometimes possible), Azure IMDS, and GCP metadata. Output: a boolean reachability per SSRF entry point.
- Phase 3 — Credential exfil simulation and IAM evaluation. Where reachability is confirmed, the test fetches the credential payload (read-only, never used to make further calls), parses the IAM role, and queries the cloud provider's IAM simulator API to enumerate what the role can do. Output: a list of high-impact actions the leaked role would grant — list buckets, read database credentials, assume other roles, modify IAM policies.
Findings are emitted as a single chained CRITICAL when all three phases succeed, regardless of how each individual primitive would be scored in isolation. The customer report shows the full path, the median attacker time-to-exfil, and the specific IMDS configuration, IAM permission, or egress rule that would have broken the chain. Fix any one of them and the chain breaks. The tests rerun on every scan, so a regression — someone re-introducing an over-permissive role on a still-SSRF-vulnerable app — is caught the same week, not the next quarter.
The test pairs with our cross-team report view, which shows AppSec and CloudSec findings on the same asset on a single page. Customers tell us this single view is the most-shared dashboard in the platform, because it gives a director-level engineer something a pen-test PDF never could: a picture of the chain, from the public endpoint all the way to the data, drawn once, owned by neither team in isolation.
Bottom line
The Capital One pattern is not rare. It is the median enterprise. 14.5% of the environments we scanned in Q1 2026 had the canonical SSRF-to-metadata chain. 90% of the time the customer's own teams had already found both halves and dismissed each as a P3. The breach that happens next is almost always one your security organization already saw, in two pieces, sitting in two queues, owned by two people who have never been in the same meeting.
The fix is structural. Force the cross-team conversation. Inventory findings as graph nodes. Run the chain analysis preventively, before incident response is the function that finally connects the dots. The test exists. The data exists. The pattern is six and a half years old. The only variable is whether you find your version of it before or after a Senate hearing.
Sources
- United States v. Paige A. Thompson, indictment (W.D. Wash. 2019)
- OCC consent order: Capital One, $80M civil penalty (Aug 2020)
- AWS Security Blog: Defense in depth — IMDSv2 and SSRF
- Microsoft: Azure Instance Metadata Service
- Google Cloud: Compute Engine metadata overview
- CELVEX Group platform: Chain-correlation engine and cross-team views
Run a free Exposure Check — 60 seconds, no signup
See whether your public web surface and your cloud metadata posture combine into the same chain we found in 14.5% of Q1 environments. No account required.
Start your Exposure Check