gcp-pubsub-create-push-subscription-can-pass-identity
pubsub.subscriptions.create (or pubsub.subscriptions.update) + iam.serviceAccounts.actAs on a push SA allows creating a push subscription that delivers the SA's OIDC token to a push endpoint, attaching the SA's identity to the event source.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
{'match_effective_permission': {'action': 'pubsub.subscriptions.create', 'resource_type': 'google.pubsub.Topic'}}
{'match_effective_permission': {'action': 'iam.serviceAccounts.actAs', 'resource_type': 'google.iam.ServiceAccount'}}
where
both permissions held by ?principal
?sa is the target of iam.serviceAccounts.actAs
emit
| source type | Identity |
|---|---|
| target type | ServiceAccount |
| source | <principal> |
| target | <push service account> |
| permissions | pubsub.subscriptions.create iam.serviceAccounts.actAs |
| conditions | iam_permission role_compatibility |
| state logic | ACTIVE when both pubsub.subscriptions.create (or pubsub.subscriptions.update) and iam.serviceAccounts.actAs on the target SA are effectively granted and no deny policy blocks. CONDITIONAL(role_compatibility) when the actAs scope or role compatibility to the push SA is not confirmed. BLOCKED by IAM deny on either permission or an org policy that restricts actAs usage. |
Narrative
{principal.name} can create a Pub/Sub push subscription (pubsub.subscriptions.create) with pushConfig.oidcToken.serviceAccountEmail set to {sa.name} and holds iam.serviceAccounts.actAs on {sa.name}, enabling delivery of {sa.name}'s OIDC token to an attacker-controlled push endpoint for extraction and replay.
Raw rule rules/derived/gcp/pubsub.yaml
id: gcp-pubsub-create-push-subscription-can-pass-identity
emits: CanPassIdentity
description: pubsub.subscriptions.create (or pubsub.subscriptions.update) + iam.serviceAccounts.actAs
on a push SA allows creating a push subscription that delivers the SA's OIDC token to a push endpoint,
attaching the SA's identity to the event source.
match:
- match_effective_permission:
action: pubsub.subscriptions.create
resource_type: google.pubsub.Topic
- match_effective_permission:
action: iam.serviceAccounts.actAs
resource_type: google.iam.ServiceAccount
where:
- both permissions held by ?principal
- ?sa is the target of iam.serviceAccounts.actAs
emit:
source_type: Identity
target_type: ServiceAccount
source: <principal>
target: <push service account>
permissions:
- pubsub.subscriptions.create
- iam.serviceAccounts.actAs
conditions:
- iam_permission
- role_compatibility
state_logic: ACTIVE when both pubsub.subscriptions.create (or pubsub.subscriptions.update) and iam.serviceAccounts.actAs
on the target SA are effectively granted and no deny policy blocks. CONDITIONAL(role_compatibility)
when the actAs scope or role compatibility to the push SA is not confirmed. BLOCKED by IAM deny on
either permission or an org policy that restricts actAs usage.
confidence: 0.85
derived_from:
- 'effective-permission: pubsub.subscriptions.create (or .update) on Topic'
- 'effective-permission: iam.serviceAccounts.actAs on ServiceAccount'
false_positive_note: "The Pub/Sub Service Agent (service-<project-number>@gcp-sa-pubsub.iam.gserviceaccount.com)\
\ must also hold iam.serviceAccounts.actAs on the push SA at delivery time \u2014 if the Service Agent\
\ binding is absent or revoked, push delivery fails even if the creator has actAs. Do not emit unless\
\ both permissions are confirmed on the same principal. The CanPassIdentity edge feeds credential-chains\
\ when combined with ExposesCredential(subscription \u2192 push SA) and CredentialsFor(token \u2192\
\ push SA)."
narrative: '{principal.name} can create a Pub/Sub push subscription (pubsub.subscriptions.create) with
pushConfig.oidcToken.serviceAccountEmail set to {sa.name} and holds iam.serviceAccounts.actAs on {sa.name},
enabling delivery of {sa.name}''s OIDC token to an attacker-controlled push endpoint for extraction
and replay.'