aws-ecr-registry-replication-cross-account-trust
A registry-level replication policy allows an external account's repository to replicate images into the victim's registry, enabling cross-account supply-chain poisoning via the replication mechanism.
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']
?registry_policy is the account-level registry policy of ?repository.account (via ecr:GetRegistryPolicy)
?registry_policy grants ecr:ReplicateImage (or implied by ecr:*) to a principal in a different account
emit
| source type | ResourcePolicy |
|---|---|
| target type | ExternalIdentity |
| source | ?registry_policy |
| target | <external account named in the registry's replication policy> |
| permissions | ecr:PutRegistryPolicy ecr:PutReplicationConfiguration |
| conditions | resource_policy |
| state logic | ACTIVE when the registry-level policy grants cross-account replication to an external account and the policy is not overridden by an SCP or deny policy. CONDITIONAL(resource_policy) when the policy includes a Condition gate (e.g. aws:PrincipalOrgID) — the cross-account replication is then conditional on those conditions holding. BLOCKED when an SCP or deny assignment blocks cross-account replication. |
Narrative
{registry_policy.name} (the account-level registry policy of account {repository.account}) grants cross-account replication to {external.name} in account {external.account}. If the external account's repository is attacker-controlled, they can replicate poisoned images into {repository.account}'s registry, where downstream workloads will pull and execute them.
Raw rule rules/derived/aws/ecr.yaml
id: aws-ecr-registry-replication-cross-account-trust
emits: CrossAccountTrust
description: A registry-level replication policy allows an external account's repository to replicate
images into the victim's registry, enabling cross-account supply-chain poisoning via the replication
mechanism.
match:
- - repository: null
- HasPolicy
- registry_policy: null
where:
- node_type(?repository) == ContainerRegistry
- ?repository.provider_type in ['AWS::ECR::Repository']
- ?registry_policy is the account-level registry policy of ?repository.account (via ecr:GetRegistryPolicy)
- ?registry_policy grants ecr:ReplicateImage (or implied by ecr:*) to a principal in a different account
emit:
source_type: ResourcePolicy
target_type: ExternalIdentity
source: ?registry_policy
target: <external account named in the registry's replication policy>
permissions:
- ecr:PutRegistryPolicy
- ecr:PutReplicationConfiguration
conditions:
- resource_policy
state_logic: "ACTIVE when the registry-level policy grants cross-account replication to an external\
\ account and the policy is not overridden by an SCP or deny policy. CONDITIONAL(resource_policy)\
\ when the policy includes a Condition gate (e.g. aws:PrincipalOrgID) \u2014 the cross-account replication\
\ is then conditional on those conditions holding. BLOCKED when an SCP or deny assignment blocks cross-account\
\ replication."
confidence: 0.88
derived_from:
- ?repository HasPolicy ?registry_policy (account-level registry policy grants cross-account replication)
false_positive_note: "Replication must cross account boundaries to constitute a CrossAccountTrust \u2014\
\ same-account replication does not emit this edge. Validate that the principal named in the policy\
\ is in a different account from the repository's account. Honor Condition gates: if the policy restricts\
\ replication to members of an org via aws:PrincipalOrgID, mark as CONDITIONAL(condition_expression)."
narrative: '{registry_policy.name} (the account-level registry policy of account {repository.account})
grants cross-account replication to {external.name} in account {external.account}. If the external
account''s repository is attacker-controlled, they can replicate poisoned images into {repository.account}''s
registry, where downstream workloads will pull and execute them.'