Skip to main content
Synced from an Obsidian vault

For graph and advanced features, download the full Intel Codex Vault and open it in Obsidian.

Cloud Pentesting SOP (Authorized)

Authorized environments only. This SOP covers defensive assessment of cloud (AWS / Azure / GCP) tenants. Cloud testing reaches the identity plane: a single mis-scoped iam:PassRole, Microsoft.Authorization/roleAssignments/write, or iam.serviceAccounts.actAs rehearsal can grant cross-tenant access. Authorization is per-CSP, per-account/subscription/project, per-region, and per-identity-plane action.


Table of Contents


Pre-Engagement & Authorization

Cloud pentesting touches the highest-trust control plane in modern infrastructure: identity (IAM / Entra ID / Cloud IAM), management APIs (CloudTrail-loud *:Create*, *:Update*, role-assumption chains), and shared-responsibility boundaries that are different per CSP. A single mis-scoped privilege-escalation rehearsal in an unauthorized account can become a CFAA / Computer Misuse Act / EU Cybercrime Directive 2013/40/EU matter — see Legal & Ethics for the canonical legal framework. CSPs themselves require advance notice for some test types (AWS no longer requires pre-approval for most penetration testing under its current Customer Support Policy [verify 2026-04-26]; Azure aligns with Microsoft Cloud Penetration Testing Rules of Engagement [verify 2026-04-26]; GCP follows the Cloud Platform Acceptable Use Policy [verify 2026-04-26]).

Authorization Checklist

  • Signed Rules of Engagement enumerates which CSPs, accounts/subscriptions/projects, and regions are in-scope. "Production tenant" without a per-account list is too vague — collect the 12-digit AWS account IDs, Azure subscription GUIDs, and GCP project IDs in writing.
  • Identity-plane scope is explicit: which roles / service principals / service accounts can be assumed, which cannot. *:PassRole, Microsoft.Authorization/roleAssignments/write, and iam.serviceAccounts.actAs rehearsals require named-target authorization.
  • Customer data handling agreed: what may be read (object metadata only? object contents in non-prod buckets? RDS snapshot exports?). PII / PHI / PCI / cardholder data is excluded by default — document any exceptions.
  • Destructive primitives explicitly listed in/out of scope: iam:DeleteRole, KMS key disable, snapshot delete, Microsoft.Compute/virtualMachines/delete, gcloud compute instances stop, IAM policy attach/detach. Default: read-only enumeration; any write requires named authorization.
  • CSP marketplace pre-notification done where required. Check current CSP penetration-testing policies before scanning any service the customer doesn't own (e.g. third-party SaaS hosted on the CSP).
  • Blue-team coordination confirmed with whoever runs CloudTrail / Activity Log / Cloud Audit Logs alerting, GuardDuty / Defender for Cloud / Security Command Center, the SIEM (Sentinel / Chronicle / Splunk Cloud), and the SOC bridge. Names plus 24/7 contacts captured. Emergency-stop signal agreed.
  • Recovery preconditions met: working IaC rollback (Terraform / Bicep / Deployment Manager state intact), tested backup of any tenant-level config that will be touched (Organizations / Management Groups / Folders), and a documented "service principal credential rotation" runbook if any client secret will be extracted.
  • Cross-cloud / federated identity in scope? AWS↔Azure (SAML federation, Workload Identity Federation), Azure↔GCP (Workload Identity Federation), and any third-party IdP (Okta, Auth0, Ping) trust relationships — traversing a trust to query objects in the federated tenant requires that tenant's separate authorization.

Lab Environment Requirements

  • Standalone cloud account / subscription / project per CSP — never rehearse against the customer's tenant. Free-tier (AWS Free Tier, Azure Free Account, GCP $300 trial) suffices for most enumeration practice; some services (GuardDuty, Sentinel, SCC Premium) carry meaningful spend [verify 2026-04-26].
  • Lab parity with target's identity model: same federation provider (Entra ID, Okta, Ping), same MFA enforcement, similar role count. Privilege-escalation paths often exist only when role inventory crosses certain thresholds.
  • Tooling rehearsed end-to-end against the lab first — Pacu, ScoutSuite, Prowler, ROADtools, AzureHound, CloudFox, gcloud — to confirm version compatibility, permission requirements, and CloudTrail / Activity Log noise level.
  • Privilege-escalation primitives (iam:PassRole chains, Microsoft.Authorization/roleAssignments/write, iam.serviceAccounts.actAs) verified on lab roles before being attempted on customer roles. Some chains require specific role-trust-policy / role-assignment / IAM-binding configurations that don't exist in the customer tenant.
  • Sacrificial credentials used for password-spray / credential-stuffing tests — never the engagement operator's daily-driver IAM user / Entra account / GCP user.

Disclosure-Ready Posture

Stand up the evidence pipeline before the first aws sts get-caller-identity call, not after the report deadline. Capture every API call (CSP CLI verbose mode, --debug flag, or wrap all calls in a logging proxy), every IAM/role/binding diff (export before/after the test window), every storage-bucket ACL state at the moment of test — chain-of-custody per Collection Log (UTC timestamps, source IP, operator handle, SHA-256 of each artifact). CloudTrail / Activity Log / Cloud Audit Log copies are part of the evidence package; coordinate a log-preservation request with the SOC up front (CloudTrail S3 buckets are eventually consistent — verify object lock / versioning before relying on the bucket as evidence). Short-lived credentials harvested during testing (STS tokens, Entra access tokens, GCP access tokens) hold replayable identity material: encrypt at rest, store separately from the report deliverable, and schedule destruction in the engagement letter — OPSEC framing per OPSEC. Defang any IOC that ships in the final write-up (redact account IDs, subscription GUIDs, project IDs to the minimum needed for the customer to identify the affected resources) and route the hand-off through Reporting & Disclosure; tokens and policies never travel in the same channel as the executive summary.


1. Engagement Setup

Pre-Engagement

  • Define scope (CSPs, accounts/subscriptions/projects, regions, services in/out)
  • Obtain written authorization (RoE) — see Pre-Engagement & Authorization above
  • Identify CSP support plan (some destructive checks require pre-coordination with CSP support)
  • Establish testing windows and emergency-stop signal
  • Set up testing infrastructure (jump host with logged shells, evidence bucket, time-sync to NTP)
  • Confirm cost ceiling — some tools (Prowler full scan, Cartography ingest) generate API request volume that bills

Objectives (Examples)

  • Identify identity-plane attack paths (privilege escalation, lateral movement across accounts/subscriptions/projects)
  • Discover misconfigured or publicly exposed storage / databases / APIs
  • Test detection coverage (CloudTrail / Activity Log / Cloud Audit Log → SIEM pipeline)
  • Validate IAM least-privilege posture (role/binding inventory, dormant access, separation of duties)
  • Identify unmanaged shadow IT (rogue accounts, abandoned projects, unmanaged service principals)
  • Evaluate compliance posture (CIS Benchmarks, NIST SP 800-53 cloud controls, PCI DSS 4.0 cloud scoping)

2. Cloud Account Discovery & Attribution

AWS Account Enumeration

# Confirm caller identity (account ID, ARN, user/role)
aws sts get-caller-identity

# List your own access — start here, never assume root
aws iam get-account-summary
aws iam list-users
aws iam list-roles
aws iam list-account-aliases

# Account-ID disclosure via S3 (Daniel Grzelak technique — works against any S3 ARN you can read)
# https://aws.amazon.com/blogs/security/easier-way-to-determine-the-presence-of-aws-account-access-keys/ [verify 2026-04-26]
aws s3api get-bucket-policy --bucket <target-bucket>

# Bucket-name enumeration (target-naming patterns, not brute-force against AWS)
# https://github.com/sa7mon/S3Scanner
s3scanner scan --bucket-file my-targets.txt

# Region inventory (some services are global, most are regional — enumerate all enabled regions)
aws ec2 describe-regions --query 'Regions[].RegionName' --output text

Azure Tenant Enumeration

# Confirm signed-in identity
az account show
az account list --all

# Tenant-ID disclosure (unauthenticated — DNS + OpenID Connect discovery)
# Tenant ID for any verified domain:
curl -s "https://login.microsoftonline.com/<domain>/.well-known/openid-configuration" | jq -r .issuer
# Returns: https://login.microsoftonline.com/<tenant-guid>/v2.0

# AADInternals (PowerShell) — comprehensive Entra reconnaissance, much of it unauthenticated
# https://github.com/Gerenios/AADInternals (Nestori Syynimaa)
Install-Module AADInternals
Get-AADIntLoginInformation -Domain example.com
Get-AADIntTenantDomains -Domain example.com
Invoke-AADIntReconAsOutsider -DomainName example.com # external-only recon

# Subscription / management group inventory (authenticated)
az account management-group list
az account list --output table
az role assignment list --all --output table # tenant-wide if the caller has read access

GCP Project Enumeration

# Confirm signed-in identity
gcloud auth list
gcloud config list

# Project inventory (caller-visible only)
gcloud projects list
gcloud organizations list
gcloud resource-manager folders list --organization=<org-id>

# Service-account inventory in a project
gcloud iam service-accounts list --project=<project-id>

# Cloud Asset Inventory — single API for enumeration across an org/folder/project
# https://cloud.google.com/asset-inventory/docs
gcloud asset search-all-resources --scope=organizations/<org-id> --asset-types='compute.googleapis.com/Instance'

Multi-Cloud Asset Inventory

For multi-CSP engagements, an asset graph is faster than per-CSP enumeration:

# Cartography (CNCF Sandbox; was Lyft, donated 2024-09 [verify 2026-04-26])
# https://github.com/cartography-cncf/cartography
# Multi-cloud (AWS / Azure / GCP) + Kubernetes + GitHub asset graph in Neo4j
docker run -d -p 7474:7474 -p 7687:7687 --env=NEO4J_AUTH=neo4j/<pwd> neo4j:5
cartography --neo4j-uri=bolt://localhost:7687 --aws-sync-all-profiles

# Steampipe — SQL over CSP APIs (read-only)
# https://steampipe.io
steampipe plugin install aws azure gcp
steampipe query "select arn, name from aws_iam_role where create_date > now() - interval '90 days'"

# Prowler (multi-CSP — formerly AWS-only, now AWS / Azure / GCP / k8s / M365 [verify 2026-04-26])
# https://github.com/prowler-cloud/prowler
prowler aws --severity high critical --output-formats json-asff csv html
prowler azure --output-formats csv
prowler gcp --output-formats csv

# CloudFox (Bishop Fox) — post-exploit recon focused on attacker-relevant data
# https://github.com/BishopFox/CloudFox
cloudfox aws all-checks
cloudfox azure all-checks
cloudfox gcp all-checks

3. Identity & Access Management

The identity plane is the cloud equivalent of Active Directory's directory: where most exploitable misconfigurations live. Enumerate exhaustively before attempting any privilege-escalation primitive.

AWS IAM (Read-Only Enumeration)

# IAM principals
aws iam list-users
aws iam list-roles
aws iam list-groups
aws iam list-account-aliases

# Per-principal policies (inline + attached)
aws iam list-attached-user-policies --user-name <user>
aws iam list-user-policies --user-name <user>
aws iam get-user-policy --user-name <user> --policy-name <policy>

# Role trust policies — who can assume this role? (Key for cross-account pivots)
aws iam get-role --role-name <role> --query 'Role.AssumeRolePolicyDocument'

# IAM Access Analyzer — find resources shared outside the account / org
aws accessanalyzer list-analyzers
aws accessanalyzer list-findings --analyzer-arn <analyzer-arn>

# Pacu (RhinoSecurityLabs) — AWS exploitation framework
# https://github.com/RhinoSecurityLabs/pacu
pacu # interactive shell
# > set_keys
# > run iam__enum_permissions
# > run iam__enum_users_roles_policies_groups
# > run iam__privesc_scan # safe enumeration of privesc paths

# enumerate-iam.py — brute-force which IAM actions a key can call (no permission required)
# https://github.com/andresriancho/enumerate-iam
python enumerate-iam.py --access-key AKIA... --secret-key ...

# CloudFox — IAM-focused checks
cloudfox aws permissions --principal <user-or-role-arn>
cloudfox aws principals # who has admin?

Azure Entra ID

Microsoft renamed Azure AD to Microsoft Entra ID in September 2023 [verify 2026-04-26]. Many tools and docs still use the legacy name; the API surface (graph.microsoft.com) is unchanged.

# az CLI — authenticated enumeration
az ad user list --output table
az ad group list --output table
az ad app list --output table # application registrations
az ad sp list --output table # service principals (app instances per tenant)
az role assignment list --all # subscription / RG / resource role assignments

# Microsoft Graph PowerShell — broader Entra surface than az CLI
Install-Module Microsoft.Graph
Connect-MgGraph -Scopes "Directory.Read.All","Application.Read.All"
Get-MgUser -All
Get-MgServicePrincipal -All
Get-MgDirectoryRole | Get-MgDirectoryRoleMember

# ROADtools (DirkjanM) — Entra ID reconnaissance dumped to local SQLite
# https://github.com/dirkjanm/ROADtools
pip install roadrecon
roadrecon auth -u user@example.com # interactive auth
roadrecon gather # dump tenant
roadrecon-gui # web UI for analysis

# AzureHound (SpecterOps) — Entra ID + Azure RBAC collector for BloodHound CE
# https://github.com/SpecterOps/AzureHound
azurehound -u user@example.com -p '<pwd>' --tenant <tenant-id> list -o azure.json
# Then ingest into BloodHound CE — pre-built Cypher queries highlight admin paths

# AADInternals — extensive Entra research toolkit (Nestori Syynimaa)
Get-AADIntCondionalAccessPolicies # CA policy bypass research
Get-AADIntPRTToken # Primary Refresh Token extraction (post-compromise)

# MicroBurst (NetSPI) — Azure-focused PowerShell offensive toolkit
# https://github.com/NetSPI/MicroBurst
Import-Module .\MicroBurst.psm1
Get-AzPasswords -Subscription <sub-id> # secrets in App Services / Automation Accounts / KeyVaults
Invoke-EnumerateAzureBlobs -Base <storage-acct> # public-blob enumeration

GCP IAM

# Authenticated enumeration
gcloud auth list
gcloud projects get-iam-policy <project-id>
gcloud organizations get-iam-policy <org-id>
gcloud iam service-accounts list --project=<project-id>

# Per-service-account policies (who can impersonate this SA?)
gcloud iam service-accounts get-iam-policy <sa-email>

# Custom roles (often where over-permissive grants hide)
gcloud iam roles list --project=<project-id>
gcloud iam roles describe <role-id> --project=<project-id>

# Cloud Asset Inventory — historical IAM policy state
gcloud asset search-all-iam-policies --scope=organizations/<org-id>

# gcp_scanner (Google) — multi-project enumeration
# https://github.com/google/gcp_scanner
python3 scanner.py -o ./output -ks <service-account-key.json>

# GCPBucketBrute (RhinoSecurityLabs) — bucket-name enumeration
# https://github.com/RhinoSecurityLabs/GCPBucketBrute
python3 gcpbucketbrute.py -k <keywords.txt> -u

Workload Identity Federation (Cross-Cloud Trust)

Workload Identity Federation lets workloads in one CSP / IdP assume identities in another without long-lived credentials. Trust relationships are a frequent source of unintended access:

  • AWS → external IdP — AWS IAM roles trust external OIDC providers (GitHub Actions, GitLab CI, Entra ID). Mis-scoped Condition blocks in the trust policy let any GitHub repo / any tenant assume the role. Audit aws iam list-open-id-connect-providers and the trust policy Condition block.
  • Azure → external workload (federated credentials on app registrations)az ad app federated-credential list --id &lt;app-id&gt; shows trusted external workloads; check subject and issuer.
  • GCP Workload Identity Federationgcloud iam workload-identity-pools list and gcloud iam workload-identity-pools providers list-oidc enumerate trusted external identities. Common misconfigurations: missing attribute_condition, overly broad audience claim acceptance.

4. Privilege Escalation Paths

CSP privilege escalation is fundamentally about identity-plane primitives, not OS-level exploits. Always rehearse in the lab first; some primitives have side effects (role-trust modification persists; KMS key disable can break encrypted services).

AWS Privilege Escalation

The canonical reference is Spencer Gietzen's "AWS IAM Privilege Escalation – Methods and Mitigation" (Rhino Security, originally 2018; expanded in subsequent posts) — covers ~21 documented privesc paths [verify 2026-04-26]. Categories:

  • iam:PassRole + service-createlambda:CreateFunction, ec2:RunInstances, glue:CreateDevEndpoint, cloudformation:CreateStack, datapipeline:CreatePipeline etc., where the attacker passes a privileged role to a service they control.
  • Policy-attach / version primitivesiam:AttachUserPolicy, iam:AttachRolePolicy, iam:PutUserPolicy, iam:PutRolePolicy, iam:CreatePolicyVersion (set as default), iam:SetDefaultPolicyVersion.
  • Role-assumption primitivesiam:UpdateAssumeRolePolicy (rewrite the trust policy to allow yourself), sts:AssumeRole against an over-trusted role.
  • Group / login profileiam:AddUserToGroup (add yourself to a privileged group), iam:UpdateLoginProfile / iam:CreateLoginProfile (reset another user's console password).
  • Access-key primitivesiam:CreateAccessKey for another user.
# Pacu privesc scan — checks against the canonical Rhino path catalog
pacu
# > run iam__privesc_scan # enumeration only, lists candidate paths
# > run iam__backdoor_users_keys # WRITE — only with explicit authorization

# Manual check — enumerate which actions you can call (no policy read needed)
# https://github.com/andresriancho/enumerate-iam
python enumerate-iam.py --access-key AKIA... --secret-key ...

# Specific primitive checks
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::&lt;account&gt;:user/&lt;user&gt; \
--action-names iam:PassRole iam:AttachUserPolicy iam:CreatePolicyVersion sts:AssumeRole

Azure Privilege Escalation

  • Microsoft.Authorization/roleAssignments/write — assign yourself any role on any scope you can write to. Owner role at any subscription = subscription takeover.
  • Microsoft.Compute/virtualMachines/runCommand/action — execute commands on a VM as SYSTEM / root, even without VM admin. Combined with a managed identity, escalates to whatever the MI can do.
  • Microsoft.Web/sites/publishxml/action — extract App Service deployment credentials.
  • App registration secret abuseApplication.ReadWrite.OwnedBy or Application.ReadWrite.All lets an attacker add a client secret / certificate to any owned (or all) app registrations, then sign in as that service principal.
  • Custom-role escalation — custom RBAC roles with * action scopes; enumerate with az role definition list --custom-role-only.
  • Conditional Access bypass — devices, location-based exceptions, legacy-auth fallback. AADInternals' CA policy enumeration helps map gaps.
# az CLI primitive checks
az role assignment list --assignee &lt;upn-or-spn-id&gt; --all
az role definition list --custom-role-only --output table

# MicroBurst — secret extraction across subscription
Get-AzPasswords -Subscription &lt;sub-id&gt;

# Stormspotter is archived (2022) — AzureHound + BloodHound CE replaced it [verify 2026-04-26]
# https://github.com/Azure/Stormspotter (archived)
# https://github.com/SpecterOps/AzureHound (current)

GCP Privilege Escalation

The canonical reference is the Rhino Security GCP IAM Privilege Escalation series (2020) — distinct categories include impersonation chains and resource-hierarchy abuse [verify 2026-04-26].

  • iam.serviceAccounts.actAs + service-create — analogous to AWS iam:PassRole. iam.serviceAccounts.actAs + compute.instances.create lets you launch a VM as a privileged SA and inherit its tokens via the metadata server.
  • iam.serviceAccounts.getAccessToken — directly mint a token for any SA you can impersonate.
  • iam.serviceAccounts.signJwt / signBlob — sign tokens / blobs as the SA without minting an access token (useful when access-token minting is logged but signing is not).
  • iam.serviceAccountKeys.create — create a long-lived JSON key for any SA.
  • deploymentmanager.deployments.create — Deployment Manager runs as the project's default service account; deployment templates can grant arbitrary IAM bindings.
  • cloudbuild.builds.create — Cloud Build runs as the Cloud Build SA (default has broad project access); a malicious cloudbuild.yaml exfiltrates the SA token.
# Direct primitive checks
gcloud projects get-iam-policy &lt;project-id&gt; | grep -E 'serviceAccount(Key|Token|User|Admin)'

# gcp_iam_privesc / iam-privilege-escalation references
# https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation [verify 2026-04-26]
# Manual — impersonate a target SA (read-only check that your account has actAs)
gcloud iam service-accounts get-iam-policy &lt;target-sa&gt;@&lt;project&gt;.iam.gserviceaccount.com
gcloud auth print-access-token --impersonate-service-account=&lt;target-sa&gt;@&lt;project&gt;.iam.gserviceaccount.com

Cross-Account / Cross-Tenant Pivots

  • AWSsts:AssumeRole to a role in another account that trusts your account / principal. Enumerate trust policies of every role in scope; cross-account trust to overly broad principals (Principal: "*" with weak Condition) is a finding.
  • AWS Organizationsorganizations:DescribeOrganization + delegated-admin checks; SCPs (Service Control Policies) only deny — they don't grant — but a misconfigured SCP can mask risk.
  • Azure — guest users (B2B), federated tenants (cross-tenant access settings), Lighthouse delegated resource management, multi-tenant app registrations consented in another tenant.
  • GCP — folder-level / org-level IAM bindings, cross-project SA impersonation, shared VPC host-project bindings.

5. Storage & Data Exposure

S3 (AWS)

# Read-only audit (no objects downloaded)
aws s3api list-buckets
aws s3api get-bucket-policy --bucket &lt;bucket&gt;
aws s3api get-bucket-acl --bucket &lt;bucket&gt;
aws s3api get-public-access-block --bucket &lt;bucket&gt; # account + bucket-level
aws s3api get-account-public-access-block # account-level

# Bucket-name enumeration — public discovery (no credentials needed)
# https://github.com/sa7mon/S3Scanner
s3scanner scan --bucket-file targets.txt

# Presigned-URL exposure — leaked URLs in source code, CI logs, support tickets
# (No CLI primitive — enumerate via leak surfaces; see sop-bug-bounty for code-search workflow)

# CloudFox S3 audit
cloudfox aws buckets

Azure Storage

# Public-blob enumeration (unauthenticated DNS-based)
# Storage account naming: &lt;account&gt;.blob.core.windows.net
# https://github.com/NetSPI/MicroBurst — Invoke-EnumerateAzureBlobs
Invoke-EnumerateAzureBlobs -Base &lt;storage-account-name&gt;

# Authenticated audit
az storage account list --output table
az storage account blob-service-properties show --account-name &lt;account&gt;
az storage container list --account-name &lt;account&gt; # public-access container check
az storage account keys list --account-name &lt;account&gt; # account keys (sensitive)

# SAS-token leak detection
# Search source / logs / support channels for sig=, sv=, st=, se= patterns; presence of
# &sig=&lt;urlencoded-base64&gt;&se=&lt;expiry&gt; is the canonical SAS signature.

Google Cloud Storage

# IAM and bucket policies
gcloud storage buckets list --project=&lt;project-id&gt;
gcloud storage buckets get-iam-policy gs://&lt;bucket&gt;
gcloud storage buckets describe gs://&lt;bucket&gt; --format=json

# Public-bucket enumeration
# https://github.com/RhinoSecurityLabs/GCPBucketBrute
python3 gcpbucketbrute.py -k <keywords.txt> -u

# Signed URLs — same caveat as AWS presigned URLs (leaked in code / CI / tickets)

Managed Databases

  • AWS RDS / Auroraaws rds describe-db-instances --query 'DBInstances[?PubliclyAccessible==true]'; check security-group ingress.
  • AWS DynamoDB — IAM-only access (no public option), but resource policies and IAM roles can grant cross-account access.
  • Azure SQL / Cosmos DB / Managed Instanceaz sql server firewall-rule list; check for 0.0.0.0 allow rules; Cosmos DB has its own az cosmosdb show --name &lt;db&gt; --query 'publicNetworkAccess'.
  • GCP Cloud SQL / Spanner / BigQuerygcloud sql instances describe &lt;inst&gt; --format='value(settings.ipConfiguration.authorizedNetworks)'; BigQuery datasets have IAM bindings (bq show --format=prettyjson &lt;dataset&gt;).

6. Compute Layer

Instance Metadata Service Abuse

The metadata service is the most common SSRF-to-cloud-credential pivot. CSP differences matter:

  • AWS IMDSv1 vs IMDSv2 — IMDSv1 is unauthenticated GET against 169.254.169.254. IMDSv2 requires a PUT to retrieve a session token, then GET with the token; unauthenticated SSRF cannot reach IMDSv2 if HttpTokens=required and HttpPutResponseHopLimit=1. Audit:

    aws ec2 describe-instances \
    --query 'Reservations[].Instances[].[InstanceId,MetadataOptions.HttpTokens,MetadataOptions.HttpPutResponseHopLimit]' \
    --output table
    # Look for HttpTokens=optional (IMDSv1 still allowed) and HopLimit>1 (containers can reach it)
  • Azure Instance Metadata Service (IMDS)169.254.169.254/metadata, requires Metadata: true header (mitigates trivial SSRF). Managed Identity tokens via /metadata/identity/oauth2/token. SSRF that lets the attacker set an arbitrary header bypasses the protection.

  • GCP metadata servermetadata.google.internal (169.254.169.254), requires Metadata-Flavor: Google header. Default-token endpoint at /computeMetadata/v1/instance/service-accounts/default/token. Same SSRF caveat as Azure.

# AWS — credential extraction from IMDSv2 (assumes shell on the instance)
TOKEN=$(curl -s -X PUT 'http://169.254.169.254/latest/api/token' -H 'X-aws-ec2-metadata-token-ttl-seconds: 60')
ROLE=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" 'http://169.254.169.254/latest/meta-data/iam/security-credentials/')
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" "http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE"

# Azure — managed identity token
curl -s -H "Metadata: true" 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/'

# GCP — default service-account token
curl -s -H "Metadata-Flavor: Google" 'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token'

Serverless

  • AWS Lambda — function role permissions, environment variables (often hold secrets), Lambda Layers (shared code; layer-poisoning if you can publish to a layer the function imports), function URL exposure (aws lambda get-function-url-config).
  • Azure Functions — function-app identity, app settings (often hold connection strings — az functionapp config appsettings list), function-level auth keys (az functionapp keys list).
  • GCP Cloud Functions / Cloud Run — runtime SA, environment variables, public ingress (gcloud functions describe --format='value(httpsTrigger.url)').

Managed Kubernetes Control Planes

This SOP covers the control-plane / management-API layer of EKS / AKS / GKE — IAM bindings, public endpoint exposure, control-plane logging. Pod-level escapes, RBAC abuse inside the cluster, and admission-controller bypass live in Container & Kubernetes Pentesting. Hand off when the engagement reaches kubectl exec.

# AWS EKS
aws eks list-clusters
aws eks describe-cluster --name &lt;cluster&gt; --query 'cluster.{Endpoint:endpoint,PublicAccess:resourcesVpcConfig.endpointPublicAccess,PublicCIDRs:resourcesVpcConfig.publicAccessCidrs,Logging:logging}'
# Public endpoint with 0.0.0.0/0 + no logging = finding

# Azure AKS
az aks list --output table
az aks show --name &lt;cluster&gt; --resource-group &lt;rg&gt; --query 'apiServerAccessProfile'

# GCP GKE
gcloud container clusters list
gcloud container clusters describe &lt;cluster&gt; --zone=&lt;zone&gt; --format='value(privateClusterConfig,masterAuthorizedNetworksConfig)'

7. Network & Edge

Perimeter Rule Review

# AWS — Security Groups with 0.0.0.0/0 ingress
aws ec2 describe-security-groups \
--query 'SecurityGroups[?IpPermissions[?IpRanges[?CidrIp==`0.0.0.0/0`]]].[GroupId,GroupName,IpPermissions[?IpRanges[?CidrIp==`0.0.0.0/0`]].FromPort]' \
--output table

# Azure — NSGs with 0.0.0.0 / Internet source
az network nsg list --output table
az network nsg rule list --nsg-name &lt;nsg&gt; --resource-group &lt;rg&gt; --query "[?sourceAddressPrefix=='*' || sourceAddressPrefix=='0.0.0.0/0' || sourceAddressPrefix=='Internet']"

# GCP — firewall rules
gcloud compute firewall-rules list --filter="sourceRanges:0.0.0.0/0 AND direction=INGRESS"

Cross-VPC / Cross-VNet Reachability

  • AWS — VPC peering connections, Transit Gateway attachments, VPC endpoints, PrivateLink endpoints; aws ec2 describe-vpc-peering-connections.
  • Azure — VNet peering, Virtual WAN hubs, Private Link / Private Endpoints; az network vnet peering list.
  • GCP — VPC peering, Shared VPC, Network Connectivity Center; gcloud compute networks peerings list.
  • VPC Service Controls (GCP) — perimeter around APIs; misconfigured perimeter = data-exfil path. gcloud access-context-manager perimeters list.

Edge Services

  • AWS CloudFront — origin discovery (aws cloudfront get-distribution-config), cache-behavior misconfiguration; signed-URL key compromise.
  • Azure Front Door / Application Gateway — origin pool review, WAF policy review.
  • GCP Cloud CDN / Cloud Load Balancing — backend service config, IAP (Identity-Aware Proxy) misconfiguration.
  • API Gateway / API Management — authorizer bypass (request-context header manipulation), unauthenticated routes.

8. Logging & Detection Coverage

Trail / Log Coverage Audit

Every CSP has a single-question check: "is the audit log on, in every region/scope, with tamper protection?"

# AWS — CloudTrail org-wide multi-region trail with S3 object-lock
aws cloudtrail describe-trails --query 'trailList[].[Name,IsMultiRegionTrail,IsOrganizationTrail,LogFileValidationEnabled,S3BucketName]' --output table

# Verify S3 destination has object-lock enabled
aws s3api get-object-lock-configuration --bucket &lt;cloudtrail-bucket&gt;

# Azure — Activity Log diagnostic settings (per subscription, sent to Log Analytics / Event Hub / Storage)
az monitor diagnostic-settings subscription list --query '[].{name:name,workspaceId:workspaceId,storageAccountId:storageAccountId,eventHubAuthorizationRuleId:eventHubAuthorizationRuleId}' --output table

# GCP — Cloud Audit Logs are on by default for Admin Activity; Data Access logs require explicit enable
gcloud logging sinks list
gcloud projects get-iam-policy &lt;project&gt; | grep -E 'logging\.(privateLogViewer|admin)'

Threat-Detection Service Coverage

  • AWS GuardDutyaws guardduty list-detectors + get-detector; check if enabled in every region (regional service); EKS Audit Log monitoring + Malware Protection are extra-cost add-ons [verify 2026-04-26].
  • Azure Defender for Cloudaz security pricing list (per-plan pricing tier per subscription); "Defender for X" plan toggles per workload type.
  • GCP Security Command Centergcloud scc settings services list --organization=&lt;org&gt;; SCC Standard is free, Premium is paid [verify 2026-04-26].

For controlled detection-coverage testing (running a known-bad action and verifying it surfaces in the SIEM), see Detection Evasion Testing — that SOP is the canonical home for evasion methodology and SIEM-validation playbooks.


9. Common Misconfigurations Checklist

  • Root account / Global Admin / Org Admin without MFA (or with MFA but no break-glass procedure documented)
  • IAM users with both console password AND access keys (one or the other, not both)
  • Long-lived access keys (>90d) — prefer SSO + short-lived STS / Entra access tokens / GCP short-lived credentials
  • Service principals / service accounts with * action scope or Owner / Project Owner role
  • Public S3 / Storage / GCS buckets without business justification (and BlockPublicAccess off at the account level)
  • RDS / Cosmos DB / Cloud SQL with 0.0.0.0/0 firewall and no VPC restriction
  • CloudTrail / Activity Log / Cloud Audit Logs disabled in any region/scope, or destination bucket without object-lock
  • EKS / AKS / GKE control-plane endpoint public to 0.0.0.0/0 without a tight allowlist
  • EC2 / VM / Compute Engine with IMDSv1 enabled (IMDSv2 / IMDS-with-header should be enforced)
  • Lambda / Function / Cloud Function with secrets in environment variables (use Secrets Manager / Key Vault / Secret Manager)
  • Cross-account / cross-tenant trust with Principal: "*" or no Condition
  • Workload Identity Federation provider with no attribute_condition / no subject_claim restriction
  • Default service account on Compute Engine VMs (use a dedicated SA per workload)
  • Default VPC still present and used (custom VPCs with explicit subnets preferred)
  • IAM Access Analyzer / Defender for Cloud Recommendations / Security Health Analytics findings unaddressed for >30 days
  • Cost-anomaly alerting absent (cryptojacking is often discovered via the bill, not the SIEM)

10. Evidence Collection

Cloud evidence is API-call output — capture the call, the response, and the timestamp. Per-CSP:

  • AWS — wrap commands in aws --debug ... for full request/response logging; archive CloudTrail event IDs (eventID, eventTime, requestID) for each test action so the customer can correlate.
  • Azureaz --debug and Activity Log correlationId for each test action.
  • GCPgcloud --log-http and Cloud Audit Log protoPayload.requestId.

Hash every artifact (SHA-256), record source IP + operator handle + UTC timestamp, store in an evidence bucket separate from the customer's tenant. Pattern per Collection Log.

For STS / Entra / GCP access tokens harvested during testing: encrypt-at-rest, log who accessed them, schedule destruction in the engagement letter. Tokens are replayable identity material until they expire (1h-12h typical) — short window but real.


11. Reporting

Finding Format

**Title:** &lt;CSP&gt; · &lt;Service&gt; · &lt;Issue&gt; — e.g. "AWS · IAM · iam:PassRole + lambda:CreateFunction privilege escalation path"
**Severity:** Critical / High / Medium / Low / Info
**CSP:** AWS / Azure / GCP / Multi
**Account / Subscription / Project:** &lt;ID&gt; (redact to last-4 in shareable copies)
**Region(s):** &lt;list&gt; or "Global"
**Affected Resources:** <ARNs / Resource IDs / Project paths>
**Description:** <what the misconfiguration is, in CSP-neutral terms where possible>
**Attack Path:** <step-by-step, named primitives, no production-data screenshots>
**Impact:** <data confidentiality / integrity / availability + blast radius>
**Evidence:** <CloudTrail event IDs / Activity Log correlation IDs / Cloud Audit Log request IDs + UTC timestamps + SHA-256 hashes>
**Remediation:** <CSP-native fix; IaC snippet preferred; link to vendor docs>
**References:** <CIS Benchmark control / NIST SP 800-53 / CSP whitepaper>

Remediation Priority

  1. Public storage / database with PII, PHI, PCI, or secrets (Critical — fix in hours)
  2. Identity-plane privilege escalation paths to Owner / Admin / Org-level roles (Critical — fix in days)
  3. Long-lived access keys / service-principal secrets / SA keys for high-privilege identities (High)
  4. CloudTrail / Activity Log / Cloud Audit Log gaps (High — without logs, no IR)
  5. Over-permissive cross-account / cross-tenant trust (High)
  6. IMDSv1 / weak metadata-service config on internet-facing compute (High)
  7. Public k8s control-plane endpoints (High)
  8. Network exposures (0.0.0.0/0 on management ports) (Medium-High depending on service)
  9. Findings from CSP-native scanners (Access Analyzer / Defender / SCC) backlogged >30d (Medium)
  10. Documentation / IaC drift, missing tags, missing cost alerting (Low-Info)

12. SaaS Collaboration Plane

The CSP control plane is one half of a modern enterprise; the other half is the SaaS layer anchored to it. Microsoft 365, Google Workspace, Slack, Salesforce, and GitHub Enterprise / Cloud share the customer's IdP (Entra ID, Workspace directory, Okta, Ping) and frequently terminate OAuth consents, PATs, and federated trust relationships that bridge into the AWS / Azure / GCP planes covered in §1–§9. A compromised Slack bot token can carry the same blast radius as a leaked AWS access key; a Workspace service account with Domain-Wide Delegation is the SaaS analog of a * IAM policy.

This section covers SaaS as an offensive target. The defensive counterpart — log retention, OAuth-consent forensics, mailbox-rule audit — lives in SaaS Log Forensics and Email & BEC Forensics.

Scope & Authorization (SaaS-specific)

SaaS testing layers vendor-specific rules of engagement on top of the customer's authorization:

  • Microsoft 365 / Entra ID — Microsoft Cloud Penetration Testing Rules of Engagement [verify 2026-04-26]. Most testing of customer-owned tenants needs no Microsoft pre-approval; DDoS, brute-force at volumes that alert MSRC, and any test touching infrastructure outside the customer's tenant remain prohibited.
  • Google Workspace — Google's testing policy aligns with the Cloud Platform AUP [verify 2026-04-26]; test only customer-owned domains and tenants.
  • Slack, Salesforce, GitHub — each platform has its own bug-bounty / security-research policy. Salesforce limits testing on production orgs without prior coordination; GitHub maintains a HackerOne program with safe-harbor language and out-of-scope rules for github.com infrastructure separate from customer-owned repos / orgs / Apps.
  • Cross-tenant OAuth abuse is its own scope class. A consent-phishing test that lands an attacker app in a third-party tenant requires that tenant's authorization, even if the attacker app is owned by the customer's tenant.

Capture in the RoE for each SaaS in scope: tenant ID / domain / org name, sandbox-vs-production designation, which integrations / Connected Apps / GitHub Apps may be created or modified, which scopes (Mail.ReadWrite, Drive.ReadWrite.All, repo, etc.) are authorized, and the OAuth-consent rollback procedure if anything is granted in error. Identity-plane fundamentals (operator handle, sacrificial accounts, IOC defang) per OPSEC apply unchanged.

Microsoft 365 / Entra ID Offensive

M365 is the densest SaaS attack surface and overlaps with §3 Entra ID enumeration. Beyond directory enumeration, M365 adds:

  • Illicit consent grants — OAuth phishing where a victim user grants Mail.ReadWrite / Files.ReadWrite.All / offline_access to an attacker-controlled multi-tenant app registration. Persists past password reset and survives MFA. CISA AA22-074A documented the technique [verify 2026-04-26]. Audit Get-MgServicePrincipal -All | Get-MgServicePrincipalOauth2PermissionGrant for unexpected delegated grants and admin-consented application permissions.
  • App registration credential abuse — already cited in §4 Azure Privilege Escalation; relevant here because most "M365 apps" are app registrations.
  • Mail-flow / inbox rules — Exchange transport rules and per-mailbox inbox rules forward / hide / auto-delete mail. Recurring BEC TTP. Audit via Get-InboxRule (per mailbox) and Get-TransportRule (tenant-wide).
  • SharePoint / OneDrive sharing — anonymous sharing links, "anyone with the link" defaults, external-sharing scope. Get-SPOSite and Get-SPOTenant for tenant-level posture; Get-SPOExternalUser for external-user inventory.
  • Teams external access and federation — federation with arbitrary tenants, guest-tenant access, custom Teams app sideloading, policy bypass via legacy Skype-for-Business federation if not disabled.
  • Conditional Access bypass paths — legacy auth (POP / IMAP / SMTP-AUTH against cloud mailboxes if not blocked), device-compliance gaps, location / IP allowlist abuse, named-locations spoofing where the CA policy uses caller-IP only.
# GraphRunner (BHIS / Beau Bullock) — post-compromise Graph API enumeration + abuse
# https://github.com/dafthack/GraphRunner
Import-Module .\GraphRunner.ps1
Invoke-GraphRunner -Tokens $tokens # interactive; modules: Inbox-Search, SharePoint-Search, etc.
Invoke-SearchSharePointAndOneDrive -Tokens $tokens -SearchTerm "password"

# AADInternals — Conditional Access policy enumeration (post-auth, requires admin-equivalent)
Get-AADIntConditionalAccessPolicies

# MFASweep (BHIS) — checks which Microsoft endpoints accept legacy auth without MFA
# https://github.com/dafthack/MFASweep
Invoke-MFASweep -Username 'user@example.com' -Password '&lt;pwd&gt;' -Recon

# TeamFiltration (TrustedSec / Flangvik) — M365 password spray + post-auth dump
# https://github.com/Flangvik/TeamFiltration
TeamFiltration --outpath ./out --config config.json --spray --userlist users.txt --password 'Spring2026!'

# o365spray — O365 user enumeration + password spray
# https://github.com/0xZDH/o365spray
o365spray --validate --domain example.com
o365spray --spray -U users.txt -p 'Spring2026!' --domain example.com

# roadrecon (already cited in §3) — Entra dump; GUI shows app-consent grants visually
roadrecon-gui # navigate to "Application permissions" and "Delegated permissions"

Google Workspace Offensive

Workspace is structurally similar to M365 but with thinner public offensive tooling. The primitive set is shorter; the impact is comparable.

  • OAuth consent on a Workspace user — same model as M365 illicit consent. Workspace marketplace apps with https://www.googleapis.com/auth/drive or https://mail.google.com/ scopes are the equivalent of Files.ReadWrite.All / Mail.ReadWrite. Admin policy can require admin approval for any non-internal app, but many tenants leave the marketplace open.
  • Domain-Wide Delegation (DWD) — a service account with DWD can impersonate any user in the Workspace domain over OAuth 2.0 service-account flow, bound to specific scopes the admin pre-approved. A leaked SA key plus DWD authorization equals cross-domain mailbox / Drive read. Audit at admin.google.com → Security → API controls → Domain-wide delegation.
  • Workspace admin role privesc — Super Admin without 2SV is the canonical finding; "Service Admin" / "User Admin" / custom admin roles often grant more than expected. Inventory via Admin SDK Directory API (directory.roleAssignments.list).
  • Mail filters / auto-forwarding — per-user mail filters, "send as" delegated send, IMAP/POP enable. Admin SDK Email Audit API surfaces filter rules; per-user gmail.users.settings.filters.list is the offensive read.
  • Drive sharing — anonymous link sharing, "anyone with the link" defaults, external-sharing controls. Admin SDK + Drive API enumeration; gam (Google Apps Manager, community tool) is the de facto admin CLI.
# GAM (Google Apps Manager) — community admin CLI
# https://github.com/GAM-team/GAM
gam info domain
gam print users
gam print users delegate # delegated send relationships
gam all users print filters # per-user mail filters
gam print roleassignments # admin role inventory
gam print drivesettings # Drive sharing posture per user

# Steampipe googleworkspace plugin
# https://hub.steampipe.io/plugins/turbot/googleworkspace
steampipe plugin install googleworkspace
steampipe query "select primary_email, is_admin, is_delegated_admin, is_enforced_in_2sv from googleworkspace_user where is_admin = true"

# DWD enumeration (requires Workspace admin + GCP project access for the delegating SA)
gcloud iam service-accounts list --project=&lt;project-id&gt;
gcloud iam service-accounts keys list --iam-account=&lt;sa-email&gt;
# Cross-reference with Admin Console DWD client list — any SA with DWD scopes is a privesc candidate

Slack Offensive

Slack tokens are bearer credentials with very granular scopes. Token leak in source code, Workflow Builder triggers, and webhook URL exposure are the recurring findings.

  • Token taxonomyxoxb- (bot), xoxp- (user), xoxc- (browser session cookie, paired with d cookie), xoxa- (app-level), xoxr- (refresh). xoxc + d cookie is the full session — extracted from the desktop app's local storage on a compromised endpoint.
  • Channel-history search for secrets — once a token is in hand, search.messages API across all accessible channels for password, aws_access_key_id, BEGIN PRIVATE KEY, etc.
  • Webhook URL leakagehttps://hooks.slack.com/services/T.../B.../... in code, CI logs, or support tickets equals post-as-app to whatever channel the webhook targets. Common in public repos.
  • Workflow Builder abuse — workflows configured to post external webhook input to a sensitive channel can be triggered by anyone holding the URL.
  • App install scope sprawl — third-party Slack apps frequently request channels:history, groups:history, im:history (private channels + DMs); review installed apps via apps.list (admin token).
# SlackPirate (emtunc) — token-driven Slack workspace dump
# https://github.com/emtunc/SlackPirate
python3 SlackPirate.py --token xoxb-...

# slack-watchman (PaperMtn) — sensitive-data scanning, useful in offensive recon too
# https://github.com/PaperMtn/slack-watchman
slack-watchman --timeframe d --output stdout

# Manual API — enumerate users, channels, files
curl -s -H "Authorization: Bearer xoxb-..." 'https://slack.com/api/auth.test'
curl -s -H "Authorization: Bearer xoxb-..." 'https://slack.com/api/users.list'
curl -s -H "Authorization: Bearer xoxb-..." 'https://slack.com/api/conversations.list?types=public_channel,private_channel'

# Search messages for secrets (xoxp- user tokens have search; bots usually do not)
curl -s -H "Authorization: Bearer xoxp-..." 'https://slack.com/api/search.messages?query=aws_secret_access_key'

# Webhook abuse — post-as-app once URL is known
curl -X POST -H 'Content-type: application/json' --data '{"text":"&lt;message&gt;"}' https://hooks.slack.com/services/T.../B.../...

Salesforce Offensive

Salesforce is unusual: customer data lives behind a SOQL / SOAP / REST API, and the platform has its own permission model (Profiles, Permission Sets, Sharing Rules, Field-Level Security) layered on top of the IdP. Connected Apps are the OAuth bridge.

  • Connected App scope abuse — a Connected App with api, refresh_token, full scope and a broad IP allowlist is the SaaS equivalent of an admin API key. sf org list metadata --metadata-type ConnectedApp inventories Connected Apps with their scopes.
  • API-only / integration user — no UI, no MFA prompt, often a static password or stored OAuth refresh token. Audit Profiles for "API Enabled" + "Password Never Expires" combinations.
  • SOQL injection — Apex code that concatenates user input into dynamic SOQL (Database.query(...)); the same class as SQL injection. Static review of Apex via sf project retrieve then grep.
  • Sharing-rule misconfiguration — Org-Wide Defaults set to Public Read/Write, criteria-based sharing rules that grant too broadly, manual shares persisting after role changes.
  • Apex class with without sharing — bypasses record-level security; combined with a Site / Experience Cloud guest user, exposes internal data.
  • Custom REST endpoint exposure@RestResource(urlMapping='/api/...') Apex classes published via Site / Experience Cloud guest profile.
# Salesforce CLI (sf, formerly sfdx)
# https://developer.salesforce.com/tools/salesforcecli
sf org login web --alias target
sf org display --target-org target # auth, instance URL, API version
sf org list metadata --metadata-type ApexClass --target-org target
sf org list metadata --metadata-type ConnectedApp --target-org target
sf data query --target-org target --query "SELECT Id, Name, ProfileId FROM User WHERE IsActive = true"

# Apex source review — pull all Apex classes for offline grep
sf project retrieve start --target-org target --metadata ApexClass
grep -rE 'Database\.query\(|without sharing' force-app/main/default/classes/

# Permission inventory
sf data query --target-org target --query "SELECT Name, PermissionsModifyAllData FROM PermissionSet WHERE PermissionsModifyAllData = true"

GitHub (Enterprise Cloud / Server) Offensive

GitHub is a SaaS with three identity types — users (PATs), GitHub Apps, OAuth Apps — plus Actions OIDC trust into AWS / Azure / GCP that bridges this section back to §3 Identity. Most findings are credential leakage and Actions-pipeline misconfiguration.

  • PAT / fine-grained PAT in code — classic finding. Tokens grant repo, workflow, admin:org. Fine-grained PATs are scope-bound to specific repos but still expose data within that scope.
  • GitHub App private key abuse — Apps authenticate via JWT signed with an RSA private key. Key-in-source equals full App authority across every repo the App is installed on.
  • Actions secrets and OIDC trustsecrets.AWS_ACCESS_KEY_ID exposed via workflow log injection (debug-mode dumps, ::add-mask:: bypass tricks). Actions OIDC tokens trust an AWS IAM role / Azure federated credential / GCP Workload Identity Pool — misconfigured sub claim conditions (e.g. accepting any repo:*) let attacker-controlled forks assume the role.
  • pull_request_target workflow abuse — workflows triggered on PR but running with write tokens against the base repo; running attacker code from the PR head equals secret theft. Documented at GitHub Security Lab [verify 2026-04-26].
  • Self-hosted runner takeover — public-repo self-hosted runners accept any PR's workflow; an attacker submits a workflow that runs on the runner host. Mitigation is "private repos only" plus ephemeral runners.
  • Branch protection bypass — repo admins can disable protection, force-push, or temporarily delete the branch. Audit org owner / repo admin inventory.
  • Codeowners bypassCODEOWNERS only enforces review when "Require review from Code Owners" is on; repo admins bypass via "Allow specified actors to bypass required pull requests".
# GitHub secret-scanning alerts (defensive view of what's already detected)
gh api -H "Accept: application/vnd.github+json" /repos/&lt;owner&gt;/&lt;repo&gt;/secret-scanning/alerts

# gitleaks — scan for committed secrets
# https://github.com/gitleaks/gitleaks
gitleaks dir /path/to/repo --report-format json --report-path leaks.json

# trufflehog v3 — broader detector set, verifies live tokens
# https://github.com/trufflesecurity/trufflehog
trufflehog git file:///path/to/repo --only-verified
trufflehog github --org=&lt;org&gt; --token=$GH_TOKEN --only-verified

# Actions workflow review (offline static lint)
# https://github.com/rhysd/actionlint
actionlint .github/workflows/*.yml

# Self-hosted runner inventory (org-admin token required)
gh api /orgs/&lt;org&gt;/actions/runners
gh api /repos/&lt;owner&gt;/&lt;repo&gt;/actions/runners

# OIDC trust enumeration — check Actions claims a role can accept
# AWS side: see §3 Workload Identity Federation; check trust-policy Condition for token.actions.githubusercontent.com sub claim
aws iam list-open-id-connect-providers
aws iam get-role --role-name &lt;gha-role&gt; --query 'Role.AssumeRolePolicyDocument'

# octosuite (Bellingcat) — public repo / org / user OSINT
# https://github.com/bellingcat/octosuite
octosuite

Cross-SaaS Pivot Patterns

SaaS findings rarely stand alone — most chain into the identity plane:

  • M365 illicit consent → Azure subscription. A user who is also Owner of an Azure subscription, consenting an attacker app with delegated https://management.azure.com/.default scope, hands the attacker subscription-level access. M365 user identity equals Entra user identity.
  • Workspace OAuth → GCP. A Workspace user who is roles/owner on a GCP project, consenting an OAuth app with the GCP scopes, exposes the project. SA Domain-Wide Delegation amplifies — DWD over Drive scope plus a leaked SA key reads any user's Drive even without that user's consent.
  • GitHub Actions → AWS / Azure / GCP. Misconfigured OIDC trust accepts any sub claim from token.actions.githubusercontent.com — an attacker fork or a pull_request_target workflow assumes the role. The most common SaaS-to-CSP pivot in 2025–26 [verify 2026-04-26].
  • Slack token harvest → CSP credential leak. A bot token's channel-history search frequently surfaces AWS access keys, Azure SAS tokens, GCP SA JSON keys posted by ops or CI bots.
  • Salesforce Connected App → external API. A Connected App with full scope and an API-only user can be the exfil vector for the customer database, even when no CSP credential is exposed.

Document each pivot as its own finding with both the SaaS root cause and the CSP blast radius, per the §11 Reporting format.

Common SaaS Misconfigurations Checklist

  • Multi-tenant app registrations / Workspace Marketplace apps with admin consent for high-risk scopes (Mail.ReadWrite, Files.ReadWrite.All, Drive scope) and unknown publisher
  • Service principals / Connected Apps / GitHub Apps owned by departed employees
  • Service accounts with Domain-Wide Delegation and key inventory > 0
  • Mail-flow / inbox / Workspace-filter forwarding rules sending outside the tenant
  • Anonymous SharePoint / OneDrive / Drive sharing links to sensitive content
  • Conditional Access / Context-Aware Access with no enforcement on legacy auth or named-location bypass
  • GitHub Actions OIDC trust policies with overly broad sub claim conditions (repo:* or no condition)
  • GitHub self-hosted runners enabled on public repos
  • Slack webhook URLs in public repos / build logs / support tickets
  • Salesforce Profiles with Modify All Data granted beyond a documented break-glass set
  • Salesforce Org-Wide Defaults set to Public Read/Write on objects holding regulated data
  • Workspace Super Admin / M365 Global Admin without phishing-resistant MFA (FIDO2 / passkey)

13. Tools Reference

ToolCSPPurposeLink
PacuAWSExploitation framework — IAM enum, privesc, persistencegithub.com/RhinoSecurityLabs/pacu
ScoutSuiteAWS / Azure / GCP / Aliyun / OCIMulti-cloud security auditing (NCC Group)github.com/nccgroup/ScoutSuite
ProwlerAWS / Azure / GCP / k8s / M365Multi-cloud benchmark + finding scannergithub.com/prowler-cloud/prowler
CloudFoxAWS / Azure / GCPPost-exploit recon focused on attacker-relevant data (Bishop Fox)github.com/BishopFox/CloudFox
enumerate-iamAWSBrute-force which IAM actions an access key can callgithub.com/andresriancho/enumerate-iam
s3scannerAWSPublic S3 bucket discovery + permission checkgithub.com/sa7mon/S3Scanner
CartographyAWS / Azure / GCP / k8s / GitHubMulti-cloud asset graph in Neo4j (CNCF Sandbox)github.com/cartography-cncf/cartography [verify 2026-04-26]
SteampipeAWS / Azure / GCP / manySQL-over-cloud-APIs (read-only)steampipe.io
Cloud CustodianAWS / Azure / GCPPolicy-as-code; can also be used for read-only enumerationcloudcustodian.io
ROADtools (roadrecon)Azure / EntraEntra ID reconnaissance + offline analysis (DirkjanM)github.com/dirkjanm/ROADtools
AzureHoundAzure / EntraEntra ID + RBAC collector for BloodHound CE (SpecterOps)github.com/SpecterOps/AzureHound
BloodHound CEAD / Entra / AzureGraph analysis for AD + Entra + Azure RBACgithub.com/SpecterOps/BloodHound
AADInternalsAzure / EntraPowerShell research toolkit (Nestori Syynimaa)github.com/Gerenios/AADInternals
MicroBurstAzureOffensive PowerShell toolkit (NetSPI)github.com/NetSPI/MicroBurst
StormspotterAzureArchived (2022); replaced by AzureHound + BloodHound CE [verify 2026-04-26]github.com/Azure/Stormspotter
GCPBucketBruteGCPPublic Cloud Storage bucket enumeration (RhinoSecurityLabs)github.com/RhinoSecurityLabs/GCPBucketBrute
gcp_scannerGCPMulti-project enumeration (Google)github.com/google/gcp_scanner
CloudSploit / AquaAWS / Azure / GCP / OracleOpen-source security scanning (Aqua acquired CloudSploit 2019)github.com/aquasecurity/cloudsploit
PMapperAWSIAM authorization graph + attack-path searchgithub.com/nccgroup/PMapper
ConsoleMe (Netflix)AWSIAM access portal — useful for inventory in large orgsgithub.com/Netflix/consoleme [verify 2026-04-26]
GraphRunnerM365 / EntraPost-compromise Graph API enumeration + abuse (BHIS)github.com/dafthack/GraphRunner
MFASweepM365 / EntraTests which Microsoft endpoints accept legacy auth without MFA (BHIS)github.com/dafthack/MFASweep
TeamFiltrationM365Password spray + post-auth Outlook / OneDrive / Teams dump (TrustedSec)github.com/Flangvik/TeamFiltration
o365sprayM365User enumeration + password spraygithub.com/0xZDH/o365spray
GAMWorkspaceCommunity admin CLI for Google Workspacegithub.com/GAM-team/GAM
SlackPirateSlackToken-driven Slack workspace dump (emtunc)github.com/emtunc/SlackPirate
slack-watchmanSlackSensitive-data scanning across Slack workspaces (PaperMtn)github.com/PaperMtn/slack-watchman
Salesforce CLI (sf)SalesforceOfficial CLI for Salesforce metadata + data queriesdeveloper.salesforce.com/tools/salesforcecli
gitleaksGitHub / gitCommitted-secret scannergithub.com/gitleaks/gitleaks
trufflehog v3GitHub / git / SaaSVerified-secret scanner across many SaaS surfaces (TruffleSecurity)github.com/trufflesecurity/trufflehog
actionlintGitHub ActionsStatic lint for workflow YAMLgithub.com/rhysd/actionlint
octosuiteGitHubPublic-repo / org / user OSINT (Bellingcat)github.com/bellingcat/octosuite

14. Reference Resources

Comprehensive Knowledge Bases

CSP-Specific Attack Research

Defense & Hardening

Cheat Sheets & Practice


15. Common Pitfalls

  • ❌ Running write operations (*Create*, *Update*, *Delete*) without explicit named-resource authorization in the RoE
  • ❌ Skipping per-region enumeration ("we tested us-east-1" — the attacker pivots to af-south-1)
  • ❌ Treating CloudTrail as authoritative without verifying it's enabled in every region and tamper-protected
  • ❌ Running tools (Prowler, ScoutSuite) at default verbosity in production — generates massive API request volume; cost + alert noise
  • ❌ Using a long-lived access key for testing — short-lived STS / Entra / GCP credentials minimize blast radius if a session is compromised
  • ❌ Mixing customer evidence into the operator's personal CSP account — use a dedicated engagement bucket / storage / GCS, separate from operator's daily-driver
  • ❌ Reporting findings without CloudTrail event IDs / Activity Log correlation IDs / Cloud Audit Log request IDs — defenders can't reproduce
  • ❌ Forgetting that some CSP services are global (IAM, Route 53, CloudFront in AWS) and don't have a region — region-filtered enumeration misses them
  • ❌ Pivoting across federated trusts (Workload Identity Federation, B2B guests, multi-tenant apps) without separate written authorization for the federated tenant
  • ❌ Leaving testing infrastructure (jump VMs, test buckets, Pacu sessions with credentials) running after the engagement

Cloud pentesting touches the highest-trust control plane in the customer's infrastructure, and CSPs have their own rules of engagement separate from the customer's. The canonical legal framework is in Legal & Ethics; this section names only the cloud-specific exposures.

  • CSP penetration-testing policy is binding even when the customer has authorized you. AWS, Azure, and GCP each maintain a public penetration-testing policy. Most current policies do not require pre-approval for testing customer-owned resources [verify 2026-04-26], but DDoS / volumetric / targeting-CSP-infrastructure / port-scan-from-CSP-against-third-party are still prohibited or require advance notice. Re-read the current policy at the start of each engagement.
  • Scope is per-CSP, per-account/subscription/project, per-region. A signed RoE that authorizes "AWS pentest" without naming the 12-digit account IDs and regions is too vague and may not cover an account discovered mid-engagement.
  • Identity-plane writes are uniquely sensitive. iam:UpdateAssumeRolePolicy, Microsoft.Authorization/roleAssignments/write, and iam.serviceAccounts.setIamPolicy can persist long after the engagement ends. Get each privileged write in writing; rotate / revert at engagement close; document the rotation in the report.
  • Customer data is regulated. Reading customer S3 / Storage / GCS objects, RDS rows, BigQuery tables — even via authorized credentials — can trigger GDPR Art. 6/9, HIPAA, PCI DSS, or sector-specific obligations. Default to metadata-only enumeration; any read of object/row/table contents needs named authorization, and PII / PHI / PCI exposure findings must be handled per Sensitive Crime Intake & Escalation if the data crosses certain thresholds.
  • Federated and cross-tenant pivots require separate authorization. A trust relationship that lets you reach a federated tenant does not authorize you to query that tenant — get its owner's written consent.
  • Short-lived credentials are still credentials. STS / Entra / GCP access tokens harvested during testing are replayable for 1h-12h typically; treat them like long-lived secrets for the window of validity.
  • CSP marketplace and third-party SaaS are out of scope by default. A SaaS hosted on the customer's CSP that the customer doesn't own is the SaaS vendor's tenant — testing it requires that vendor's authorization, not the customer's.
  • Cost can be the finding. Cryptojacking and runaway compute cost are real outcomes of pentest mistakes — set a cost ceiling in the RoE, monitor billing alerts during testing, kill anything you don't recognize.

OPSEC framing (operator handle, sacrificial-account pool, IOC defang in the report) lives in OPSEC.


Engagement governance:

Pentesting & Security:

Analysis:


Version: 1.0 · Last Updated: 2026-04-27 · Review Frequency: Quarterly (fast-rotating CSP API surface and identity-plane primitives)