gcp-eventarc-execute-as-via-redirect

Update an existing Eventarc trigger's destination to point at attacker-controlled or higher-privilege compute, then wait for a natural event to fire, gaining execution as the new destination's runtime SA.

derived gcp emits CanExecuteAs

match

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

{'principal': None} CanModifyConfiguration {'trigger': None} {'trigger': None} CanTrigger {'consumer': None} {'consumer': None} ExecutesAs {'sa': None}

where

node_type(?trigger) == EventRule node_class(?consumer) == Compute or node_type(?consumer) == Workflow ?sa.privilege_level >= ?principal.privilege_level

emit

source typeIdentity
target typeIdentity
source<principal>
target<consumer's runtime service account>
permissionseventarc.triggers.update iam.serviceAccounts.actAs?
conditionstrigger_exists role_compatibility
state logicCONDITIONAL(trigger_exists) — the trigger update is immediate (ACTIVE once update succeeds) but the consumer only executes when the next matching event arrives from the (unchanged) event source. The ACTIVE gate requires both: (a) CanModifyConfiguration(trigger) is ACTIVE and (b) the trigger SA holds invoker rights on the new destination. POTENTIAL(role_compatibility) when the trigger SA's invoker rights on the new destination are unconfirmed. If the trigger's event source fires frequently (e.g. high-volume Audit Log activity), CONDITIONAL is nearly equivalent to ACTIVE in practice.

Narrative

{principal.name} can update trigger {trigger.name} (eventarc.triggers.update) to redirect its event delivery to {consumer.name}; the next matching natural event causes {consumer.name} to run as {sa.name} with an attacker-influenced payload.

Raw rule rules/derived/gcp/eventarc.yaml

id: gcp-eventarc-execute-as-via-redirect
emits: CanExecuteAs
description: Update an existing Eventarc trigger's destination to point at attacker-controlled or higher-privilege
  compute, then wait for a natural event to fire, gaining execution as the new destination's runtime SA.
match:
- - principal: null
  - CanModifyConfiguration
  - trigger: null
- - trigger: null
  - CanTrigger
  - consumer: null
- - consumer: null
  - ExecutesAs
  - sa: null
where:
- node_type(?trigger) == EventRule
- node_class(?consumer) == Compute or node_type(?consumer) == Workflow
- ?sa.privilege_level >= ?principal.privilege_level
emit:
  source_type: Identity
  target_type: Identity
  source: <principal>
  target: <consumer's runtime service account>
  permissions:
  - eventarc.triggers.update
  - iam.serviceAccounts.actAs?
  conditions:
  - trigger_exists
  - role_compatibility
  state_logic: "CONDITIONAL(trigger_exists) \u2014 the trigger update is immediate (ACTIVE once update\
    \ succeeds) but the consumer only executes when the next matching event arrives from the (unchanged)\
    \ event source. The ACTIVE gate requires both: (a) CanModifyConfiguration(trigger) is ACTIVE and (b)\
    \ the trigger SA holds invoker rights on the new destination. POTENTIAL(role_compatibility) when the\
    \ trigger SA's invoker rights on the new destination are unconfirmed. If the trigger's event source\
    \ fires frequently (e.g. high-volume Audit Log activity), CONDITIONAL is nearly equivalent to ACTIVE\
    \ in practice."
  confidence: min(contributing_confidences) * 0.85
  derived_from:
  - ?principal CanModifyConfiguration ?trigger (gcp-eventarc-update-trigger)
  - ?trigger CanTrigger ?consumer (gcp-eventarc-trigger-routes-to-consumer)
  - ?consumer ExecutesAs ?sa (from consumer's rule file)
  false_positive_note: "Destination redirect is the strongest Eventarc escalation because it hijacks an\
    \ existing live event stream (no attacker-controlled publish needed). Key gates: (1) the trigger SA\
    \ must have invoker rights on the NEW destination (roles/run.invoker / roles/workflows.invoker); if\
    \ not, delivery fails \u2014 downgrade to POTENTIAL(role_compatibility). (2) If the trigger SA must\
    \ also be changed (the existing SA lacks invoker on the new destination), actAs on the new trigger\
    \ SA is required. (3) The redirected consumer must be attacker-controlled or must run as a SA the\
    \ attacker wants to execute as (modeling requires knowing the new destination's ExecutesAs edge).\
    \ De-dupes with messaging-chains messaging-trigger-executes-as on (CanExecuteAs, ?principal, ?sa)."
  narrative: '{principal.name} can update trigger {trigger.name} (eventarc.triggers.update) to redirect
    its event delivery to {consumer.name}; the next matching natural event causes {consumer.name} to run
    as {sa.name} with an attacker-influenced payload.'
move · open · esc close