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.
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 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) — 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.'