Enumerating AWS Cloud Infrastructure
External-to-internal AWS recon — domains, S3 buckets, and IAM users/roles/policies via the AWS CLI and Pacu.
Enumerating AWS Cloud Infrastructure
External-to-internal recon of an AWS estate: find the domains and S3 buckets, then pivot to the AWS API with any credentials you obtain and enumerate IAM users, groups, roles, and policies.
Lab DNS setup (OffSec lab only)
$cat /etc/resolv.conf$sudo nano /etc/resolv.conf$# Put the lab DNS first:$# nameserver 44.205.254.229$# nameserver 1.1.1.1$ $host www.offseclab.io 44.205.254.229$host www.offseclab.io$ $# NetworkManager overwrites resolv.conf — reset when done:$sudo systemctl restart NetworkManagerDomain & subdomain reconnaissance
$host -t ns offseclab.io # authoritative name servers$whois awsdns-00.com | grep "Registrant Organization"$host www.offseclab.io # public IP$host 52.70.117.69 # reverse lookup$whois 52.70.117.69 | grep "OrgName"$dnsenum offseclab.io --threads 100Discovering S3 buckets
Inspect the site's requests (DevTools → Network). Assets served from s3.amazonaws.com reveal a bucket name and object key, e.g. offseclab-assets-public-axevtewi/sites/www/images/saphire.jpg → bucket offseclab-assets-public-axevtewi.
$# Browse http://<bucket-name>.s3.amazonaws.com or http://s3.amazonaws.com/<bucket-name>$# NoSuchBucket -> doesn't exist$# AccessDenied -> exists but not publicly listable$# XML listing -> publicly listable$ $# Guess sibling buckets by swapping the environment word$# offseclab-assets-dev / -private / -prod ...$ $# Automate with cloud_enum / cloudbrute$sudo apt install cloud-enum cloudbrute$cloud_enum -k offseclab-assets-public-axevtewi --quickscan --disable-azure --disable-gcp$ $for key in "public" "private" "dev" "prod" "development" "production"; do echo "offseclab-assets-$key-axevtewi"; done | tee /tmp/keyfile.txt$cloud_enum -kf /tmp/keyfile.txt -qs --disable-azure --disable-gcpReconnaissance via the AWS API
Configure the CLI with any credentials you find, then confirm who you are.
$sudo apt install -y awscli$aws configure --profile attacker$# AWS Access Key ID: AKIA...$# Secret Access Key: ...$# region: us-east-1 output: json$ $aws --profile attacker sts get-caller-identityPublicly shared resources
$# Public AMIs / snapshots — filter by keyword$aws --profile attacker ec2 describe-images --owners amazon --executable-users all$aws --profile attacker ec2 describe-images --executable-users all --filters "Name=name,Values=*Offseclab*"$aws --profile attacker ec2 describe-snapshots --filters "Name=description,Values=*offseclab*"Obtaining the account ID from an S3 bucket
$# Pull the public bucket name straight from the site$curl -s www.offseclab.io | grep -o -P 'offseclab-assets-public-\w{8}'$aws --profile attacker s3 ls offseclab-assets-public-kaykoour$ $# Create an isolated IAM user and key to probe with$aws --profile attacker iam create-user --user-name enum$aws --profile attacker iam create-access-key --user-name enum$aws configure --profile enumAttach an inline policy whose Condition restricts by account-ID prefix; toggling the prefix and observing allow/deny leaks the account ID digit by digit:
01{02 "Version": "2012-10-17",03 "Statement": [{04 "Sid": "AllowResourceAccount",05 "Effect": "Allow",06 "Action": ["s3:ListBucket", "s3:GetObject"],07 "Resource": "*",08 "Condition": { "StringLike": { "s3:ResourceAccount": ["0*"] } }09 }]10}$aws --profile attacker iam put-user-policy --user-name enum --policy-name s3-read --policy-document file://policy-s3-read.json$aws --profile enum s3 ls offseclab-assets-private-kaykoour$# Widen the prefix (1*, 10*, 11*, ...) until the listing succeeds — that's the account ID.Enumerating IAM users in other accounts
A bucket policy Principal is validated on write — a nonexistent principal throws MalformedPolicy: Invalid principal, so you can test for the existence of arn:aws:iam::<acct>:user/<name>.
$aws --profile attacker s3 mb s3://offseclab-dummy-bucket-$RANDOM-$RANDOM-$RANDOM$# put-bucket-policy with Principal arn:aws:iam::123456789012:user/cloudadmin$# success -> user exists$# MalformedPolicy -> user does not existAutomate user/role enumeration with Pacu:
$sudo apt install pacu$pacu$import_keys attacker$ls$help iam__enum_roles$run iam__enum_roles --word-list /tmp/role-names.txt --account-id 123456789012Initial IAM reconnaissance (from compromised creds)
$aws configure --profile target$aws --profile target sts get-caller-identity # username, account, path$ $# Stealthier account-ID lookup from an access key$aws --profile challenge sts get-access-key-info --access-key-id AKIA...$ $# Error messages leak identity even for nonexistent resources$aws --profile target lambda invoke --function-name arn:aws:lambda:us-east-1:123456789012:function:nonexistent-function outfileScoping your own permissions
$aws --profile target iam list-user-policies --user-name clouddesk-plove$aws --profile target iam list-attached-user-policies --user-name clouddesk-plove$aws --profile target iam list-groups-for-user --user-name clouddesk-plove$aws --profile target iam list-attached-group-policies --group-name support$ $# Read a managed policy document (find current version first)$aws --profile target iam list-policy-versions --policy-arn "arn:aws:iam::aws:policy/job-function/SupportUser"$aws --profile target iam get-policy-version --policy-arn arn:aws:iam::aws:policy/job-function/SupportUser --version-id v8IAM resource enumeration
$aws --profile target iam get-account-summary | tee account-summary.json # MFA usage, counts$aws --profile target iam list-users | tee users.json$aws --profile target iam list-groups | tee groups.json$aws --profile target iam list-roles | tee roles.json$aws --profile target iam list-policies --scope Local --only-attached | tee policies.json$ $# Full snapshot (needs GetAccountAuthorizationDetails)$aws --profile target iam get-account-authorization-details --filter User Group LocalManagedPolicy Role | tee account-authorization-details.jsonFiltering with JMESPath (--query)
$aws --profile target iam get-account-authorization-details --filter User --query "UserDetailList[].UserName"$aws --profile target iam get-account-authorization-details --filter User --query "UserDetailList[0].{Name: UserName,Path: Path,Groups: GroupList}"$aws --profile target iam get-account-authorization-details --filter User --query "UserDetailList[?contains(UserName, 'admin')].{Name: UserName}"Automated enumeration with Pacu
$pacu$import_keys target$run iam__enum_users_roles_policies_groups$services$data IAMLook for tells of over-privilege: usernames containing admin, membership in admin groups, and attached AdministratorAccess / job-function policies. Those become your escalation and lateral-movement paths.