Browse CTFs New CTF Sign in

S3 Bucket Policy Misconfiguration: Public Access, Cross-Account Grants and Transport Gaps

log_analysis_siem Difficulty 1–5 30 min certifiable

Theory

Why This Matters

Misconfigured S3 buckets are among the most frequently reported cloud data exposure incidents. The 2019 Capital One breach, the 2017 Verizon data exposure (Nice Systems), the 2018 FedEx customer data leak, and dozens of smaller incidents catalogued by researchers like Bob Diachenko and Troy Hunt all trace to S3 bucket policies or ACLs that permitted public or overly broad access. AWS introduced S3 Block Public Access in 2018 precisely because bucket policy misconfigurations were so epidemic — but Block Public Access must be explicitly enabled, and many legacy buckets predate the feature or had it disabled to accommodate a perceived application requirement. Understanding every layer of S3 access control is essential for any cloud security assessment.

Core Concept

S3 access control has multiple overlapping layers that must all be understood to determine effective access:

Bucket Policy is a resource-based IAM policy attached directly to the bucket. It can grant access to any AWS principal (including "Principal": "*" for anonymous public access), to other AWS accounts, or to AWS services. It can also deny access regardless of IAM identity policies.

Bucket ACL is a legacy access control mechanism predating IAM policies. AWS recommends disabling ACLs in favour of bucket policies. A bucket ACL that grants READ to the predefined AllUsers group makes all objects publicly readable regardless of the bucket policy.

S3 Block Public Access is a set of four account-level and bucket-level settings that override policies and ACLs to prevent public access. The settings are: BlockPublicAcls, IgnorePublicAcls, BlockPublicPolicy, and RestrictPublicBuckets. All four should be enabled on buckets not intentionally serving public content.

Object ownership controls whether uploaded objects can have ACLs at all. Setting BucketOwnerEnforced disables ACLs entirely and ensures the bucket owner controls all objects — this prevents the scenario where a cross-account upload grants the uploader's account read access via object ACL.

Critical misconfiguration flags: "Principal": "*" without a restrictive Condition means the action is available to the entire internet. Absence of "aws:SecureTransport": "true" in a Deny statement means the bucket allows plaintext HTTP access, enabling network interception of credentials and data in transit.

Technical Deep-Dive

# List all S3 buckets in the account
aws s3api list-buckets --query 'Buckets[*].{Name:Name,Created:CreationDate}' --output table

# Check Block Public Access settings for a specific bucket
aws s3api get-public-access-block --bucket target-bucket-name

# Retrieve the bucket policy and check for wildcard principals
aws s3api get-bucket-policy --bucket target-bucket-name 
  --query 'Policy' --output text | python3 -m json.tool

# Check for Principal: * (public access) without conditions
aws s3api get-bucket-policy --bucket target-bucket-name 
  --query 'Policy' --output text | 
  python3 -c "
import sys, json
policy = json.load(sys.stdin)
for stmt in policy['Statement']:
    principal = stmt.get('Principal', {})
    is_wildcard = principal == '*' or principal.get('AWS') == '*'
    has_condition = bool(stmt.get('Condition'))
    if is_wildcard:
        print(f'[{stmt['Effect']}] Action={stmt['Action']} Principal=* Condition={has_condition}')
"

# Check bucket ACL for public grants
aws s3api get-bucket-acl --bucket target-bucket-name 
  --query 'Grants[?Grantee.URI!=null].{Grantee:Grantee.URI,Permission:Permission}'

# Check for missing SecureTransport enforcement
aws s3api get-bucket-policy --bucket target-bucket-name 
  --query 'Policy' --output text | grep -c SecureTransport

# Check object ownership settings
aws s3api get-bucket-ownership-controls --bucket target-bucket-name

# Attempt anonymous access to the bucket (no credentials)
aws s3 ls s3://target-bucket-name/ --no-sign-request

# Audit all buckets for Block Public Access disabled (account-level check)
aws s3control get-public-access-block 
  --account-id $(aws sts get-caller-identity --query Account --output text)
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyInsecureTransport",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::my-bucket",
        "arn:aws:s3:::my-bucket/*"
      ],
      "Condition": {
        "Bool": {"aws:SecureTransport": "false"}
      }
    }
  ]
}

Security Assessment Methodology

  1. Enumerate all buckets and check account-level Block Public Access. A disabled account-level Block Public Access setting means individual bucket settings must be inspected. An enabled account-level setting provides a safety net but does not prevent all misconfiguration.
  2. Check each bucket's Block Public Access settings. Use get-public-access-block per bucket. Any bucket with any of the four settings disabled requires deeper inspection of its policy and ACL.
  3. Parse bucket policies for wildcard principals. "Principal": "*" with "Effect": "Allow" and no restrictive Condition is public access. Even with a Condition, verify the condition is meaningful — "IpAddress": {"aws:SourceIp": "0.0.0.0/0"} is not a restriction.
  4. Check ACLs for public grants. Use get-bucket-acl and inspect for grants to http://acs.amazonaws.com/groups/global/AllUsers or AuthenticatedUsers. The latter grants access to any AWS account, not just your organization.
  5. Verify SecureTransport enforcement. Check for a Deny statement with "aws:SecureTransport": "false". Its absence means the bucket accepts HTTP requests, enabling plaintext credential and data interception.
  6. Check cross-account access. Bucket policies that allow principals from other account IDs should be justified by a documented business requirement. Use aws organizations list-accounts to verify cross-account principals are within the organization.
  7. Remediate by enabling all four Block Public Access settings at the account level, adding the SecureTransport Deny statement to all bucket policies, setting object ownership to BucketOwnerEnforced, and reviewing and revoking all ACL grants.

Common Assessment Errors

  • Treating Block Public Access as sufficient. Block Public Access prevents public ACL and policy grants but does not prevent cross-account access by specific AWS principals. A bucket policy granting access to arn:aws:iam::ATTACKER_ACCOUNT:root bypasses Block Public Access completely.
  • Missing the AllAuthenticatedUsers ACL grant. The AuthenticatedUsers predefined group means any authenticated AWS user — approximately 300 million accounts — not just users in your organization. Many assessors see it and assume it means "authenticated to our company."
  • Not testing anonymous access. Always attempt aws s3 ls s3://bucket --no-sign-request. API responses confirming "access denied" are different from actually testing whether unauthenticated access works in practice.
  • Ignoring object-level policies. S3 supports per-object ACLs. Even if the bucket policy is correct, individual objects may have been uploaded with permissive ACLs. Spot-check object ACLs on sensitive objects with aws s3api get-object-acl.
  • Overlooking the account-level vs bucket-level distinction. Account-level Block Public Access settings are separate from bucket-level settings. An account-level block does not override a bucket-level setting that explicitly disables the block — the bucket-level setting takes precedence where it is more permissive.

NICE Framework Alignment

Code Knowledge/Skill/Task Statement How This Card Develops It
K0053 Knowledge of security risk management processes Evaluating layered S3 access control risk: the interaction of bucket policies, ACLs, Block Public Access, and object ownership creates complex effective-access scenarios
K0167 Knowledge of system administration, network, and OS hardening techniques Hardening S3 buckets: enabling Block Public Access, enforcing SecureTransport, disabling ACLs, and scoping bucket policies to specific principals and conditions
S0073 Skill in conducting vulnerability scans and recognizing vulnerabilities Using get-bucket-policy, get-bucket-acl, and get-public-access-block to systematically audit S3 access controls across large account inventories
T0144 Conduct penetration testing as required for new or updated applications Testing anonymous and cross-account S3 access during AWS penetration tests to confirm public or over-broad bucket exposures
T0395 Write code to address security vulnerabilities Writing correct S3 bucket policies with SecureTransport Deny statements, least-privilege principal scoping, and condition-key restrictions

Further Reading

  • AWS S3 Security Best Practices — AWS Documentation (docs.aws.amazon.com/AmazonS3/latest/userguide/security-best-practices.html)
  • flaws.cloud — S3 Security Training — Scott Piper, interactive S3 misconfiguration training (flaws.cloud)
  • Cloud Security Alliance: Top Threats to Cloud Computing — Cloud Security Alliance, Storage Misconfiguration section (cloudsecurityalliance.org)

Challenge Lab

Reinforce your learning with a hands-on generated challenge based on this card's competency.