One SSRF to 106 Million Records
The 2019 Capital One breach, reconstructed from the public record
In 2019 a single server-side request forgery against a misconfigured web application firewall turned into the theft of 106 million credit card applicants' records. The chain is short and every link is an ordinary cloud misconfiguration: an internet-facing WAF that could be coerced into making requests on the attacker's behalf, an EC2 metadata service that handed out credentials to anyone who asked, an IAM role with far more S3 access than it needed, and no alerting on bulk data egress. The intrusion began in March 2019 and was not discovered until July — and then only because someone emailed Capital One's responsible-disclosure address after the attacker posted about it publicly. Press play, then switch to Defend mode and try to break the chain.
Source: U.S. Department of Justice — United States v. Paige Thompson (W.D. Wash.)
Educational reconstruction based on public court records and Capital One's disclosure. This replay is event-counted rather than action-counted: unlike incidents where responders published action telemetry, the public record supports a sequence of events, not a per-step action tally. Dates come from the public record; the commands shown are representative illustrations of the documented techniques, not recovered artifacts.
Break the Chain
You're the defender. Deploy up to 2 controls, then run the intrusion. Each control you place severs the attack chain at one point — the agent reaches everything up to it, and nothing beyond. Contain it as early as you can.
Deploy controls, then run to see how far the agent gets.
- Public internetInternet-facing endpointattacker's starting position
- Capital One AWS · perimeter↓reach the proxyMisconfigured WAFEC2 · coercible into internal requests
- Capital One AWS · instance↓SSRFInstance metadata service169.254.169.254 · IMDSv1
- ↓request credentialsWAF instance roletemporary credentials · broad S3 access
- Capital One AWS · storage↓list + readS3 buckets700+ buckets readable by the role
- ↓syncCustomer recordscredit card applications · SSNs · account numbers
Defensive lessons
- 1Enforce IMDSv2 everywhere. Requiring a session token (PUT, then GET with the token, with a hop limit) is what turns a trivially-exploitable SSRF into a dead end. This is a one-setting change and it breaks the entire chain at step two.
- 2Scope instance roles to the job. A web application firewall had list-and-read access across hundreds of S3 buckets. If the role had carried only what a WAF needs, stolen credentials would have unlocked nothing worth taking.
- 3Treat SSRF as a cloud-credential vulnerability, not a web bug. In a cloud environment, any component that can be induced to fetch an attacker-chosen URL is one request away from the credential store.
- 4Alert on bulk egress. Hundreds of buckets synced out over months produced no alarm. Volume-based detection on data access and egress is the control that shortens dwell time when prevention fails.
- 5Encrypt or tokenize the sensitive fields. Read access to an object should not automatically mean read access to the SSNs inside it — field-level protection limits what a successful exfiltration is actually worth.
- 6Publish and monitor a responsible-disclosure address. It is genuinely how this incident ended. That channel is a security control; make sure someone reads it.
- 7The whole chain is four ordinary misconfigurations. No zero-day, no malware, no insider access to production. Attack surface management is largely the discipline of not leaving these four in place at the same time.