aws-ecr-cross-account-push-can-modify-code

An external principal granted ecr:PutImage via the repository's resource policy can push images (cross-account supply-chain attack).

derived aws emits CanModifyCode

match

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

{'resource_policy': None} CrossAccountTrust {'external': None}

where

node_type(?resource_policy) == ResourcePolicy ?resource_policy is a policy of a ContainerRegistry (?repository) ?resource_policy grants ecr:PutImage (directly or via ecr:*) to ?external ?external.account != ?repository.account

emit

source typeIdentity
target typeContainerRegistry
source?external
target?repository
permissionsecr:PutImage ecr:BatchCheckLayerAvailability ecr:InitiateLayerUpload ecr:UploadLayerPart ecr:CompleteLayerUpload
conditionsresource_policy
state logicInherit state from the matched CrossAccountTrust. ACTIVE when the resource policy statement granting PutImage to the external principal is present and not blocked by an SCP in the owning account's organization. CONDITIONAL(resource_policy) when the policy includes a Condition gate (aws:SourceArn, aws:PrincipalOrgID, etc.) — the cross-account push is then conditional on those conditions holding. BLOCKED when an SCP denies cross-account ECR push to this external principal.

Narrative

{external.name} (account {external.account}) is explicitly granted ecr:PutImage to {repository.name} in account {repository.account} via the repository's resource policy. The external principal can push images into this repository, poisoning every workload that re-pulls from it (container-chains propagates this to each consumer in the owning account).

Raw rule rules/derived/aws/ecr.yaml

id: aws-ecr-cross-account-push-can-modify-code
emits: CanModifyCode
description: An external principal granted ecr:PutImage via the repository's resource policy can push
  images (cross-account supply-chain attack).
match:
- - resource_policy: null
  - CrossAccountTrust
  - external: null
where:
- node_type(?resource_policy) == ResourcePolicy
- ?resource_policy is a policy of a ContainerRegistry (?repository)
- ?resource_policy grants ecr:PutImage (directly or via ecr:*) to ?external
- ?external.account != ?repository.account
emit:
  source_type: Identity
  target_type: ContainerRegistry
  source: ?external
  target: ?repository
  permissions:
  - ecr:PutImage
  - ecr:BatchCheckLayerAvailability
  - ecr:InitiateLayerUpload
  - ecr:UploadLayerPart
  - ecr:CompleteLayerUpload
  conditions:
  - resource_policy
  state_logic: "Inherit state from the matched CrossAccountTrust. ACTIVE when the resource policy statement\
    \ granting PutImage to the external principal is present and not blocked by an SCP in the owning account's\
    \ organization. CONDITIONAL(resource_policy) when the policy includes a Condition gate (aws:SourceArn,\
    \ aws:PrincipalOrgID, etc.) \u2014 the cross-account push is then conditional on those conditions\
    \ holding. BLOCKED when an SCP denies cross-account ECR push to this external principal."
  confidence: min(contributing_confidences) * 0.92
  derived_from:
  - ?resource_policy CrossAccountTrust ?external (from aws-ecr-cross-account-push)
  - ?resource_policy grants ecr:PutImage in its statement
  false_positive_note: "This rule bridges the CrossAccountTrust fact to CanModifyCode so that container-chains\
    \ image-push-poisons-consumers can consume it and fan out to consumers. Do not emit if the policy\
    \ grants only pull (BatchGetImage + GetDownloadUrlForLayer) without PutImage \u2014 that is pull-only\
    \ trust, not push trust."
  narrative: '{external.name} (account {external.account}) is explicitly granted ecr:PutImage to {repository.name}
    in account {repository.account} via the repository''s resource policy. The external principal can
    push images into this repository, poisoning every workload that re-pulls from it (container-chains
    propagates this to each consumer in the owning account).'
move · open · esc close