diff --git a/ScoutSuite/output/data/html/partials/azure/services.acr.subscriptions.id.registries.html b/ScoutSuite/output/data/html/partials/azure/services.acr.subscriptions.id.registries.html
new file mode 100755
index 000000000..290974338
--- /dev/null
+++ b/ScoutSuite/output/data/html/partials/azure/services.acr.subscriptions.id.registries.html
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ScoutSuite/output/data/inc-scoutsuite/scoutsuite.js b/ScoutSuite/output/data/inc-scoutsuite/scoutsuite.js
index f40c25120..7b8ffa152 100755
--- a/ScoutSuite/output/data/inc-scoutsuite/scoutsuite.js
+++ b/ScoutSuite/output/data/inc-scoutsuite/scoutsuite.js
@@ -1233,6 +1233,7 @@ function makeTitle(title) {
const uppercaseTitles = [
'acm', 'aks', 'ec2', 'ecr', 'ecs', 'efs', 'eks', 'gke', 'iam', 'kms', 'rbac',
'rds', 'sns', 'ses', 'sqs', 'vpc', 'elb', 'elbv2', 'emr', 'dns', 'oss', 'ram',
+ 'acr'
]
const formattedTitles = {
diff --git a/ScoutSuite/providers/azure/facade/acr.py b/ScoutSuite/providers/azure/facade/acr.py
new file mode 100644
index 000000000..f885ea25e
--- /dev/null
+++ b/ScoutSuite/providers/azure/facade/acr.py
@@ -0,0 +1,28 @@
+from azure.mgmt.containerregistry import ContainerRegistryManagementClient
+
+from ScoutSuite.core.console import print_exception
+from ScoutSuite.providers.utils import run_concurrently
+from ScoutSuite.utils import get_user_agent
+
+
+class ACRFacade:
+
+ def __init__(self, credentials):
+ self.credentials = credentials
+
+ def get_client(self, subscription_id: str):
+ client = ContainerRegistryManagementClient(self.credentials.get_credentials(),
+ subscription_id=subscription_id, user_agent=get_user_agent())
+ return client
+
+ async def get_registries(self, subscription_id: str):
+ try:
+ client = self.get_client(subscription_id)
+ registries = await run_concurrently(
+ lambda: list(client.registries.list())
+ )
+ except Exception as e:
+ print_exception(f'Failed to retrieve container registries: {e}')
+ return []
+ else:
+ return registries
diff --git a/ScoutSuite/providers/azure/facade/base.py b/ScoutSuite/providers/azure/facade/base.py
index b209e2e43..29c50ee4f 100755
--- a/ScoutSuite/providers/azure/facade/base.py
+++ b/ScoutSuite/providers/azure/facade/base.py
@@ -1,5 +1,6 @@
from ScoutSuite.providers.azure.authentication_strategy import AzureCredentials
from ScoutSuite.providers.azure.facade.aad import AADFacade
+from ScoutSuite.providers.azure.facade.acr import ACRFacade
from ScoutSuite.providers.azure.facade.rbac import RBACFacade
from ScoutSuite.providers.azure.facade.keyvault import KeyVaultFacade
from ScoutSuite.providers.azure.facade.network import NetworkFacade
@@ -48,6 +49,7 @@ def __init__(self,
self.all_subscriptions = all_subscriptions
self.aad = AADFacade(credentials)
+ self.acr = ACRFacade(credentials)
self.rbac = RBACFacade(credentials)
self.keyvault = KeyVaultFacade(credentials)
self.virtualmachines = VirtualMachineFacade(credentials)
diff --git a/ScoutSuite/providers/azure/metadata.json b/ScoutSuite/providers/azure/metadata.json
index b35f9b7ec..96f92fc48 100755
--- a/ScoutSuite/providers/azure/metadata.json
+++ b/ScoutSuite/providers/azure/metadata.json
@@ -31,6 +31,16 @@
}
}
},
+ "container": {
+ "acr": {
+ "resources": {
+ "registries": {
+ "cols": 2,
+ "path": "services.acrs.subscriptions.id.registries"
+ }
+ }
+ }
+ },
"database": {
"sqldatabase": {
"resources": {
diff --git a/ScoutSuite/providers/azure/resources/acr/base.py b/ScoutSuite/providers/azure/resources/acr/base.py
new file mode 100644
index 000000000..166d29c41
--- /dev/null
+++ b/ScoutSuite/providers/azure/resources/acr/base.py
@@ -0,0 +1,9 @@
+from ScoutSuite.providers.azure.resources.subscriptions import Subscriptions
+
+from .registries import Registries
+
+
+class ACRRegistries(Subscriptions):
+ _children = [
+ (Registries, 'registries')
+ ]
diff --git a/ScoutSuite/providers/azure/resources/acr/registries.py b/ScoutSuite/providers/azure/resources/acr/registries.py
new file mode 100644
index 000000000..247d03322
--- /dev/null
+++ b/ScoutSuite/providers/azure/resources/acr/registries.py
@@ -0,0 +1,75 @@
+from ScoutSuite.providers.azure.facade.base import AzureFacade
+from ScoutSuite.providers.azure.resources.base import AzureResources
+from ScoutSuite.providers.utils import get_non_provider_id
+
+
+class Registries(AzureResources):
+
+ def __init__(self, facade: AzureFacade, subscription_id: str):
+ super().__init__(facade)
+ self.subscription_id = subscription_id
+
+ async def fetch_all(self):
+ for raw_registry in await self.facade.acr.get_registries(self.subscription_id):
+ id, registry = self._parse_registry(raw_registry)
+ self[id] = registry
+
+ def _parse_registry(self, raw_registry):
+
+ registry = {}
+ registry['id'] = get_non_provider_id(raw_registry.id)
+ registry['name'] = raw_registry.name
+ registry['type'] = raw_registry.type
+ registry['location'] = raw_registry.location
+ if raw_registry.tags is not None:
+ registry['tags'] = ["{}:{}".format(key, value) for key, value in raw_registry.tags.items()]
+ else:
+ registry['tags'] = []
+
+ registry['sku'] = {}
+ registry['sku']['name'] = raw_registry.sku.name
+ registry['sku']['tier'] = raw_registry.sku.tier
+
+ registry['identity'] = raw_registry.identity
+ registry['login_server'] = raw_registry.login_server
+ registry['provisioning_state'] = raw_registry.provisioning_state
+ registry['status'] = raw_registry.status
+ registry['admin_user_enabled'] = bool(raw_registry.admin_user_enabled)
+ registry['network_rule_set'] = raw_registry.network_rule_set
+
+ registry['policies'] = {
+ 'azureAD_authentication_policy': {},
+ 'soft_delete_policy': {},
+ 'quarantine_policy': {},
+ 'trust_policy': {},
+ 'retention_policy': {},
+ 'export_policy': {}
+ }
+
+ registry['policies']['azureAD_suthentication_policy'] = raw_registry.policies.additional_properties['azureADAuthenticationAsArmPolicy']
+
+ registry['policies']['soft_delete_policy']['retention_days'] = raw_registry.policies.additional_properties['softDeletePolicy']['retentionDays']
+ registry['policies']['soft_delete_policy']['status'] = raw_registry.policies.additional_properties['softDeletePolicy']['status']
+
+ registry['policies']['quarantine_policy']['status'] = raw_registry.policies.quarantine_policy.status
+
+ registry['policies']['trust_policy']['status'] = raw_registry.policies.trust_policy.status
+ registry['policies']['trust_policy']['type'] = raw_registry.policies.trust_policy.type
+
+ registry['policies']['retention_policy']['status'] = raw_registry.policies.retention_policy.status
+ registry['policies']['retention_policy']['type'] = raw_registry.policies.retention_policy.days
+
+ registry['policies']['export_policy']['status'] = raw_registry.policies.export_policy.status
+
+ registry['encryption'] = {}
+ registry['encryption']['status'] = raw_registry.encryption.status
+ registry['encryption']['key_vault_properties'] = raw_registry.encryption.key_vault_properties
+
+ registry['data_endpoint_enabled'] = bool(raw_registry.data_endpoint_enabled)
+ registry['data_endpoint_host_names'] = raw_registry.data_endpoint_host_names
+ registry['private_endpoint_connections'] = raw_registry.private_endpoint_connections
+ registry['public_network_access'] = raw_registry.public_network_access
+ registry['network_rule_bypass_options'] = raw_registry.network_rule_bypass_options
+ registry['zone_redundancy'] = raw_registry.zone_redundancy
+
+ return registry['id'], registry
\ No newline at end of file
diff --git a/ScoutSuite/providers/azure/rules/findings/acr-admin-user-enabled.json b/ScoutSuite/providers/azure/rules/findings/acr-admin-user-enabled.json
new file mode 100644
index 000000000..dc791c33f
--- /dev/null
+++ b/ScoutSuite/providers/azure/rules/findings/acr-admin-user-enabled.json
@@ -0,0 +1,20 @@
+{
+ "description": "Container Registry admin user Enabled",
+ "rationale": "",
+ "remediation": "",
+ "compliance": [
+ ],
+ "references": [
+ ],
+ "dashboard_name": "Registries",
+ "path": "containerregistry.subscriptions.id.registries.id",
+ "conditions": [
+ "and",
+ [
+ "containerregistry.subscriptions.id.registries.id.admin_user_enabled",
+ "notEqual",
+ "False"
+ ]
+ ],
+ "id_suffix": "admin_user_enabled"
+}
\ No newline at end of file
diff --git a/ScoutSuite/providers/azure/rules/findings/acr-public-access.json b/ScoutSuite/providers/azure/rules/findings/acr-public-access.json
new file mode 100644
index 000000000..df16e1e2c
--- /dev/null
+++ b/ScoutSuite/providers/azure/rules/findings/acr-public-access.json
@@ -0,0 +1,21 @@
+{
+ "description": "Container Registry public access Enabled",
+ "rationale": "",
+ "remediation": "",
+ "compliance": [
+ ],
+ "references": [
+ "https://rnd-confluence.veeam.local/display/AP/AppSec+Cloud+Infrastructure+Security+Requirements"
+ ],
+ "dashboard_name": "Registries",
+ "path": "containerregistry.subscriptions.id.registries.id",
+ "conditions": [
+ "and",
+ [
+ "containerregistry.subscriptions.id.registries.id.public_network_access",
+ "equal",
+ "Enabled"
+ ]
+ ],
+ "id_suffix": "public_access_enabled"
+}
\ No newline at end of file
diff --git a/ScoutSuite/providers/azure/services.py b/ScoutSuite/providers/azure/services.py
index a8604ea7a..64f793e42 100755
--- a/ScoutSuite/providers/azure/services.py
+++ b/ScoutSuite/providers/azure/services.py
@@ -1,6 +1,7 @@
from ScoutSuite.providers.azure.authentication_strategy import AzureCredentials
from ScoutSuite.providers.azure.facade.base import AzureFacade
from ScoutSuite.providers.azure.resources.aad.base import AAD
+from ScoutSuite.providers.azure.resources.acr.base import ACRRegistries
from ScoutSuite.providers.azure.resources.rbac.base import RBAC
from ScoutSuite.providers.azure.resources.keyvault.base import KeyVaults
from ScoutSuite.providers.azure.resources.network.base import Networks
@@ -44,6 +45,7 @@ def __init__(self,
programmatic_execution)
self.aad = AAD(facade)
+ self.acr = ACRRegistries(facade)
self.rbac = RBAC(facade)
self.securitycenter = SecurityCenter(facade)
self.sqldatabase = Servers(facade)
diff --git a/ScoutSuite/utils.py b/ScoutSuite/utils.py
index 3965e717b..8647b0a29 100755
--- a/ScoutSuite/utils.py
+++ b/ScoutSuite/utils.py
@@ -38,6 +38,7 @@
'ssm': 'Systems Manager',
# Azure
'aad': 'Azure Active Directory',
+ 'registry': 'ACR Registry',
'storageaccounts': 'Storage Accounts',
'sqldatabase': 'SQL Database',
'securitycenter': 'Security Center',
diff --git a/requirements.txt b/requirements.txt
index c2041cd46..3eeec4b95 100755
--- a/requirements.txt
+++ b/requirements.txt
@@ -34,6 +34,7 @@ azure-identity==1.5.0
## for resources
+azure-mgmt-containerregistry==10.3.0
azure-mgmt-resource==15.0.0
azure-mgmt-storage==17.0.0
azure-mgmt-monitor==2.0.0