aws-imagebuilder-container-recipe-poisons-ecr
Modify an Image Builder container recipe (or the pipeline referencing it) and trigger a build to push a poisoned container image to the target ECR repository, feeding the container-chains supply-chain fan-out to all downstream consumers.
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'
?pipeline.containerRecipeArn != null # pipeline uses a container recipe, not an AMI recipe
?ecr_repository is the targetRepository of the container recipe used by ?pipeline
node_type(?ecr_repository) == ContainerRegistry
emit
| source type | Identity |
|---|---|
| target type | ContainerRegistry |
| source | ?principal |
| target | ?ecr_repository |
| permissions | imagebuilder:CreateComponent imagebuilder:CreateContainerRecipe imagebuilder:UpdateImagePipeline imagebuilder:StartImagePipelineExecution? |
| conditions | run_path |
| state logic | Inherit state from the contributing CanModifyCode(principal -> pipeline): ACTIVE if that edge is ACTIVE (run path exists + definition is modifiable); CONDITIONAL(run_path) if the modification is planted but no run path fires the build; BLOCKED if the pipeline CanModifyCode is BLOCKED. container-chains image-push-poisons-consumers (cited, not re-derived) then propagates this CanModifyCode to each downstream consumer of the ECR tag, and poisoned-image-executes-as (cited) produces CanExecuteAs the consumer's identity. This rule only emits the ECR CanModifyCode fact that container-chains needs. |
Narrative
{principal.name} can modify the container recipe of {pipeline.name} and trigger a build, causing Image Builder to push a poisoned OCI image to {ecr_repository.name}. Every workload (ECS task, EKS pod, Lambda container) that re-pulls the affected tag executes attacker-controlled layers (container-chains image-push-poisons-consumers fans out; poisoned-image-executes-as produces the CanExecuteAs each workload's identity).
Raw rule rules/derived/aws/imagebuilder.yaml
id: aws-imagebuilder-container-recipe-poisons-ecr
emits: CanModifyCode
description: Modify an Image Builder container recipe (or the pipeline referencing it) and trigger a build
to push a poisoned container image to the target ECR repository, feeding the container-chains supply-chain
fan-out to all downstream consumers.
match:
- - principal: null
- CanModifyCode
- pipeline: null
where:
- node_type(?pipeline) == BuildWorker
- ?pipeline.provider_type == 'AWS::ImageBuilder::ImagePipeline'
- '?pipeline.containerRecipeArn != null # pipeline uses a container recipe, not an AMI recipe'
- ?ecr_repository is the targetRepository of the container recipe used by ?pipeline
- node_type(?ecr_repository) == ContainerRegistry
emit:
source_type: Identity
target_type: ContainerRegistry
source: ?principal
target: ?ecr_repository
permissions:
- imagebuilder:CreateComponent
- imagebuilder:CreateContainerRecipe
- imagebuilder:UpdateImagePipeline
- imagebuilder:StartImagePipelineExecution?
conditions:
- run_path
state_logic: 'Inherit state from the contributing CanModifyCode(principal -> pipeline): ACTIVE if that
edge is ACTIVE (run path exists + definition is modifiable); CONDITIONAL(run_path) if the modification
is planted but no run path fires the build; BLOCKED if the pipeline CanModifyCode is BLOCKED. container-chains
image-push-poisons-consumers (cited, not re-derived) then propagates this CanModifyCode to each downstream
consumer of the ECR tag, and poisoned-image-executes-as (cited) produces CanExecuteAs the consumer''s
identity. This rule only emits the ECR CanModifyCode fact that container-chains needs.'
confidence: min(contributing_confidences) * 0.92
derived_from:
- ?principal CanModifyCode ?pipeline (imagebuilder rules 1-2, including container-recipe path)
- ?pipeline ContainerRecipe.targetRepository -> ?ecr_repository
- "container-chains image-push-poisons-consumers (cited \u2014 propagates to consumers)"
- "container-chains poisoned-image-executes-as (cited \u2014 produces CanExecuteAs consumer identity)"
false_positive_note: "Only emit when the pipeline uses a CONTAINER recipe (containerRecipeArn set),\
\ not an AMI recipe. The build pushes to the targetRepository tag specified in the container recipe;\
\ if the tag is immutable in the ECR repository, the push will fail \u2014 downgrade the CanModifyCode\
\ to CONDITIONAL(image_tag_mutability). Verify the targetRepository ARN resolves to a real ContainerRegistry\
\ node. container-chains handles the fan-out; do not re-derive which downstream workloads are affected\
\ here. The CanModifyCode edge should come from either aws-imagebuilder-modify-component-executes-as\
\ (rule 1, for component paths), aws-imagebuilder-update-pipeline-executes-as (rule 2, for recipe\
\ swap paths), or aws-imagebuilder-can-modify-code-container (explicit rule for CreateContainerRecipe\
\ path)."
narrative: '{principal.name} can modify the container recipe of {pipeline.name} and trigger a build,
causing Image Builder to push a poisoned OCI image to {ecr_repository.name}. Every workload (ECS task,
EKS pod, Lambda container) that re-pulls the affected tag executes attacker-controlled layers (container-chains
image-push-poisons-consumers fans out; poisoned-image-executes-as produces the CanExecuteAs each workload''s
identity).'