gcp-cloudbuild-trigger-create
cloudbuild.triggers.create + iam.serviceAccounts.actAs: create a new trigger whose builds execute attacker-controlled steps as a chosen SA.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
where
?principal has effective cloudbuild.triggers.create on the project
node_type(?sa) in [
ServiceAccount, MachineIdentity]
emit
| source type | Identity |
|---|---|
| target type | Compute |
| source | <principal> |
| target | <new Cloud Build trigger (BuildWorker) bound to ?sa> |
| permissions | cloudbuild.triggers.create iam.serviceAccounts.actAs |
| conditions | iam_permission |
| state logic | ACTIVE for the code-injection capability itself (the trigger spec is wholly under attacker control). The downstream CanExecuteAs from cicd-chains is ACTIVE if the attacker can also fire the trigger (has cloudbuild.triggers.run or can push to the tracked branch or has Pub/Sub publish on the trigger topic) or CONDITIONAL(run_path) if a fire mechanism must first be established. Inherits from CanPassIdentity; BLOCKED if a deny policy applies to either permission. |
Narrative
{principal.name} can create a new Cloud Build trigger (cloudbuild.triggers.create) with arbitrary build steps, bound to {sa.name} (iam.serviceAccounts.actAs); the next trigger fire executes attacker steps as {sa.name}.
Raw rule rules/derived/gcp/cloudbuild.yaml
id: gcp-cloudbuild-trigger-create
emits: CanModifyCode
description: 'cloudbuild.triggers.create + iam.serviceAccounts.actAs: create a new trigger whose builds
execute attacker-controlled steps as a chosen SA.'
match:
- - ?principal
- CanPassIdentity
- ?sa
where:
- ?principal has effective cloudbuild.triggers.create on the project
- node_type(?sa) in [ServiceAccount, MachineIdentity]
emit:
source_type: Identity
target_type: Compute
source: <principal>
target: <new Cloud Build trigger (BuildWorker) bound to ?sa>
permissions:
- cloudbuild.triggers.create
- iam.serviceAccounts.actAs
conditions:
- iam_permission
state_logic: ACTIVE for the code-injection capability itself (the trigger spec is wholly under attacker
control). The downstream CanExecuteAs from cicd-chains is ACTIVE if the attacker can also fire the
trigger (has cloudbuild.triggers.run or can push to the tracked branch or has Pub/Sub publish on the
trigger topic) or CONDITIONAL(run_path) if a fire mechanism must first be established. Inherits from
CanPassIdentity; BLOCKED if a deny policy applies to either permission.
confidence: 0.92
derived_from:
- <CanPassIdentity edge_id for ?principal -> ?sa>
- <cloudbuild.triggers.create effective permission>
false_positive_note: 'This emits CanModifyCode for the NEW trigger being created (not an existing one).
The trigger node does not exist until created; emit the edge with the planned SA as the associated
ExecutesAs target, with state ACTIVE for the spec-control capability. The legacy SA path (no actAs
needed) is handled differently: if the legacy SA is used, no actAs is required but triggering still
needs a fire path.'
narrative: '{principal.name} can create a new Cloud Build trigger (cloudbuild.triggers.create) with
arbitrary build steps, bound to {sa.name} (iam.serviceAccounts.actAs); the next trigger fire executes
attacker steps as {sa.name}.'