aws-imagebuilder-modify-component-executes-as
Create a new malicious component version, update the pipeline to reference it, and trigger a build to execute shell commands as the build instance's IAM role.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
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}
CanTrigger {'pipeline': None}
emit
| source type | Identity |
|---|---|
| target type | MachineIdentity |
| source | ?principal |
| target | ?role |
| permissions | imagebuilder:CreateComponent imagebuilder:CreateContainerRecipe imagebuilder:UpdateImagePipeline imagebuilder:StartImagePipelineExecution? |
| conditions | run_path |
| state logic | ACTIVE if a run path is established — the attacker can also trigger the pipeline (matched(optional[0]): has imagebuilder:StartImagePipelineExecution) OR the pipeline has a schedule that will fire after the recipe/component update (schedule present and not disabled). Otherwise CONDITIONAL(run_path): the malicious component is planted but the build will not run until a trigger or schedule fires. Inherit weakest from CanModifyCode (which is itself CONDITIONAL(run_path) if no trigger exists) and ExecutesAs (always ACTIVE for a running pipeline). A branch-protection analog — e.g. an approval workflow gating pipeline execution — caps the CanModifyCode to CONDITIONAL/BLOCKED and this edge inherits it. BLOCKED when an SCP or permission boundary denies imagebuilder:CreateComponent, imagebuilder:CreateContainerRecipe, imagebuilder:UpdateImagePipeline, or imagebuilder:StartImagePipelineExecution on the effective scope. |
Narrative
{principal.name} can create a new Image Builder component version with malicious shell steps (imagebuilder:CreateComponent), update {pipeline.name} to reference it (imagebuilder:UpdateImagePipeline), and trigger a build (imagebuilder:StartImagePipelineExecution or schedule). The build instance runs as {role.name} via the SSM agent; the malicious steps execute as {role.name} and expose its credentials to the attacker.
Raw rule rules/derived/aws/imagebuilder.yaml
id: aws-imagebuilder-modify-component-executes-as
emits: CanExecuteAs
description: Create a new malicious component version, update the pipeline to reference it, and trigger
a build to execute shell commands as the build instance's IAM role.
match:
- - principal: null
- CanModifyCode
- pipeline: null
- - pipeline: null
- ExecutesAs
- role: null
where:
- node_type(?pipeline) == BuildWorker
- ?pipeline.provider_type == 'AWS::ImageBuilder::ImagePipeline'
optional:
- - principal: null
- CanTrigger
- pipeline: null
emit:
source_type: Identity
target_type: MachineIdentity
source: ?principal
target: ?role
permissions:
- imagebuilder:CreateComponent
- imagebuilder:CreateContainerRecipe
- imagebuilder:UpdateImagePipeline
- imagebuilder:StartImagePipelineExecution?
conditions:
- run_path
state_logic: "ACTIVE if a run path is established \u2014 the attacker can also trigger the pipeline\
\ (matched(optional[0]): has imagebuilder:StartImagePipelineExecution) OR the pipeline has a schedule\
\ that will fire after the recipe/component update (schedule present and not disabled). Otherwise\
\ CONDITIONAL(run_path): the malicious component is planted but the build will not run until a trigger\
\ or schedule fires. Inherit weakest from CanModifyCode (which is itself CONDITIONAL(run_path) if\
\ no trigger exists) and ExecutesAs (always ACTIVE for a running pipeline). A branch-protection analog\
\ \u2014 e.g. an approval workflow gating pipeline execution \u2014 caps the CanModifyCode to CONDITIONAL/BLOCKED\
\ and this edge inherits it. BLOCKED when an SCP or permission boundary denies imagebuilder:CreateComponent,\
\ imagebuilder:CreateContainerRecipe, imagebuilder:UpdateImagePipeline, or imagebuilder:StartImagePipelineExecution\
\ on the effective scope."
confidence: min(contributing_confidences) * 0.90
derived_from:
- ?principal CanModifyCode ?pipeline (imagebuilder:CreateComponent/CreateContainerRecipe + UpdateImagePipeline)
- ?pipeline ExecutesAs ?role (instanceProfileName -> IAM role)
- ?principal CanTrigger ?pipeline? (imagebuilder:StartImagePipelineExecution)
- cicd-chains cicd-modify-buildspec-executes-as (cited, deduped)
- can-execute-as execute-as-via-code-modify (cited, deduped)
false_positive_note: "Components are immutable per version: CreateComponent alone does NOT modify existing\
\ builds \u2014 the attacker must ALSO update the recipe or pipeline to reference the new component\
\ version AND trigger a build. For container recipes, CreateContainerRecipe similarly requires UpdateImagePipeline\
\ to point the pipeline to the new recipe version. Do not emit ACTIVE when the run path is absent\
\ (no schedule, no StartImagePipelineExecution permission). A pipeline that references only AWS-managed\
\ components (owner 'Amazon') cannot have its component mutated \u2014 only the UpdateImagePipeline\
\ path applies to swap in an attacker-authored recipe. Verify that the CanModifyCode gate confirms\
\ BOTH (CreateComponent OR CreateContainerRecipe) AND UpdateImagePipeline (or an equivalent recipe\
\ swap) before emitting."
narrative: '{principal.name} can create a new Image Builder component version with malicious shell steps
(imagebuilder:CreateComponent), update {pipeline.name} to reference it (imagebuilder:UpdateImagePipeline),
and trigger a build (imagebuilder:StartImagePipelineExecution or schedule). The build instance runs
as {role.name} via the SSM agent; the malicious steps execute as {role.name} and expose its credentials
to the attacker.'