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.

derived aws emits CanExecuteAs

match

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

{'principal': None} CanModifyCode {'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} CanTrigger {'pipeline': None}

emit

source typeIdentity
target typeMachineIdentity
source?principal
target?role
permissionsimagebuilder:CreateComponent imagebuilder:CreateContainerRecipe imagebuilder:UpdateImagePipeline imagebuilder:StartImagePipelineExecution?
conditionsrun_path
state logicACTIVE 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.'
move · open · esc close