flAWS: Six Levels of AWS Misconfiguration
At a glance
| Level | Flaw I exploited | Fix |
|---|---|---|
| 1 | S3 bucket listing open to everyone | Block public listing, review the bucket policy |
| 2 | Bucket readable by any authenticated AWS user | Grant access to named principals only |
| 3 | AWS keys left in a public bucket’s git history | Revoke and rotate the keys; never commit secrets |
| 4 | Public EBS snapshot containing credentials | Keep snapshots private and encrypted; no secrets in scripts |
| 5 | SSRF to the EC2 metadata service (169.254.169.254) | Block internal ranges, enforce IMDSv2, least-privilege roles |
| 6 | Read-only audit access that exposed an API Gateway and Lambda | Hand out read access sparingly, even for metadata |
flaws.cloud is a deliberately vulnerable training site. I did this by following my own notes and screenshots; credentials, account IDs and my own IP are left out on purpose.
Level 1: public bucket listing
dig and reverse nslookup on the site’s IPs showed it is an S3 website in us-west-2. Listing the bucket needed no account:
aws s3 ls s3://flaws.cloud/ --no-sign-request --region us-west-2
That exposed every file, including a secret page. Lesson: buckets are private by default, so a public listing is a permission someone added by mistake.
Level 2: “any authenticated user” is not “my users”
Same idea, but the bucket allowed any AWS-authenticated principal. I created my own AWS account, configured a CLI profile and listed the bucket with it. Lesson: “authenticated users” means anyone on the internet with an AWS account. Name the specific principals.
Level 3: keys in git history
The bucket contained a .git folder. I synced it down with aws s3 sync --no-sign-request, ran git log and found two commits, the newer one titled as an accident. Checking out the older commit revealed access_keys.txt, deleted in the later commit but still in history. I configured a profile with those keys, and aws s3 ls listed every bucket in that account, because a profile can’t be limited to certain buckets. Lesson: deleting a leaked secret doesn’t remove it. Revoke it, rotate it, and “roll early, roll often”.
Level 4: a public EBS snapshot
aws sts get-caller-identity gave the account that owned the leaked keys. aws ec2 describe-snapshots --owner-id <id> found a snapshot of a web server that was shared publicly.
- Created a volume from the snapshot in my own account (
us-west-2a). - Launched an Ubuntu instance, then hit an error: the instance landed in
us-west-2band the volume in2a, and EBS volumes attach only within the same availability zone. Setting the subnet explicitly fixed it. - Attached the volume (
aws ec2 attach-volume), connected over SSH, usedlsblkto find the device and mounted it. - A setup script in the home directory held the basic-auth credentials for the next level.
Lesson: snapshots can quietly hold secrets. Keep them private and encrypted, and keep credentials out of setup scripts.
Level 5: SSRF to the metadata service
The target exposed an HTTP proxy that would fetch any URL. Pointing it at 169.254.169.254 returned the instance metadata. I walked to meta-data/iam/security-credentials/, which returned temporary credentials (access key, secret and session token). With those in a CLI profile I could list the next level’s bucket and find a hidden directory. Lesson: an SSRF in a cloud workload can become an IAM compromise. Block internal and link-local ranges, enforce IMDSv2 and limit what the instance role can do.
Level 6: what read-only access reveals
Given an audit-level key, I mapped what it could see:
iam get-userandlist-attached-user-policiesshowed two policies: AWS’sSecurityAuditand a customlist_apigateways.iam get-policyandget-policy-versionshowed the custom one allowsapigateway:GETon REST APIs.lambda list-functionsfound a function namedLevel6, andlambda get-policyrevealed the REST API ID that can invoke it.apigateway get-stagesreturned the stage name (Prod), which gave me the invoke URL. Calling it returned the final link.
Lesson: read-only isn’t harmless. The ability to read policies tells an attacker what exists and where to look.
What I take from it
- Most of these were permission mistakes rather than software bugs.
- Every level chained: one small exposure produced the credential for the next.
- A defender’s checklist falls out of it: no public listings, named principals, secrets never in repos, snapshots private, IMDSv2 and least privilege everywhere.
Limits
Training environment, guided by the level hints, and cloud-offence only. I haven’t yet looked at how each step would appear in CloudTrail logs. That is the natural next step, and it connects this to my SIEM lab.