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.
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 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 — 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.'