Help Ukraine, click for information

> AWS Misconfigurations Attackers Actually Exploit_

Cloud breaches look different from traditional ones. There's no lateral movement through workstations — instead, an exposed key in a GitHub commit leads to full account takeover in minutes. An EC2 instance with default metadata access lets a web server become a credential theft vector. An S3 bucket with a world-readable ACL leaks your entire customer database.

These aren't theoretical. They're the actual patterns behind real breaches — Capital One, Toyota, LastPass. This post covers the most common, how to find them, and how to fix them.


## 1. S3 bucket misconfiguration

The classic. An S3 bucket with public read access exposes everything inside.

Finding public buckets:

terminal
# Try accessing the bucket directly curl https://target-bucket.s3.amazonaws.com/ # List contents if ListBucket is allowed aws s3 ls s3://target-bucket/ --no-sign-request # Find buckets via certificate transparency, GitHub, JS files # Tools: # - bucket_finder (bruteforce bucket names) # - lazys3 (common name permutations) # - S3Scanner # Check your own buckets for public access aws s3api get-bucket-acl --bucket my-bucket aws s3api get-bucket-policy --bucket my-bucket # List all buckets and check each aws s3 ls for bucket in $(aws s3 ls | awk '{print $3}'); do echo "=== $bucket ===" aws s3api get-bucket-public-access-block --bucket "$bucket" 2>/dev/null || echo "No public access block" done

What attackers look for inside buckets:

  • >.env files, config files with credentials
  • >Database backups (.sql, .dump, .bak)
  • >CloudFormation templates (often contain hardcoded values)
  • >Private keys, certificates
  • >PII (customer data, exports)

Fix:

terminal
# Enable S3 Block Public Access at the account level aws s3control put-public-access-block \ --account-id 123456789012 \ --public-access-block-configuration "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"

## 2. IMDSv1 and SSRF → credential theft

EC2 instances have a metadata service at http://169.254.169.254. It provides temporary IAM credentials for the instance role. By default (IMDSv1), any process on the instance — or any SSRF vulnerability in an app running on it — can read these credentials.

From an SSRF vulnerability:

terminal
GET /fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/ → Returns role name, e.g.: "ec2-prod-role" GET /fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/ec2-prod-role → Returns: { "AccessKeyId": "ASIA...", "SecretAccessKey": "...", "Token": "...", "Expiration": "2025-11-25T12:00:00Z" }

These credentials have whatever permissions the instance's IAM role has. If the role has AdministratorAccess — you own the account.

Use the stolen credentials:

terminal
export AWS_ACCESS_KEY_ID="ASIA..." export AWS_SECRET_ACCESS_KEY="..." export AWS_SESSION_TOKEN="..." aws sts get-caller-identity # confirm who you are aws s3 ls # list all buckets aws iam list-roles # enumerate IAM roles

Fix: Enforce IMDSv2, which requires a token exchange that HTTP redirect-based SSRF can't complete:

terminal
# Require IMDSv2 on an existing instance aws ec2 modify-instance-metadata-options \ --instance-id i-0abcdef1234567890 \ --http-tokens required \ --http-put-response-hop-limit 1 # Set default to IMDSv2 for all new instances in the account aws ec2 modify-instance-metadata-defaults \ --http-tokens required

## 3. Over-permissive IAM policies

### The * wildcard problem

A common pattern in IAM policies:

terminal
{ "Effect": "Allow", "Action": "*", "Resource": "*" }

That's AdministratorAccess. It appears in service accounts, Lambda execution roles, developer IAM users — often because it was "temporary" and never cleaned up.

Find over-permissive roles:

terminal
# List all policies attached to all roles aws iam list-roles --query 'Roles[*].RoleName' --output text | tr '\t' '\n' | while read role; do echo "=== $role ===" aws iam list-attached-role-policies --role-name "$role" \ --query 'AttachedPolicies[*].PolicyName' --output text done # Find roles with AdministratorAccess aws iam list-entities-for-policy \ --policy-arn arn:aws:iam::aws:policy/AdministratorAccess # Use tools: Cloudsploit, Prowler, PMapper (finds privilege escalation paths)

### IAM privilege escalation paths

PMapper maps trust relationships between IAM entities and finds if a low-privilege user can escalate to admin. Common paths:

  • >iam:CreatePolicyVersion — create a new version of any managed policy with * permissions
  • >iam:AttachRolePolicy — attach AdministratorAccess to a role you can assume
  • >iam:PassRole + ec2:RunInstances — launch an EC2 with an admin role, use IMDS to steal creds
  • >lambda:CreateFunction + iam:PassRole + lambda:InvokeFunction — create a Lambda with admin role, invoke it to exfil credentials
terminal
# Run PMapper to find privilege escalation paths pip install principalmapper pmapper --profile default graph create pmapper --profile default analysis --find-risks

## 4. Exposed access keys in code

The most common entry point into an AWS account.

terminal
# Search GitHub for leaked keys # (GitHub secret scanning catches most, but historical commits aren't always cleaned) # Scan your own repos git log -p | grep -E "(AKIA|ASIA)[A-Z0-9]{16}" git log -p | grep -i "aws_secret" trufflehog git file:///path/to/repo gitleaks detect --source . --verbose # Validate a key aws sts get-caller-identity --profile leaked-key-profile

What to do when you find a leaked key:

terminal
# Immediately deactivate aws iam update-access-key --access-key-id AKIAIOSFODNN7EXAMPLE --status Inactive # Check what it did (CloudTrail) aws cloudtrail lookup-events \ --lookup-attributes AttributeKey=AccessKeyId,AttributeValue=AKIAIOSFODNN7EXAMPLE \ --start-time 2025-01-01 \ --max-results 50 # Create a new key, rotate all systems using the old one, then delete aws iam delete-access-key --access-key-id AKIAIOSFODNN7EXAMPLE

Prevention: Use IAM roles wherever possible. For CI/CD, use OIDC federation (GitHub Actions → AssumeRoleWithWebIdentity) instead of long-term keys. Enable AWS Secrets Manager / Parameter Store for secrets at rest.


## 5. Unrestricted security groups

Security groups act as firewall rules. A common misconfiguration:

terminal
{ "IpProtocol": "-1", "IpRanges": [{"CidrIp": "0.0.0.0/0"}] }

That's all traffic, all ports, from anywhere. Often created for "quick debugging" and left open.

terminal
# Find security groups with 0.0.0.0/0 inbound aws ec2 describe-security-groups \ --filters "Name=ip-permission.cidr,Values=0.0.0.0/0" \ --query 'SecurityGroups[*].{ID:GroupId,Name:GroupName,Rules:IpPermissions}' \ --output json # Find which instances use them aws ec2 describe-instances \ --filters "Name=network-interface.group-id,Values=sg-xxxxxxxx" \ --query 'Reservations[*].Instances[*].{ID:InstanceId,IP:PublicIpAddress}'

Particularly dangerous: port 22 (SSH), port 3389 (RDP), port 27017 (MongoDB), port 6379 (Redis) exposed to 0.0.0.0/0.


## 6. CloudTrail disabled

CloudTrail is AWS's audit log. Without it, attackers can operate with minimal evidence.

terminal
# Check if CloudTrail is enabled aws cloudtrail get-trail-status --name your-trail-name aws cloudtrail describe-trails --include-shadow-trails false # Check for recent suspicious events aws cloudtrail lookup-events \ --start-time $(date -d '24 hours ago' +%Y-%m-%dT%H:%M:%S) \ --query 'Events[?EventName==`CreateAccessKey` || EventName==`AttachUserPolicy` || EventName==`PutUserPolicy`]'

Signs of account compromise in CloudTrail:

  • >ConsoleLogin from unusual geography
  • >CreateAccessKey for existing users
  • >AttachUserPolicy / AttachRolePolicy with admin policies
  • >Disabling GuardDuty or CloudTrail
  • >RunInstances with unknown AMIs or regions you don't use

## Automated scanning tools

ToolWhat it finds
Prowler250+ CIS benchmark checks across all AWS services
ScoutSuiteMulti-cloud security posture assessment
CloudSploitReal-time misconfiguration scanning
PacuAWS exploitation framework (red team)
PMapperIAM privilege escalation path analysis
TruffleHog / GitleaksSecret scanning in git history
S3ScannerPublic S3 bucket finder
terminal
# Prowler quick scan (uses your default AWS profile) pip install prowler prowler aws --profile default -f eu-west-1

## Priority hardening checklist

PriorityAction
1Enable S3 Block Public Access at the account level
2Enforce IMDSv2 on all EC2 instances
3Rotate or delete unused IAM access keys
4Enable MFA on all IAM users and especially root
5Enable CloudTrail in all regions, centralise to S3
6Enable GuardDuty
7Review IAM roles for * wildcards, apply least privilege
8Lock down security groups — no 0.0.0.0/0 on admin ports
9Enable AWS Config rules for continuous compliance
10Set up SCPs in AWS Organizations to prevent privilege misuse

Cloud security isn't fundamentally different from on-prem security — it's the same principles (least privilege, logging, network segmentation) applied to APIs instead of servers. The difference is the blast radius: a single leaked key can expose your entire infrastructure in minutes.

root@sovietghost:/blog/042-aws-misconfig# ls -la ../

> Thanks for visiting. Stay curious and stay secure. _