gcp-cloudbuild-trigger-create

cloudbuild.triggers.create + iam.serviceAccounts.actAs: create a new trigger whose builds execute attacker-controlled steps as a chosen SA.

derived gcp emits CanModifyCode

match

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

?principal CanPassIdentity ?sa

where

?principal has effective cloudbuild.triggers.create on the project node_type(?sa) in [ServiceAccount, MachineIdentity]

emit

source typeIdentity
target typeCompute
source<principal>
target<new Cloud Build trigger (BuildWorker) bound to ?sa>
permissionscloudbuild.triggers.create iam.serviceAccounts.actAs
conditionsiam_permission
state logicACTIVE 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}.'
move · open · esc close