Fundamentals

T1525

S3 Exposure

S3 exposure happens when bucket permissions or object policies make data accessible beyond the intended audience — how it happens, how to detect it in CloudTrail, and how to respond when you find exposed data.

View on Graph

What It Is and Why It Matters

S3 exposure is one of the most common and damaging cloud security failures because S3 is widely used to store everything from application assets to customer data to internal documents. The total volume of breached records from exposed S3 buckets across public disclosures exceeds 100 million.

The risk comes from the complex interaction between multiple permission layers — bucket policies, IAM user policies, ACLs, and account-level public access blocks — where a single misconfigured setting can make an entire bucket readable by anyone on the internet.

Even when a bucket itself is private, data can still be exposed if an IAM role with read access is compromised or if pre-signed URLs are generated without expiration.

For analysts, S3 exposure incidents demand rapid assessment of what data was accessible, who may have accessed it, and whether any objects were downloaded.

How Public Access Happens — The Four Permission Layers

S3 has four independent permission layers, and a bucket is publicly accessible if any one of them allows public access:

LayerWhat It ControlsTypical Misconfiguration
Bucket PolicyJSON policy document attached to the bucket"Principal": "*" with "Effect": "Allow" on "s3:GetObject" — allows anyone on the internet to read objects.
Bucket ACLLegacy access control list (pre-2023 default)AllUsers or AuthenticatedUsers group granted READ access.
Object ACLPer-object ACL overridesAn individual object set to public even when the bucket is private.
Account-level Public Access BlockAccount-wide setting that blocks new public policiesThis setting is disabled by default. If not enabled, any of the above layers can make data public.

The most dangerous scenario: an account-level public access block with the setting “Block public and cross-account access if bucket has public policies” set to false. This allows bucket policies to override everything.

Detection — Finding Exposed S3 Buckets

AWS CloudTrail Events

CloudTrail records every S3 API call. The following events are critical for S3 exposure detection:

Event NameWhat It IndicatesSuspicious Patterns
PutBucketPolicyBucket policy modifiedAn S3 bucket that previously had no policy suddenly gets one with "Principal": "*".
PutBucketAclBucket ACL modifiedAllUsers group added with READ access.
PutObjectAclObject ACL modifiedIndividual objects set to public — a pattern suggesting exfiltration staging.
GetObjectObject readMultiple GetObject calls from unfamiliar IPs or across many objects (bulk download).
ListBucketBucket listingAn attacker listing bucket contents to find valuable objects.
DeleteBucketPolicyBucket policy removedSuspicious if done right after exfiltration — attacker covering tracks.
PutPublicAccessBlockPublic access block enabled or disabledDisabling the public access block before making a bucket public is a clear compromise signal.

CloudTrail KQL Query — Detect Buckets Made Public

// Find when a bucket was made publicly accessible
CloudTrail
| where TimeGenerated > ago(7d)
| where EventName in ("PutBucketPolicy", "PutBucketAcl", "PutObjectAcl")
| extend BucketName = parse_json(RequestParameters).bucketName
| extend PolicyStatement = parse_json(RequestParameters).bucketPolicy
| extend Grantee = parse_json(RequestParameters).AccessControlPolicy.AccessControlList.Grant[*].Grantee
| extend Principal = PolicyStatement.Statement[0].Principal
| extend Effect = PolicyStatement.Statement[0].Effect
| extend Action = PolicyStatement.Statement[0].Action
| where Principal == "*" or Grantee.URI contains "global/AllUsers" or Grantee.URI contains "global/AuthenticatedUsers"
| project TimeGenerated, EventName, BucketName, UserIdentity.arn, SourceIPAddress, PolicyStatement

AWS Config Rules

AWS Config provides managed rules specifically for S3 exposure detection:

Rule NameWhat It Checks
s3-bucket-public-read-prohibitedChecks buckets are not publicly readable
s3-bucket-public-write-prohibitedChecks buckets are not publicly writable
s3-bucket-ssl-requests-onlyChecks bucket policies deny HTTP requests
s3-bucket-logging-enabledChecks server access logging is enabled
s3-bucket-server-side-encryption-enabledChecks SSE is enabled (KMS, AES-256)
s3-bucket-blacklisted-actions-prohibitedCustom: blocks specific actions like GetObject for * principals

Enable these rules in AWS Config and configure SNS notifications for NonCompliant evaluations.

S3 Server Access Logs

S3 can log every request to the bucket. Key fields for investigation:

FieldExampleWhat It Tells You
bucketacme-customer-dataWhich bucket was accessed
keycustomers/prod/export-2025-01.csvWhich object was accessed
remoteip203.0.113.5Who accessed it (correlate to org IP ranges)
requesterarn:aws:iam::123456789012:user/attacker-access-keyWhich IAM identity accessed the bucket
operationREST.GET.OBJECTRead, write, or list operation
http_status200Successful access (exfiltration)
error_codeAccessDeniedBlocked access attempt
bytes_sent8589934592Object size — large values confirm exfiltration
user_agentaws-cli/2.xCLI access is more suspicious than browser access.

Third-Party Monitoring

  • Trusted Advisor (AWS) — S3 Bucket Permissions check in the Security category.
  • Security Hub — Aggregates Config findings, GuardDuty, and Inspector into a single dashboard with CIS benchmarks for S3.
  • CloudSploit / ScoutSuite — Open-source auditing tools that scan AWS accounts for public S3 buckets.
  • GrayHatWarfare — Public bucket search engine (buckets.grayhatwarfare.com). Search your org’s naming patterns to see if your buckets appear.

What To Do When You Find Exposed Data

If you discover a publicly accessible S3 bucket, follow this sequence:

  1. Immediately block public access — Set the account-level public access block to block all public policies and ACLs. This is the fastest way to cut access without modifying individual bucket policies.
  2. Identify the data — Run aws s3 ls s3://bucket-name --recursive (using an internal role) to enumerate objects. Create a manifest of file types, sizes, and date ranges.
  3. Check CloudTrail — Query GetObject events for the bucket. Identify which IPs accessed which objects and when.
  4. Notify stakeholders — Legal, compliance, and engineering teams need to know. The bucket owner may need to issue a data breach notification depending on data classification.
  5. Rotate credentials — If IAM keys were in the exposed bucket, rotate them immediately. API keys, database passwords, and TLS private keys are high-value targets.
  6. Root cause analysis — Why was the bucket exposed? Misconfigured IaC template? Manual override? Missing public access block? Fix the process, not just the bucket.

Real-World Examples

  • Dow Jones watchlist exposure (2017) — An exposed S3 bucket contained the names, addresses, and financial account details of over two million high-risk individuals and politically exposed persons from Dow Jones watchlist data.
  • Verizon NICE Systems leak (2017) — A third-party vendor misconfigured an S3 bucket containing Verizon customer data including names, phone numbers, and account PINs for over 6 million subscribers.
  • Accenture cloud exposure (2017) — Four unsecured S3 buckets exposed internal Accenture data including API access keys, authentication credentials, and customer decryption keys.
  • US Army data leak (2023) — An exposed S3 bucket belonging to a contractor contained US Army intelligence data, accessible to anyone with the bucket URL.

Prevention — S3 Security Checklist

ItemImplementation
Enable account-level public access blockAWS Organizations: apply an SCP that denies s3:PutBucketPolicy and s3:PutBucketAcl on buckets that don’t have the account-level block.
Deny HTTP accessBucket policy: "Condition": {"Bool": {"aws:SecureTransport": "false"}}"Effect": "Deny".
Enable S3 server access loggingSend logs to a separate logging bucket in a different account with write-only access.
Enable CloudTrail Data Events for S3Capture GetObject, ListBucket, PutObject for all buckets (additional cost, but worth it for sensitive data buckets).
Use bucket policies over ACLsACLs are harder to audit. Use resource-based policies with IAM conditions and Principal restrictions.
Configure pre-signed URL expirationPre-signed URLs should expire in minutes, not days or never.
Automate compliance scanningUse AWS Config with managed S3 rules. Send non-compliance alerts to a security Slack channel.

Sources