It was a Tuesday afternoon. I was doing routine OSINT recon on a client engagement — mapping their external attack surface before we kicked off the formal pentest. Nothing fancy. Just Shodan, some Google dorks, checking for exposed assets the client might not know about.

Forty minutes in, I found a publicly accessible S3 bucket. Half a million customer records. Unencrypted. No authentication required.

This is the story of how that happens, why it keeps happening, and what you can do to make sure it’s not your bucket.

How I Found It

The recon chain was straightforward:

  1. Enumerate subdomains using amass enum -passive -d target.com plus subfinder
  2. Notice a subdomain like assets-backup.target.com resolving to an S3 endpoint
  3. Run aws s3 ls s3://assets-backup-target --no-sign-request
  4. Watch 500,000 rows of customer data appear in the terminal

That --no-sign-request flag is the key. It means you’re accessing the bucket with no AWS credentials at all — as an anonymous internet user. If the bucket ACL allows public read, everything inside is accessible to anyone who knows the URL.

What Was In the Bucket

I stopped enumerating the moment I confirmed the exposure. Per our responsible disclosure policy and the rules of engagement, I immediately notified the client’s security team. The bucket contained customer name, email, phone, partial payment data, internal order IDs, and a folder called /backups/db_exports/ containing database dumps with password hashes.

Why This Keeps Happening

I’ve found exposed S3 buckets on three separate engagements in the last 18 months. The root cause is almost always the same: a developer created a bucket, toggled “public” during testing to debug an issue, and never toggled it back. The real issue is misconfiguration drift — cloud infrastructure changes faster than security reviews happen.

How to Find Your Own Exposed Buckets

Run this yourself:

for bucket in $(aws s3api list-buckets --query "Buckets[].Name" --output text); do
  acl=$(aws s3api get-bucket-acl --bucket $bucket 2>/dev/null)
  if echo "$acl" | grep -q "AllUsers"; then
    echo "PUBLIC: $bucket"
  fi
done

Also check: aws s3api get-bucket-policy-status --bucket BUCKETNAME. If IsPublic is true, you have a problem.

The Fix

Enable “Block Public Access” at the account level, rotate any credentials that may have been in those files, and notify affected customers. Set up AWS Security Hub with the CIS benchmark enabled — it will flag public buckets automatically. The bucket had been public for at least 14 months based on file timestamps. In that time, we have no way of knowing who else found it.