Time: ~12 hours · Difficulty: Advanced · Stack: AWS · Terraform · WAF · IAM · GuardDuty
Recreating a real breach end-to-end is the single most compelling portfolio piece in cloud security. The 2019 Capital One breach is the canonical AWS choice — the kill chain is well-documented, every step maps to a control you can demonstrate, and most cloud security engineers can pattern-match on it instantly.
The artifact is a public repo that, given an AWS account, will deploy the vulnerable setup, walk you through the exploit, then deploy the fixes — all via Terraform. Hiring managers will remember this one.
📖 On this page
What you'll have at the end
- A Terraform module that deploys the vulnerable architecture (a server with SSRF, IMDSv1 enabled, an over-privileged IAM role, S3 buckets accessible from that role).
- A walkthrough of the exploit: SSRF → IMDSv1 → temporary creds → S3 list / get.
- A Terraform module that deploys the fixed architecture (IMDSv2 only, scoped IAM role, WAF SSRF rule, S3 access logging, GuardDuty enabled).
- GuardDuty findings + CloudTrail evidence showing the original attack would now be detected.
- A write-up mapping each fix to the original breach root cause.
Prerequisites
- Strong comfort with Terraform and AWS. This is an advanced project.
- Read the CSOH Capital One kill chain and the original Krebs / DOJ write-ups first.
- Home lab guardrails — this project spins up a NAT gateway and an EC2 instance; tear them down at end of session.
- An ethical reminder: this is for self-education in your own lab account. Don't replicate against anything you don't own.
Step-by-step
1. Re-read the breach in detail
Read the CSOH kill chain, the DOJ indictment, and the AWS official statement. Make a four-step diagram on paper: ingress → SSRF → IMDS → S3 exfil.
2. Terraform the vulnerable architecture
Deploy: VPC with public subnet, EC2 instance running a deliberately-SSRF-vulnerable web app (a one-line Python Flask app that does requests.get(request.args['url'])), the EC2 instance launched with http_tokens = optional (IMDSv1 allowed), an instance profile with overly-broad S3 read/list across multiple buckets, and three S3 buckets with sample data.
Important: this code is intentionally vulnerable. Comment that loudly in the repo's top-level README, and add an automatic teardown script.
3. Execute the exploit
From your laptop, hit the SSRF endpoint to fetch http://169.254.169.254/latest/meta-data/iam/security-credentials/<role-name>. You should get back temporary credentials. Use aws configure with those creds, then aws s3 ls across the buckets. Screenshot every step.
4. Capture the CloudTrail of your own attack
Pull the CloudTrail events your exploit generated. Note: the IMDS call itself doesn't show in CloudTrail — only the subsequent sts:AssumeRole (if any) and the S3 calls do. This is itself a teaching moment about the limits of AWS detection.
5. Now build the fixes — one at a time
Walk through the Capital One root causes and Terraform a fix per cause:
- IMDSv2 enforcement via
http_tokens = requiredat the launch template, plus an SCP that denies launching with v1. - Scoped IAM role — read access on one bucket, no list-buckets, no cross-account.
- WAF rule blocking requests targeting
169.254.169.254(or other internal IPs). - Egress restrictions — VPC endpoint for S3 + NAT egress restricted to known destinations.
- S3 access logging + bucket policies preventing exfiltration.
- GuardDuty enabled with the relevant findings turned on.
6. Re-run the exploit against the fixed architecture
Try the SSRF again. With IMDSv2 enforced, the simple SSRF can't get credentials (it needs to PUT first to get a session token). With the WAF rule, the request is blocked at the edge. Document each "now blocked" outcome with a screenshot.
7. Validate detection
Use Stratus Red Team or your own CloudTrail queries to confirm GuardDuty + CloudTrail surface the original attack pattern when run against the fixed architecture. Map findings to MITRE ATT&CK Cloud.
8. Write the kill chain in your own voice
Use our kill chain template as inspiration but write it as your own analysis, with your own diagrams, your own commands, and the lessons you took from doing it hands-on.
9. Tear down (immediately)
This lab leaves nothing useful running. terraform destroy the moment the write-up is recorded.
What hiring managers look for
- You actually did the exploit, not just described it. The screenshots show CLI output, not theory.
- Your fixes are technically specific and Terraformed — not generic "apply least privilege."
- You map each fix to the original root cause; this proves you understood the breach, not just memorized it.
- You demonstrate awareness that defense in depth means multiple controls — IMDSv2 + scoped IAM + WAF + egress, not just one.
- You speak to what GuardDuty would and wouldn't catch — shows mature understanding of detection limits.
Common mistakes
- Doing this in a non-lab account. Never deploy intentionally vulnerable code to a production-shaped environment.
- Forgetting the SCP layer. Per-account fixes are fragile; org-level guardrails are the durable answer.
- Skipping the detection validation. Recreating the breach without proving you'd catch it next time misses the point.
- Not tearing down. NAT gateways are silent killers.
- Glossing over IMDSv2's design (the session-token requirement is what makes it SSRF-resistant; explain it in the write-up).
Where to publish
The full publishing playbook is on the portfolio hub page. The short version: a public GitHub repo with a thorough README is the strongest single signal; pair it with a LinkedIn post and (optionally) a 5-minute lightning talk at a CSOH Friday Zoom.
Where next
- All 7 portfolio projects — pick your next one.
- Home lab setup — the safe environment this project runs in.
- Careers guide — how this project fits into the hiring story.
- Friday Zoom + Signal chat — share your write-up with practitioners.
