# Az - Container Registry

{{#include ../../../banners/hacktricks-training.md}}

## Basic Information

Azure Container Registry (ACR) is a secure, private registry that lets you **store, manage, and access container images in the Azure cloud**. It integrates seamlessly with several Azure services, providing automated build and deployment workflows at scale. With features like geo-replication and vulnerability scanning, ACR helps ensure enterprise-grade security and compliance for containerized applications.

### Permissions

These are the **different permissions** [according to the docs](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-roles?tabs=azure-cli#access-resource-manager) that can be given over a Container Registry:

- Access Resource Manager
- Create/delete registry
- Push image
- Pull image
- Delete image data
- Change policies
- Sign images

There are also some **built-in roles** that can be assigned, and it's also possible to create **custom roles**.

![](/images/registry_roles.png)

### Authentication

> [!WARNING]
> It's very imporatant that even if the registry name contains some uppercase letters, you should always use **lowercase letters** to login, push and pull images.

There are 4 ways to authenticate to an ACR:

- **With Entra ID**: This is the **default** way to authenticate to an ACR. It uses the **`az acr login`** command to authenticate to the ACR. This command will **store the credentials** in the **`~/.docker/config.json`** file. Moreover, if you are running this command from an environment without access to a docker socket like in a **cloud shell**, it's possible to use the **`--expose-token`** flag to get the **token** to authenticate to the ACR. Then to authenticate you need to use as user name `00000000-0000-0000-0000-000000000000` like: `docker login myregistry.azurecr.io --username 00000000-0000-0000-0000-000000000000 --password-stdin <<< $TOKEN`
- **With an admin account**: The admin user is disabled by default but it can be enabled and then it'll be possible to access the registry with the **username** and **password** of the admin account with full permissions to the registry. This is still supported because some Azure services use it. Note that **2 passwords** are created for this user and both are valid. You can enable it with `az acr update -n <acrName> --admin-enabled true`. Note that the username is usually yhe registry name (and not `admin`).
- **With a token**: It's possible to create a **token** with a **specific `scope map`** (permissions) to access the registry. Then, it's possible to use the name of the token as username and any of the generated passwords to authenticate to the registry with `docker login -u <registry-name> -p <password> <registry-url>`
- **With a Service Principal**: It's possible to create a **service principal** and assign a role like **`AcrPull`** to pull images. Then, it'll be possible to **login to the registry** using the SP appId as username and a generated secret as password.

Example script from the [docs](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-auth-service-principal) to generate a SP with access over a registry:

```bash
#!/bin/bash
ACR_NAME=$containerRegistry
SERVICE_PRINCIPAL_NAME=$servicePrincipal

# Obtain the full registry ID
ACR_REGISTRY_ID=$(az acr show --name $ACR_NAME --query "id" --output tsv)

PASSWORD=$(az ad sp create-for-rbac --name $SERVICE_PRINCIPAL_NAME --scopes $ACR_REGISTRY_ID --role acrpull --query "password" --output tsv)
USER_NAME=$(az ad sp list --display-name $SERVICE_PRINCIPAL_NAME --query "[].appId" --output tsv)

echo "Service principal ID: $USER_NAME"
echo "Service principal password: $PASSWORD"
```

### Encryption

Only the **Premium SKU** supports **encryption at rest** for the images and other artifacts.

### Networking

Only the **Premium SKU** supports **private endpoints**. The other ones only support **public access**. A public endpoint has the format `<registry-name>.azurecr.io` and a private endpoint has the format `<registry-name>.privatelink.azurecr.io`. For this reason, the name of the registry must be unique across all Azure.

### Microsoft Defender for Cloud

This allows you to **scan the images** in the registry for **vulnerabilities**.

### Soft-delete

The **soft-delete** feature allows you to **recover a deleted registry** within the indicated number of days. This feature is **disabled by default**.

### Webhooks

It's possible to **create webhooks** inside registries. In this webhook it's needed to specify the URL where a **request will be sent whenever a push or delete action is performed**. Moreover, Webhooks can indicate a scope to indicate the repositories (images) that will be affected. For example, 'foo:\*' means events under repository 'foo'.

From an attackers perspective it's interesting to check this **before performing any action** in the registry, and remove it terporarely if needed, to avoid being detected.

### Connected registries

This basically allows to **mirror the images** from one registry to another one, usually located on-premises.

It has 2 modes: **ReadOnly** and **ReadWrite**. In the first one, the images are only **pulled** from the source registry, and in the second one, images can also be **pushed** to the source registry.

In order for clients to access the registry from Azure, a **token** is generated when the conected registry is used.

### Runs & Tasks

Runs & Tasks allows to execute in Azure container related actions that you typically needed to do locally or in a CI/CD pipeline. For example, you can **build, push, and run images in the registry**.

The easiest way to build and run a container is using a regular Run:

```bash
# Build
echo "FROM mcr.microsoft.com/hello-world" > Dockerfile
az acr build --image sample/hello-world:v1 --registry mycontainerregistry008 --file Dockerfile .

# Run
az acr run --registry mycontainerregistry008 --cmd '$Registry/sample/hello-world:v1' /dev/null
```

However, that will trigger runs that aren't super interesting from an attackers perspective cause they don't have any managed identity attached to them.

However, **tasks** can have a **system and user managed identity** attached to them. There tasks are the ones useful to **escalate privileges** in the container. In privileges escalation section it's possible to see how to use tasks to escalate privileges.

### Cache

The cache feature allows to **download images from an external repository** and store the new versions in the registry. It requires to have some **credentials configured** by selecting the credentials from an Azure Vault.

This is very interesting from a attacker's perspective because it allows to **pivot to an external platform** if the attacker has enough permissions to access the credentials, **download images from an external repository** and configuring a cache could also be used as **persistence mechanism**.

## Enumeration

> [!WARNING]
> It's very important that even if the registry name contains some uppercase letters, you should only use lowercase letters in the url to access it.

```bash
# List of all the registries
# Check the network, managed identities, adminUserEnabled, softDeletePolicy, url...
az acr list

# Get the details of a registry
az acr show --name <registry-name>

# List tokens of a registry
az acr token list --registry <registry-name> --resource-group <res-group>

# List repositories in a registry
az acr repository list --name <registry-name> --resource-group <res-group>

# List the tags of a repository
az acr repository show-tags --repository <repository-name> --name <registry-name> --resource-group <res-group>

# List deleted repository tags
## At the time of this writing there isn't yet any command to restore it
az acr repository list-deleted --name <registry-name>

# List tasks
## Check the git URL or the command
az acr task list --registry <registry-name>

# List tasks runs
az acr task list-runs --registry <registry-name>

# List connected registries
az acr connected-registry list --registry <registry-name>

# List cache
az acr cache list --registry <registry-name>

# Get cache details
az acr cache show --name <cache-name> --registry <registry-name>
```

## Unauthenticated Access

{{#ref}}
../az-unauthenticated-enum-and-initial-entry/az-container-registry-unauth.md
{{#endref}}

## Privilege Escalation & Post Exploitation

{{#ref}}
../az-privilege-escalation/az-container-registry-privesc.md
{{#endref}}

## References

- [https://learn.microsoft.com/en-us/azure/container-registry/container-registry-authentication?tabs=azure-cli](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-authentication?tabs=azure-cli)
- [https://learn.microsoft.com/en-us/azure/container-registry/container-registry-roles?tabs=azure-cli#access-resource-manager](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-roles?tabs=azure-cli#access-resource-manager)

{{#include ../../../banners/hacktricks-training.md}}
