ethicalhacker.ro logoethicalhacker.ro
← Blog

Abusing Misconfigured S3 Bucket Policies

2024-10CloudAWSS3Pentest

S3 bucket misconfigurations are still among the most impactful cloud findings. This post walks through the most common policy mistakes, how attackers chain them, and what a correct policy looks like.


Abusing Misconfigured S3 Bucket Policies

S3 bucket exposure is far from a new vulnerability class, yet it continues to appear in cloud pentests as a high or critical finding. The reason is not ignorance — it is the gap between what developers believe their bucket policy says and what AWS actually enforces.

The Three Misconfig Patterns

1. Public Read with No Block Public Access

The classic case. A bucket has "Principal": "*" with s3:GetObject. Combined with public ACLs not being blocked at the account or bucket level, every object is world-readable.

``json
{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::company-backups/*"
}
`

Production database dumps, .env files, and private keys appear regularly in pentests this way.

2. Overly Broad Principal with Account Trust

A bucket meant to be private to one service ends up trusting the entire AWS account:

`json
{
"Principal": { "AWS": "arn:aws:iam::123456789012:root" }
}
`

Any IAM entity in that account — including compromised developer credentials or a container with an over-scoped role — can read or write the bucket. Lateral movement from a low-privilege initial access becomes trivial.

3. Confused Deputy via Public Website Hosting

Static website hosting exposes a bucket endpoint (bucket.s3-website-region.amazonaws.com) without respecting private ACLs for objects referenced by the site. Sensitive files included via relative paths become accessible even without S3 API access.

Attack Chain in Practice

During a recent engagement, we escalated from an unauthenticated external position to exfiltrating customer PII in three steps:

1. Recon: aws s3 ls s3://target-exports/ --no-sign-request — bucket listed world-readable
2. Discovery: Found timestamped CSV exports of user records
3. Exfiltration: Synced 14 GB of data locally:
aws s3 sync s3://target-exports/ ./loot/ --no-sign-request

Total time: under 10 minutes. No authentication required.

What Correct Looks Like

`json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::company-backups",
"arn:aws:s3:::company-backups/*"
],
"Condition": {
"Bool": { "aws:SecureTransport": "false" }
}
},
{
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::123456789012:role/BackupWriter" },
"Action": ["s3:PutObject"],
"Resource": "arn:aws:s3:::company-backups/*"
}
]
}
`

Pair this with Block Public Access enabled at the account level, server-side encryption, and CloudTrail logging on the bucket.

Testing Your Own Buckets

`bash

Unauthenticated listing


aws s3 ls s3://your-bucket --no-sign-request

Check public access block


aws s3api get-public-access-block --bucket your-bucket

List effective policy


aws s3api get-bucket-policy --bucket your-bucket | jq .
``

If the first command returns results, you have a misconfiguration.

More ArticlesRequest a Pentest
Abusing Misconfigured S3 Bucket Policies | ethicalhacker.ro