aws-ecr-push-can-modify-code
An IAM principal with effective ecr:PutImage (+ layer-upload actions) on a repository can push/overwrite a mutable-tag image, injecting attacker code into every consumer that re-pulls that tag.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
where
node_type(?repository) ==
ContainerRegistry
?repository.provider_type in ['AWS::ECR::Repository', 'AWS::ECR::PublicRepository']
effective_permission(?principal, 'ecr:PutImage', ?repository) == true
effective_permission(?principal, 'ecr:GetAuthorizationToken', '*') == true
effective_permission(?principal, 'ecr:InitiateLayerUpload', ?repository) == true
effective_permission(?principal, 'ecr:CompleteLayerUpload', ?repository) == true
emit
| source type | Identity |
|---|---|
| target type | ContainerRegistry |
| source | ?principal |
| target | ?repository |
| permissions | ecr:GetAuthorizationToken ecr:BatchCheckLayerAvailability ecr:InitiateLayerUpload ecr:UploadLayerPart ecr:CompleteLayerUpload ecr:PutImage |
| conditions | iam_permission resource_policy |
| state logic | ACTIVE when ?repository.imageTagMutability == MUTABLE and the principal has all push actions (GetAuthorizationToken registry-wide, PutImage + layer-upload actions on the repository ARN) as effective permissions (identity policy OR repository resource policy grant, not denied by SCP or permission boundary). BLOCKED when ?repository.imageTagMutability == IMMUTABLE — existing tags cannot be overwritten; downgrade to CONDITIONAL(image_tag_mutability) if the attacker also holds ecr:PutImageTagMutability (see aws-ecr-mutability-unblock). CONDITIONAL(resource_policy) when push is granted only via the repository resource policy and the principal's identity policy does not independently authorize it (valid but requires the policy to remain in place). BLOCKED when an SCP or permission boundary denies ecr:PutImage or ecr:GetAuthorizationToken on the relevant scope. |
Narrative
{principal.name} can push an image to {repository.name} (ecr:PutImage + ecr:GetAuthorizationToken + layer-upload actions). Overwriting a mutable tag injects attacker-controlled layers into every workload that re-pulls that tag (container-chains image-push-poisons-consumers propagates this to each consumer).
Raw rule rules/derived/aws/ecr.yaml
id: aws-ecr-push-can-modify-code
emits: CanModifyCode
description: An IAM principal with effective ecr:PutImage (+ layer-upload actions) on a repository can
push/overwrite a mutable-tag image, injecting attacker code into every consumer that re-pulls that tag.
match:
- - principal: null
- HasPermission
- repository: null
where:
- node_type(?repository) == ContainerRegistry
- ?repository.provider_type in ['AWS::ECR::Repository', 'AWS::ECR::PublicRepository']
- effective_permission(?principal, 'ecr:PutImage', ?repository) == true
- effective_permission(?principal, 'ecr:GetAuthorizationToken', '*') == true
- effective_permission(?principal, 'ecr:InitiateLayerUpload', ?repository) == true
- effective_permission(?principal, 'ecr:CompleteLayerUpload', ?repository) == true
emit:
source_type: Identity
target_type: ContainerRegistry
source: ?principal
target: ?repository
permissions:
- ecr:GetAuthorizationToken
- ecr:BatchCheckLayerAvailability
- ecr:InitiateLayerUpload
- ecr:UploadLayerPart
- ecr:CompleteLayerUpload
- ecr:PutImage
conditions:
- iam_permission
- resource_policy
state_logic: "ACTIVE when ?repository.imageTagMutability == MUTABLE and the principal has all push actions\
\ (GetAuthorizationToken registry-wide, PutImage + layer-upload actions on the repository ARN) as\
\ effective permissions (identity policy OR repository resource policy grant, not denied by SCP or\
\ permission boundary). BLOCKED when ?repository.imageTagMutability == IMMUTABLE \u2014 existing tags\
\ cannot be overwritten; downgrade to CONDITIONAL(image_tag_mutability) if the attacker also holds\
\ ecr:PutImageTagMutability (see aws-ecr-mutability-unblock). CONDITIONAL(resource_policy) when push\
\ is granted only via the repository resource policy and the principal's identity policy does not\
\ independently authorize it (valid but requires the policy to remain in place). BLOCKED when an SCP\
\ or permission boundary denies ecr:PutImage or ecr:GetAuthorizationToken on the relevant scope."
confidence: 0.95
derived_from:
- effective_permission(?principal, ecr:PutImage, ?repository)
- effective_permission(?principal, ecr:GetAuthorizationToken, *)
- effective_permission(?principal, ecr:InitiateLayerUpload, ?repository)
- effective_permission(?principal, ecr:CompleteLayerUpload, ?repository)
false_positive_note: "PutImage is the controlling action \u2014 the mere presence of layer-upload actions\
\ without PutImage does NOT constitute CanModifyCode (the manifest is never written). GetAuthorizationToken\
\ alone does NOT constitute CanModifyCode. For imageTagMutability == IMMUTABLE repositories, emit\
\ state: BLOCKED for existing tags; new (never-pushed) tags can still be created, but ONLY reach consumers\
\ whose image reference resolves to that new tag \u2014 effectively no consumer is affected unless\
\ the consumer's spec changes. Always scope to the exact repository ARN; a wildcard grant on one repo\
\ does not imply push to another. Honor SCP / permission boundary denials: a deny on ecr:PutImage\
\ at the OU/account level blocks the push path entirely."
narrative: '{principal.name} can push an image to {repository.name} (ecr:PutImage + ecr:GetAuthorizationToken
+ layer-upload actions). Overwriting a mutable tag injects attacker-controlled layers into every workload
that re-pulls that tag (container-chains image-push-poisons-consumers propagates this to each consumer).'