In this project, I took on the role of an ethical hacker to demonstrate how a simple web vulnerability can lead to a full-scale cloud data breach. By simulating the Capital One attack pattern, I learned exactly how SSRF and IMDSv1 work together to expose sensitive data.

1. The Vulnerable Architecture

Using AWS CloudFormation, I deployed an intentionally weak infrastructure:

  • EC2 Instance: A web server running a Flask app with an unvalidated /fetch endpoint.
  • IAM Role: An over-permissioned identity with full access to S3.
  • S3 Bucket: A "private" storage container holding simulated customer records.
Inspecting the template configuration to verify the instance type and setup.
Caption: Inspecting the template configuration to verify the instance type and setup.

2. Understanding the SSRF Vulnerability

The flaw was a Server-Side Request Forgery (SSRF) vulnerability. The application code allowed me to pass a URL as a parameter, which the server would then fetch on my behalf without checking if that URL was internal or external.

The Vulnerable Code:

Python

@app.route('/fetch')
def fetch_url():
    url = request.args.get('url')
    # VULNERABLE: No validation of the URL
    response = requests.get(url)
    return response.text

3. The Attack Chain

Step 1: Accessing the Internal Metadata Service

I started by tricking the server into querying its own Instance Metadata Service (IMDS) at the internal IP 169.254.169.254. Because the server was using IMDSv1, it responded to my simple GET request immediately.

Using the SSRF to list internal metadata categories, confirming I have a “foot in the door.”
Caption: Using the SSRF to list internal metadata categories, confirming I have a "foot in the door."

Step 2: Stealing the Identity Keys

Once I saw the iam/ directory, I knew I could find the server's security credentials. I used the SSRF to pull the temporary AccessKeyId, SecretAccessKey, and SessionToken.

Extracting the JSON credentials and using ‘sts get-caller-identity’ to confirm I have assumed the server’s role.
Caption: Extracting the JSON credentials and using 'sts get-caller-identity' to confirm I have assumed the server's role.

Step 3: Exfiltrating the Data

With the stolen keys configured, I had the server's permissions. I listed the S3 buckets and found the target: lab02-ssrf-data-490017368516.

Discovering the private bucket and its sensitive contents via the stolen IAM credentials.
Caption: Discovering the private bucket and its sensitive contents via the stolen IAM credentials.

Finally, I downloaded the sensitive file directly to my terminal, completing the simulation of the 106-million-record breach.

4. The Fix: Defense-in-Depth with IMDSv2

To stop this, I implemented the industry-standard fix: IMDSv2.

IMDSv2 requires a session token obtained via a PUT request before it will provide metadata. Since SSRF attacks are typically restricted to GET requests, the attacker can no longer "reach" the keys.

The Remediation Command:

Bash

aws ec2 modify-instance-metadata-options \
  --instance-id i-06fa60b8ad2545ac7 \
  --http-tokens required

After enforcing this, my attack attempts returned a 401 Unauthorized error. The application code was still "broken," but the infrastructure was now secure.

5. Key Lessons

  • Infrastructure is your Safety Net: Applications will always have bugs; IMDSv2 provides a critical layer of defense that prevents a bug from becoming a breach.
  • Least Privilege: If the IAM role didn't have S3 permissions, the stolen keys would have been useless.
  • Framework Thinking: Security is about auditing every layer of the Defense-in-Depth model, from the network to the data itself.