Skip to main content

The Capital One Breach: SSRF, Metadata Endpoints and Over-Privileged Roles

A week after Capital One disclosed its breach, here's what the public record says, why SSRF keeps coming up, and what to check on your own cloud servers.

The CompleteStatus Team 7 min read

The Capital One Breach: SSRF, Metadata Endpoints and Over-Privileged Roles

Want this checked continuously?

CompleteStatus grades your headers, SSL and email security 24/7 — free, commercial use allowed.

Monitor your site free

On July 29, Capital One disclosed that someone had obtained personal information on roughly 100 million people in the US and around 6 million in Canada. Most of the data was from credit card applications going back to 2005. The company says around 140,000 US Social Security numbers and around 80,000 linked bank account numbers were among it, along with about a million Canadian Social Insurance Numbers. A suspect was arrested the same day.

A week on, a lot of the discussion has been about who did it. The more useful discussion for the rest of us is how, because the technique described in the court filings isn't specific to banks, or to AWS, or to large companies. It's a pattern that works against any cloud server that fetches things on behalf of users and holds credentials it doesn't need.

What the public record says

The details below come from the FBI's criminal complaint and Capital One's own statements. Neither is a full technical postmortem, and some of the analysis circulating is informed guesswork, so treat the specifics with appropriate caution.

  • A misconfigured web application firewall is the starting point. The complaint says a firewall misconfiguration allowed commands to reach and be executed on a server in Capital One's cloud environment.
  • Those commands obtained credentials for a cloud role (the complaint refers to an account with "WAF-Role" in its name).
  • The role could list and read storage buckets. Using those credentials, the attacker listed the buckets and copied data from them. The complaint dates the activity to the spring, months before anyone noticed.
  • Capital One found out from a tip. On July 17 someone emailed its responsible disclosure address pointing to a file on GitHub. That's how a months-old intrusion was discovered.

The part the complaint doesn't spell out is exactly how a firewall "misconfiguration" became credentials. Several security researchers, and some of the press coverage, point to server-side request forgery (SSRF) against the instance metadata service. Capital One hasn't confirmed that in detail, but it fits the facts, and it's worth understanding whether or not it turns out to be exactly right here.

SSRF and the metadata service in two paragraphs

Server-side request forgery is when you can get a server to make an HTTP request of your choosing. Anything that fetches a URL on a user's behalf is a candidate: link previews, webhook senders, "import from URL" features, PDF generators, image resizers, and proxies or firewalls that forward requests upstream. If the server doesn't check where that URL points, an attacker can point it inward.

On AWS (and Google Cloud and Azure, with different details), every instance can reach a metadata service at 169.254.169.254. On EC2, if the instance has an IAM role attached, the metadata service will hand temporary credentials for that role to anything on the box that asks, with a plain GET request:

curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/<role-name>

No authentication, no special headers. That's by design: it's how the SDKs on your instance get credentials without you putting keys in config files, which is a genuinely good thing. But it means an SSRF bug on an instance with a role is, in effect, a credential leak. The attacker makes your server fetch that URL and return the response.

The real multiplier was the role

SSRF gives an attacker the instance's credentials. What those credentials can do is decided by you. A role for a web application firewall arguably needs very little: perhaps writing logs somewhere. According to the complaint, this one could list buckets and read from them, including buckets full of application data.

That's the lesson that applies to every team regardless of what went wrong at the edge. Bugs will happen. The question is how much a single bug is worth to an attacker, and that's mostly determined by IAM policies nobody has looked at since the day they were written. Common patterns worth hunting for:

  • Wildcards. "Action": "s3:*" or "Resource": "*" on a role that only needs one bucket.
  • Roles shared across unrelated servers. One "app-server" role that's attached to the web tier, the worker tier and a proxy, and therefore has the union of everything they need.
  • List permissions nobody needs. s3:ListAllMyBuckets hands an attacker a map. Most applications know their bucket names already.
  • Roles on instances that don't need any. If a box doesn't call AWS APIs, it shouldn't have a role attached.

What to check this week

1. Find everything that fetches a URL. Search your codebase for outbound HTTP calls where the URL (or any part of it, including the host) comes from user input or a stored setting a user can edit. Include internal tools and admin panels; attackers find those too.

2. Validate destinations properly. For each one, resolve the hostname and reject private, loopback and link-local addresses, including 169.254.0.0/16, before connecting. Then connect to the IP you validated, not a fresh lookup, and repeat the check on every redirect. Checking the hostname string alone is not enough: DNS names can point anywhere, and redirects can bounce a request somewhere you never checked.

3. Block the metadata service where it isn't needed. If a process doesn't need instance credentials, it shouldn't be able to reach 169.254.169.254 at all. On Linux you can restrict it by user:

# Only root (or the user your AWS agent runs as) may reach the metadata service
iptables -A OUTPUT -d 169.254.169.254 -m owner ! --uid-owner root -j REJECT

This needs care if your application itself uses the SDK with the instance role, but for proxies, firewalls, renderers and other components that only touch the network on behalf of users, it's a cheap, strong control.

4. Cut roles down. Use the IAM access advisor to see which services each role has actually used recently, and remove the rest. Scope S3 permissions to specific bucket ARNs.

5. Alert on unusual API use. Turn on CloudTrail everywhere if it isn't already, and alert on things that are rare for a given role: ListBuckets from a role that never lists, large volumes of GetObject from an unfamiliar role, or instance credentials being used from an IP address outside your own infrastructure. The complaint describes months between access and discovery. Detection from your own logs is the only thing that shortens that.

6. Watch for your data in public. Capital One learned about this from a stranger's email. Make sure your responsible disclosure address exists, is monitored, and reaches someone who will act on it.

Not a cloud problem, a configuration problem

It would be easy to read this as "the cloud is insecure". It isn't really. The same pattern (a server that can be tricked into making requests, holding credentials that open far more than it needs) has existed on-premises for as long as internal services have trusted anything on the internal network. The cloud made the credentials easier to reach with one well-known URL, and made the blast radius a question of policy documents rather than network diagrams.

We made a similar point after Equifax: the initial flaw gets the headlines, but the size of the damage is decided by everything that happened after it. Most of that is within your control, and none of it requires waiting for a vendor.

External monitoring, including what CompleteStatus does, sees your site from the outside: certificates, headers, what's publicly exposed. It won't audit your IAM policies for you, so put that on this week's list separately.

Written with AI assistance and reviewed by the CompleteStatus team.