Skip to content
Eddie Mwangi

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.

  1. Created a volume from the snapshot in my own account (us-west-2a).
  2. Launched an Ubuntu instance, then hit an error: the instance landed in us-west-2b and the volume in 2a, and EBS volumes attach only within the same availability zone. Setting the subnet explicitly fixed it.
  3. Attached the volume (aws ec2 attach-volume), connected over SSH, used lsblk to find the device and mounted it.
  4. 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:

  1. iam get-user and list-attached-user-policies showed two policies: AWS’s SecurityAudit and a custom list_apigateways.
  2. iam get-policy and get-policy-version showed the custom one allows apigateway:GET on REST APIs.
  3. lambda list-functions found a function named Level6, and lambda get-policy revealed the REST API ID that can invoke it.
  4. apigateway get-stages returned 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.