gcp-dataflow-template-poison

Writing to the GCS object that serves as a Dataflow Flex or Classic Template poisons the next job created from that template; the job runs the attacker-controlled pipeline code as the worker SA.

derived gcp emits CanModifyCode

match

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

?principal CanWriteData ?templateObject ?templateObject ContainsResourceReference ?job

where

node_type(?templateObject) == ObjectStorage # GCS object ContainsResourceReference edge is confirmed by evidence (job's gcsPath / containerSpecGcsPath field references the specific template object) ?job is a google.dataflow.Job (or job template definition) that reads the template

emit

source typeIdentity
target typeCompute
source<principal>
target<Dataflow job (BatchJob) created from the template>
permissionsstorage.objects.create
conditionsiam_permission trigger_exists
state logicCONDITIONAL(trigger_exists): the template is poisoned, but the malicious code only executes when a new job is created from that template (dataflow.jobs.create reading the GCS path). ACTIVE when a job-creation event is confirmed to use this template imminently or on a schedule. Inherits from CanWriteData on the template object; BLOCKED if VPC Service Controls prevent writes to the GCS path.

Narrative

{principal.name} can overwrite the Dataflow template GCS object {templateObject.name} (storage.objects.create) referenced by job {job.name}; the next job created from that template will run the attacker-controlled pipeline code as the worker service account.

Raw rule rules/derived/gcp/dataflow.yaml

id: gcp-dataflow-template-poison
emits: CanModifyCode
description: Writing to the GCS object that serves as a Dataflow Flex or Classic Template poisons the
  next job created from that template; the job runs the attacker-controlled pipeline code as the worker
  SA.
match:
- - ?principal
  - CanWriteData
  - ?templateObject
- - ?templateObject
  - ContainsResourceReference
  - ?job
where:
- 'node_type(?templateObject) == ObjectStorage  # GCS object'
- ContainsResourceReference edge is confirmed by evidence (job's gcsPath / containerSpecGcsPath field
  references the specific template object)
- ?job is a google.dataflow.Job (or job template definition) that reads the template
emit:
  source_type: Identity
  target_type: Compute
  source: <principal>
  target: <Dataflow job (BatchJob) created from the template>
  permissions:
  - storage.objects.create
  conditions:
  - iam_permission
  - trigger_exists
  state_logic: 'CONDITIONAL(trigger_exists): the template is poisoned, but the malicious code only executes
    when a new job is created from that template (dataflow.jobs.create reading the GCS path). ACTIVE when
    a job-creation event is confirmed to use this template imminently or on a schedule. Inherits from
    CanWriteData on the template object; BLOCKED if VPC Service Controls prevent writes to the GCS path.'
  confidence: min(contributing_confidences) * 0.85
  derived_from:
  - <CanWriteData edge_id for ?principal -> ?templateObject>
  - <ContainsResourceReference edge_id for ?templateObject -> ?job (must be evidence-confirmed)>
  false_positive_note: "STRICT: Only emit when (1) the ContainsResourceReference edge is CONFIRMED by\
    \ the collector (the job or job template definition explicitly references the specific GCS path in\
    \ its gcsPath / containerSpecGcsPath field), (2) the write is to the SPECIFIC GCS path that a job\
    \ references, not to an arbitrary bucket. Write to an unrelated bucket is not this edge. The template\
    \ poisoning only takes effect on jobs created AFTER the poisoning \u2014 existing running jobs unaffected.\
    \ Without confirmed link between template GCS path and job (often unavailable before job creation),\
    \ do not emit. This pattern is supply-chain injection; prefer citation of cicd-chains for the narrative."
  narrative: '{principal.name} can overwrite the Dataflow template GCS object {templateObject.name} (storage.objects.create)
    referenced by job {job.name}; the next job created from that template will run the attacker-controlled
    pipeline code as the worker service account.'
move · open · esc close