gcp-deploy-attach-then-execute-as

Re-pointing a target's executionConfigs SA (targets.update + actAs) then promoting a rollout yields code execution as the newly attached SA.

derived gcp emits CanExecuteAs

match

A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.

{'principal': None} CanAttachIdentity {'target_worker': None} {'target_worker': None} ExecutesAs {'exec_sa': None}

where

node_type(?target_worker) == BuildWorker ?target_worker is a Cloud Deploy job run for the updated target ?principal holds clouddeploy.targets.update AND CanPassIdentity(?principal, ?exec_sa)

optional

Present → upgrade the emitted state; absent → downgrade (not a hard requirement).

{'principal': None} CanTrigger {'target_worker': None}

emit

source typeIdentity
target typeServiceAccount
source?principal
target?exec_sa
permissionsclouddeploy.targets.update iam.serviceAccounts.actAs clouddeploy.rollouts.create
conditionsiam_permission
state logicCONDITIONAL(run_path) — re-pointing the SA does not immediately run code; the attacker must also trigger a rollout (rollouts.create or an existing automation). ACTIVE when ?principal also holds clouddeploy.rollouts.create + actAs on the new exec SA (full chain: update target SA, then promote). Inherits weakest from CanAttachIdentity and ExecutesAs contributors.

Narrative

{principal.name} can update target {target.name}'s execution SA to {exec_sa.name} (clouddeploy.targets.update + iam.serviceAccounts.actAs), then promote a release (clouddeploy.rollouts.create) to execute code as {exec_sa.name}. Cite can-execute-as execute-as-via-config-identity-swap for the canonical collapse.

Raw rule rules/derived/gcp/clouddeploy.yaml

id: gcp-deploy-attach-then-execute-as
emits: CanExecuteAs
description: Re-pointing a target's executionConfigs SA (targets.update + actAs) then promoting a rollout
  yields code execution as the newly attached SA.
applies_to:
- gcp
match:
- - principal: null
  - CanAttachIdentity
  - target_worker: null
- - target_worker: null
  - ExecutesAs
  - exec_sa: null
where:
- node_type(?target_worker) == BuildWorker
- ?target_worker is a Cloud Deploy job run for the updated target
- ?principal holds clouddeploy.targets.update AND CanPassIdentity(?principal, ?exec_sa)
optional:
- - principal: null
  - CanTrigger
  - target_worker: null
emit:
  source_type: Identity
  target_type: ServiceAccount
  source: ?principal
  target: ?exec_sa
  permissions:
  - clouddeploy.targets.update
  - iam.serviceAccounts.actAs
  - clouddeploy.rollouts.create
  conditions:
  - iam_permission
  state_logic: "CONDITIONAL(run_path) \u2014 re-pointing the SA does not immediately run code; the attacker\
    \ must also trigger a rollout (rollouts.create or an existing automation). ACTIVE when ?principal\
    \ also holds clouddeploy.rollouts.create + actAs on the new exec SA (full chain: update target SA,\
    \ then promote). Inherits weakest from CanAttachIdentity and ExecutesAs contributors."
  confidence: 0.88
  derived_from:
  - ?principal CanAttachIdentity ?target_worker (gcp-deploy-target-update-attach-identity)
  - ?target_worker ExecutesAs ?exec_sa (gcp-deploy-executes-as)
  - can-execute-as execute-as-via-config-identity-swap
  false_positive_note: "This is the Cloud Deploy analog of the execute-as-via-config-identity-swap canonical\
    \ rule. The swap (targets.update) arms future rollouts; the execution requires a run path (rollouts.create\
    \ or automation). Without a run path this stays CONDITIONAL(run_path). can-execute-as execute-as-via-config-identity-swap\
    \ dedupes on (CanExecuteAs, ?principal, ?exec_sa) \u2014 emit once."
  narrative: '{principal.name} can update target {target.name}''s execution SA to {exec_sa.name} (clouddeploy.targets.update
    + iam.serviceAccounts.actAs), then promote a release (clouddeploy.rollouts.create) to execute code
    as {exec_sa.name}. Cite can-execute-as execute-as-via-config-identity-swap for the canonical collapse.'
move · open · esc close