gcp-tasks-uri-override-token-capture
Setting httpTarget.uriOverride to an attacker-controlled endpoint (via cloudtasks.queues.update, no actAs needed) redirects all future dispatches there - delivering the queue's OIDC/OAuth SA token and task body payloads to the attacker.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
where
node_type(?queue) ==
Queue AND provider_type(?queue) == google.cloud.tasks.v2.Queue
?queue.httpTarget.oidcToken.serviceAccountEmail is non-empty (queue has an OIDC SA)
?principal can set httpTarget.uriOverride via cloudtasks.queues.update
emit
| source type | Messaging |
|---|---|
| target type | Identity |
| source | <Cloud Tasks queue (Messaging)> |
| target | <configured OIDC/OAuth SA> |
| permissions | cloudtasks.queues.update |
| conditions | iam_permission trigger_exists |
| state logic | ACTIVE when the principal has cloudtasks.queues.update on the queue AND the queue has an OIDC/OAuth SA configured AND the queue state is RUNNING (or can be resumed). CONDITIONAL(trigger_exists) when the queue is PAUSED or no tasks are scheduled — the redirect is in place but tokens are only delivered when dispatch occurs. No actAs is required to set uriOverride — this is the barrier-free token-capture path. |
Narrative
{principal.name} can update {queue.name}'s httpTarget.uriOverride to an attacker-controlled endpoint (cloudtasks.queues.update, no actAs needed). All future dispatches redirect there, delivering the OIDC/OAuth token for {sa.name} and task body payloads - capturing short-lived {sa.name} credentials.
Raw rule rules/derived/gcp/tasks.yaml
id: gcp-tasks-uri-override-token-capture
emits: ExposesCredential
description: "Setting httpTarget.uriOverride to an attacker-controlled endpoint (via cloudtasks.queues.update,\
\ no actAs needed) redirects all future dispatches there \u2014 delivering the queue's OIDC/OAuth SA\
\ token and task body payloads to the attacker."
match:
- - ?principal
- CanModifyConfiguration
- ?queue
- - ?queue
- ExposesCredential
- ?sa
where:
- node_type(?queue) == Queue AND provider_type(?queue) == google.cloud.tasks.v2.Queue
- ?queue.httpTarget.oidcToken.serviceAccountEmail is non-empty (queue has an OIDC SA)
- ?principal can set httpTarget.uriOverride via cloudtasks.queues.update
emit:
source_type: Messaging
target_type: Identity
source: <Cloud Tasks queue (Messaging)>
target: <configured OIDC/OAuth SA>
permissions:
- cloudtasks.queues.update
conditions:
- iam_permission
- trigger_exists
state_logic: "ACTIVE when the principal has cloudtasks.queues.update on the queue AND the queue has\
\ an OIDC/OAuth SA configured AND the queue state is RUNNING (or can be resumed). CONDITIONAL(trigger_exists)\
\ when the queue is PAUSED or no tasks are scheduled \u2014 the redirect is in place but tokens are\
\ only delivered when dispatch occurs. No actAs is required to set uriOverride \u2014 this is the\
\ barrier-free token-capture path."
confidence: min(contributing_confidences) * 0.95
derived_from:
- ?principal CanModifyConfiguration ?queue (gcp-tasks-update-queue-config)
- ?queue ExposesCredential ?sa (gcp-tasks-queue-exposes-oidc-sa)
false_positive_note: "httpTarget.uriOverride only affects HTTP target queues \u2014 not App Engine queues.\
\ The redirect captures the token at delivery time; the attacker must control an HTTPS endpoint to\
\ receive the dispatch. Org policy constraints/cloudtasks.allowedIngress may restrict which URI schemes/hosts\
\ are permitted as httpTarget values \u2014 check org policy before marking ACTIVE. This edge expresses\
\ the token-capture capability; the CanReadSecret consequence (the captured token is CredentialsFor\
\ the SA) is handled by credential-chains."
narrative: "{principal.name} can update {queue.name}'s httpTarget.uriOverride to an attacker-controlled\
\ endpoint (cloudtasks.queues.update, no actAs needed). All future dispatches redirect there, delivering\
\ the OIDC/OAuth token for {sa.name} and task body payloads \u2014 capturing short-lived {sa.name}\
\ credentials."