aws-imagebuilder-update-pipeline-executes-as

Update an image pipeline to reference an attacker-controlled recipe (which may include malicious components or an S3-overwritable component URI), then trigger a build to execute as the build instance role.

derived aws emits CanExecuteAs

match

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

{'principal': None} CanModifyConfiguration {'pipeline': None} {'pipeline': None} ExecutesAs {'role': None}

where

node_type(?pipeline) == BuildWorker ?pipeline.provider_type == 'AWS::ImageBuilder::ImagePipeline'

optional

Present → upgrade the emitted state; absent → downgrade (not a hard requirement).

{'principal': None} CanCreate {'recipe': None} {'principal': None} CanModifyCode {'recipe': None} {'principal': None} CanTrigger {'pipeline': None}

emit

source typeIdentity
target typeMachineIdentity
source?principal
target?role
permissionsimagebuilder:UpdateImagePipeline imagebuilder:CreateImageRecipe imagebuilder:StartImagePipelineExecution?
conditionsrun_path attacker_controlled_recipe
state logicACTIVE if the principal can point the pipeline to an attacker-controlled recipe (matched optional[0] or optional[1]: has CanCreate or CanModifyCode on a recipe) AND a run path exists (matched optional[2]: has CanTrigger or pipeline has schedule). CONDITIONAL(attacker_controlled_recipe) when the attacker can update the pipeline but at least one optional recipe-control clause is not matched — emit CONDITIONAL until recipe control is confirmed. CONDITIONAL(run_path) when recipe is controllable but optional[2] not matched and no schedule is known. Inherit weakest from CanModifyConfiguration and ExecutesAs. BLOCKED when an SCP or permission boundary denies imagebuilder:UpdateImagePipeline.

Narrative

{principal.name} can update {pipeline.name} to reference an attacker-controlled image recipe (imagebuilder:UpdateImagePipeline + imagebuilder:CreateImageRecipe), then trigger a build (imagebuilder:StartImagePipelineExecution or schedule). The build instance runs as {role.name} and executes the attacker-authored component steps as that role.

Raw rule rules/derived/aws/imagebuilder.yaml

id: aws-imagebuilder-update-pipeline-executes-as
emits: CanExecuteAs
description: Update an image pipeline to reference an attacker-controlled recipe (which may include malicious
  components or an S3-overwritable component URI), then trigger a build to execute as the build instance
  role.
match:
- - principal: null
  - CanModifyConfiguration
  - pipeline: null
- - pipeline: null
  - ExecutesAs
  - role: null
where:
- node_type(?pipeline) == BuildWorker
- ?pipeline.provider_type == 'AWS::ImageBuilder::ImagePipeline'
optional:
- - principal: null
  - CanCreate
  - recipe: null
- - principal: null
  - CanModifyCode
  - recipe: null
- - principal: null
  - CanTrigger
  - pipeline: null
emit:
  source_type: Identity
  target_type: MachineIdentity
  source: ?principal
  target: ?role
  permissions:
  - imagebuilder:UpdateImagePipeline
  - imagebuilder:CreateImageRecipe
  - imagebuilder:StartImagePipelineExecution?
  conditions:
  - run_path
  - attacker_controlled_recipe
  state_logic: "ACTIVE if the principal can point the pipeline to an attacker-controlled recipe (matched\
    \ optional[0] or optional[1]: has CanCreate or CanModifyCode on a recipe) AND a run path exists (matched\
    \ optional[2]: has CanTrigger or pipeline has schedule). CONDITIONAL(attacker_controlled_recipe) when\
    \ the attacker can update the pipeline but at least one optional recipe-control clause is not matched\
    \ \u2014 emit CONDITIONAL until recipe control is confirmed. CONDITIONAL(run_path) when recipe is\
    \ controllable but optional[2] not matched and no schedule is known. Inherit weakest from CanModifyConfiguration\
    \ and ExecutesAs. BLOCKED when an SCP or permission boundary denies imagebuilder:UpdateImagePipeline."
  confidence: min(contributing_confidences) * 0.88
  derived_from:
  - ?principal CanModifyConfiguration ?pipeline (imagebuilder:UpdateImagePipeline)
  - ?pipeline ExecutesAs ?role (instanceProfileName -> IAM role)
  - ?principal CanCreate/CanModifyCode ?recipe (recipe control via optional[0] or [1])
  - ?principal CanTrigger ?pipeline? (imagebuilder:StartImagePipelineExecution, optional[2])
  - cicd-chains cicd-modify-buildspec-executes-as (cited, deduped)
  false_positive_note: "UpdateImagePipeline alone only swaps the recipe/infra config reference \u2014\
    \ the attacker must control the content of the recipe being pointed to. One of the optional clauses\
    \ (CanCreate or CanModifyCode on a recipe) must match to confirm recipe control; if no optional clause\
    \ matches, emit CONDITIONAL(attacker_controlled_recipe). A pipeline that requires a human approval\
    \ before execution keeps this CONDITIONAL(run_path). Dedupes with rule 1 on (CanExecuteAs, ?principal,\
    \ ?role) when both modification paths apply \u2014 emit once."
  narrative: '{principal.name} can update {pipeline.name} to reference an attacker-controlled image recipe
    (imagebuilder:UpdateImagePipeline + imagebuilder:CreateImageRecipe), then trigger a build (imagebuilder:StartImagePipelineExecution
    or schedule). The build instance runs as {role.name} and executes the attacker-authored component
    steps as that role.'
move · open · esc close