diff --git a/ScoutSuite/output/data/html/partials/azure/services.network.network_security_groups.html b/ScoutSuite/output/data/html/partials/azure/services.network.network_security_groups.html
new file mode 100644
index 000000000..07338cc66
--- /dev/null
+++ b/ScoutSuite/output/data/html/partials/azure/services.network.network_security_groups.html
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ScoutSuite/output/data/html/partials/azure/services.network.network_watchers.html b/ScoutSuite/output/data/html/partials/azure/services.network.network_watchers.html
new file mode 100644
index 000000000..675d67c9c
--- /dev/null
+++ b/ScoutSuite/output/data/html/partials/azure/services.network.network_watchers.html
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
\ 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 ba1bfdb6d..5d3b863a1 100644
--- a/ScoutSuite/output/data/inc-scoutsuite/scoutsuite.js
+++ b/ScoutSuite/output/data/inc-scoutsuite/scoutsuite.js
@@ -1018,6 +1018,8 @@ function make_title (title) {
return 'SQL Database';
} else if (title == 'securitycenter') {
return 'Security Center';
+ } else if (title == 'network') {
+ return 'Network';
} else if (title == 'keyvault') {
return 'Key Vault';
} else if (title == 'appgateway') {
diff --git a/ScoutSuite/providers/azure/configs/services.py b/ScoutSuite/providers/azure/configs/services.py
index 108c71443..94ccd9227 100644
--- a/ScoutSuite/providers/azure/configs/services.py
+++ b/ScoutSuite/providers/azure/configs/services.py
@@ -5,6 +5,7 @@
from ScoutSuite.providers.azure.services.monitor import MonitorConfig
from ScoutSuite.providers.azure.services.sqldatabase import SQLDatabaseConfig
from ScoutSuite.providers.azure.services.securitycenter import SecurityCenterConfig
+from ScoutSuite.providers.azure.services.network import NetworkConfig
from ScoutSuite.providers.azure.services.keyvault import KeyVaultConfig
try:
from ScoutSuite.providers.azure.services.appgateway_private import AppGatewayConfig
@@ -24,6 +25,7 @@ def __init__(self, metadata=None, thread_config=4, **kwargs):
self.monitor = MonitorConfig(thread_config=thread_config)
self.sqldatabase = SQLDatabaseConfig(thread_config=thread_config)
self.securitycenter = SecurityCenterConfig(thread_config=thread_config)
+ self.network = NetworkConfig(thread_config=thread_config)
self.keyvault = KeyVaultConfig(thread_config=thread_config)
try:
diff --git a/ScoutSuite/providers/azure/metadata.json b/ScoutSuite/providers/azure/metadata.json
index 4bb35baf7..317bc4fd5 100644
--- a/ScoutSuite/providers/azure/metadata.json
+++ b/ScoutSuite/providers/azure/metadata.json
@@ -55,6 +55,18 @@
"path": "services.appgateway.app_gateways"
}
}
+ },
+ "network": {
+ "resources": {
+ "network_watchers": {
+ "cols": 2,
+ "path": "services.network.network_watchers"
+ },
+ "network_security_groups": {
+ "cols": 2,
+ "path": "services.network.network_security_groups"
+ }
+ }
}
},
"redis": {
diff --git a/ScoutSuite/providers/azure/rules/conditions/allow-tcp.json b/ScoutSuite/providers/azure/rules/conditions/allow-tcp.json
new file mode 100644
index 000000000..135b02e26
--- /dev/null
+++ b/ScoutSuite/providers/azure/rules/conditions/allow-tcp.json
@@ -0,0 +1,7 @@
+{
+ "conditions":[
+ "or",
+ ["network.network_security_groups.id.security_rules.id.protocol", "equal", "*"],
+ ["network.network_security_groups.id.security_rules.id.protocol", "equal", "TCP"]
+ ]
+}
\ No newline at end of file
diff --git a/ScoutSuite/providers/azure/rules/conditions/exposed-to-the-internet.json b/ScoutSuite/providers/azure/rules/conditions/exposed-to-the-internet.json
new file mode 100644
index 000000000..bacdf3694
--- /dev/null
+++ b/ScoutSuite/providers/azure/rules/conditions/exposed-to-the-internet.json
@@ -0,0 +1,12 @@
+{
+ "conditions":[
+ "and",
+ ["network.network_security_groups.id.security_rules.id.allow", "true", ""],
+ ["network.network_security_groups.id.security_rules.id.direction", "equal", "Inbound"],
+ [
+ "or",
+ ["network.network_security_groups.id.security_rules.id.source_address_prefixes", "containAtLeastOneOf", "*"],
+ ["network.network_security_groups.id.security_rules.id.source_address_prefixes", "containAtLeastOneOf", "Internet"]
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/ScoutSuite/providers/azure/rules/findings/network-security-groups-rule-inbound-service.json b/ScoutSuite/providers/azure/rules/findings/network-security-groups-rule-inbound-service.json
new file mode 100644
index 000000000..53e233e8b
--- /dev/null
+++ b/ScoutSuite/providers/azure/rules/findings/network-security-groups-rule-inbound-service.json
@@ -0,0 +1,15 @@
+{
+ "dashboard_name": "Network",
+ "arg_names": [ "Protocol (ex: SSH, RDP, etc.)", "Protocol's port", "Associated CIS rule" ],
+ "key": "network-security-groups-rule-inbound-_ARG_0_",
+ "description": "Security rule allowing _ARG_0_ inbound access in security group",
+ "rationale": "You should not permit _ARG_0_(port _ARG_1_) inbound access to a network security group (CIS _ARG_2_).",
+ "path": "network.network_security_groups.id.security_rules.id",
+ "display_path": "network.network_security_groups.id",
+ "conditions": [ "and",
+ ["network.network_security_groups.id.security_rules.id.destination_ports", "containAtLeastOneOf", "_ARG_1_"],
+ ["_INCLUDE_(conditions/exposed-to-the-internet.json)", "", ""],
+ ["_INCLUDE_(conditions/allow-tcp.json)", "", ""]
+ ],
+ "id_suffix": "security_groups_rule_inbound__ARG_0_"
+}
diff --git a/ScoutSuite/providers/azure/rules/findings/network-watcher-not-enabled.json b/ScoutSuite/providers/azure/rules/findings/network-watcher-not-enabled.json
new file mode 100644
index 000000000..1f3fd9bb6
--- /dev/null
+++ b/ScoutSuite/providers/azure/rules/findings/network-watcher-not-enabled.json
@@ -0,0 +1,11 @@
+{
+ "dashboard_name": "Network",
+ "description": "Network watcher not enabled",
+ "rationale": "Network watchers should be enabled (CIS 6.5).",
+ "path": "network.network_watchers",
+ "display_path": "network.network_watchers",
+ "conditions": [ "and",
+ ["network.network_watchers", "empty", ""]
+ ],
+ "id_suffix": "network_watchers_disabled"
+}
\ No newline at end of file
diff --git a/ScoutSuite/providers/azure/rules/findings/network-watcher-not-provisioned.json b/ScoutSuite/providers/azure/rules/findings/network-watcher-not-provisioned.json
new file mode 100644
index 000000000..d84ca1e9b
--- /dev/null
+++ b/ScoutSuite/providers/azure/rules/findings/network-watcher-not-provisioned.json
@@ -0,0 +1,11 @@
+{
+ "dashboard_name": "Network",
+ "description": "Network watcher not provisioned",
+ "rationale": "Network watchers should be provisioned to work (CIS 6.5).",
+ "path": "network.network_watchers.id",
+ "display_path": "network.network_watchers.id",
+ "conditions": [ "and",
+ ["network.network_watchers.id.provisioning_state", "notEqual", "Succeeded"]
+ ],
+ "id_suffix": "network_watchers_not_provisioned"
+}
\ No newline at end of file
diff --git a/ScoutSuite/providers/azure/rules/rulesets/default.json b/ScoutSuite/providers/azure/rules/rulesets/default.json
index eda5c2775..c79203c49 100644
--- a/ScoutSuite/providers/azure/rules/rulesets/default.json
+++ b/ScoutSuite/providers/azure/rules/rulesets/default.json
@@ -1,6 +1,47 @@
{
"about": "Default ruleset for Azure.",
"rules": {
+ "network-security-groups-rule-inbound-service.json": [
+ {
+ "args": [
+ "RDP",
+ "3389",
+ "6.1"
+ ],
+ "enabled": true,
+ "level": "warning"
+ },
+ {
+ "args": [
+ "SSH",
+ "22",
+ "6.2"
+ ],
+ "enabled": true,
+ "level": "warning"
+ },
+ {
+ "args": [
+ "SQL",
+ "1433",
+ "6.3"
+ ],
+ "enabled": true,
+ "level": "warning"
+ }
+ ],
+ "network-watcher-not-enabled.json": [
+ {
+ "enabled": true,
+ "level": "warning"
+ }
+ ],
+ "network-watcher-not-provisioned.json": [
+ {
+ "enabled": true,
+ "level": "warning"
+ }
+ ],
"storageaccount-account-allowing-clear-text.json": [
{
"enabled": true,
diff --git a/ScoutSuite/providers/azure/services/network.py b/ScoutSuite/providers/azure/services/network.py
new file mode 100644
index 000000000..a23bb0a9b
--- /dev/null
+++ b/ScoutSuite/providers/azure/services/network.py
@@ -0,0 +1,145 @@
+# -*- coding: utf-8 -*-
+
+from ScoutSuite.providers.azure.configs.base import AzureBaseConfig
+
+
+class NetworkConfig(AzureBaseConfig):
+ targets = (
+ ('network_watchers', 'Network Watchers', 'list_all', {}, False),
+ ('network_security_groups', 'Network Security Group', 'list_all', {}, False),
+ )
+
+ def __init__(self, thread_config):
+ self.network_watchers = {}
+ self.network_watchers_count = 0
+
+ self.network_security_groups = {}
+ self.network_security_groups_count = 0
+
+ super(NetworkConfig, self).__init__(thread_config)
+
+ def parse_network_watchers(self, network_watcher, params):
+ network_watcher_dict = {}
+ network_watcher_dict['id'] = network_watcher.id
+ network_watcher_dict['name'] = network_watcher.name
+ network_watcher_dict['provisioning_state'] = network_watcher.provisioning_state
+ network_watcher_dict['location'] = network_watcher.location
+ network_watcher_dict['etag'] = network_watcher.etag
+
+ self.network_watchers[network_watcher_dict['id']] = network_watcher_dict
+
+ def parse_network_security_groups(self, network_security_group, params):
+ network_security_group_dict = {}
+ network_security_group_dict['id'] = network_security_group.id
+ network_security_group_dict['name'] = network_security_group.name
+ network_security_group_dict['provisioning_state'] = network_security_group.provisioning_state
+ network_security_group_dict['location'] = network_security_group.location
+ network_security_group_dict['resource_guid'] = network_security_group.resource_guid
+ network_security_group_dict['etag'] = network_security_group.etag
+
+ network_security_group_dict['security_rules'] = self._parse_security_rules(network_security_group)
+
+ exposed_ports = self._parse_exposed_ports(network_security_group)
+ network_security_group_dict['exposed_ports'] = exposed_ports
+ network_security_group_dict['exposed_port_ranges'] = self._format_ports(exposed_ports)
+
+ self.network_security_groups[network_security_group_dict['id']] = network_security_group_dict
+
+ def _parse_security_rules(self, network_security_group):
+ security_rules = {}
+ for sr in network_security_group.security_rules:
+ security_rule_dict = {}
+ security_rule_dict['id'] = sr.id
+ security_rule_dict['name'] = sr.name
+ security_rule_dict['allow'] = sr.access == "Allow"
+ security_rule_dict['priority'] = sr.priority
+ security_rule_dict['description'] = sr.description
+ security_rule_dict['provisioning_state'] = sr.provisioning_state
+
+ security_rule_dict['protocol'] = sr.protocol
+ security_rule_dict['direction'] = sr.direction
+
+ source_address_prefixes = self._merge_prefixes_or_ports(sr.source_address_prefix,
+ sr.source_address_prefixes)
+ security_rule_dict['source_address_prefixes'] = source_address_prefixes
+
+ source_port_ranges = self._merge_prefixes_or_ports(sr.source_port_range, sr.source_port_ranges)
+ security_rule_dict['source_port_ranges'] = source_port_ranges
+ security_rule_dict['source_ports'] = self._parse_ports(source_port_ranges)
+
+ destination_address_prefixes = self._merge_prefixes_or_ports(sr.destination_address_prefix,
+ sr.destination_address_prefixes)
+ security_rule_dict['destination_address_prefixes'] = destination_address_prefixes
+
+ destination_port_ranges = self._merge_prefixes_or_ports(sr.destination_port_range,
+ sr.destination_port_ranges)
+ security_rule_dict['destination_port_ranges'] = destination_port_ranges
+ security_rule_dict['destination_ports'] = self._parse_ports(destination_port_ranges)
+
+ security_rule_dict['etag'] = sr.etag
+
+ security_rules[security_rule_dict['id']] = security_rule_dict
+
+ return security_rules
+
+ def _parse_ports(self, port_ranges):
+ ports = set()
+ for pr in port_ranges:
+ if pr == "*":
+ for p in range(0, 65535 + 1):
+ ports.add(p)
+ break
+ elif "-" in pr:
+ lower, upper = pr.split("-")
+ for p in range(int(lower), int(upper) + 1):
+ ports.add(p)
+ else:
+ ports.add(int(pr))
+ ports = list(ports)
+ ports.sort()
+ return ports
+
+ def _parse_exposed_ports(self, network_security_group):
+ exposed_ports = set()
+
+ # Sort by priority.
+ rules = network_security_group.default_security_rules + network_security_group.security_rules
+ rules.sort(key=lambda x: x.priority, reverse=True)
+
+ for sr in rules:
+ if sr.direction == "Inbound" and (sr.source_address_prefix == "*"
+ or sr.source_address_prefix == "Internet"):
+ port_ranges = self._merge_prefixes_or_ports(sr.destination_port_range,
+ sr.destination_port_ranges)
+ ports = self._parse_ports(port_ranges)
+ if sr.access == "Allow":
+ for p in ports:
+ exposed_ports.add(p)
+ else:
+ for p in ports:
+ exposed_ports.discard(p)
+ exposed_ports = list(exposed_ports)
+ exposed_ports.sort()
+ return exposed_ports
+
+ def _merge_prefixes_or_ports(self, port_range, port_ranges):
+ port_ranges = port_ranges if port_ranges else []
+ if port_range:
+ port_ranges.append(port_range)
+ return port_ranges
+
+ def _format_ports(self, ports):
+ port_ranges = []
+ start = None
+ for i in range(0, 65535 + 1):
+ if i in ports:
+ if not start:
+ start = i
+ else:
+ if start:
+ if i - 1 == start:
+ port_ranges.append(str(start))
+ else:
+ port_ranges.append(str(start) + "-" + str(i - 1))
+ start = None
+ return port_ranges
diff --git a/ScoutSuite/providers/azure/utils.py b/ScoutSuite/providers/azure/utils.py
index 733539332..cbdfe5266 100644
--- a/ScoutSuite/providers/azure/utils.py
+++ b/ScoutSuite/providers/azure/utils.py
@@ -13,6 +13,7 @@
from azure.mgmt.network import NetworkManagementClient
from azure.mgmt.redis import RedisManagementClient
+
def azure_connect_service(service, credentials, region_name=None):
try:
if service == 'storageaccounts':
@@ -25,6 +26,8 @@ def azure_connect_service(service, credentials, region_name=None):
return KeyVaultManagementClient(credentials.credentials, credentials.subscription_id)
elif service == 'appgateway':
return NetworkManagementClient(credentials.credentials, credentials.subscription_id)
+ elif service == 'network':
+ return NetworkManagementClient(credentials.credentials, credentials.subscription_id)
elif service == 'rediscache':
return RedisManagementClient(credentials.credentials, credentials.subscription_id)
elif service == 'securitycenter':
diff --git a/ScoutSuite/utils.py b/ScoutSuite/utils.py
index 283fbac30..f4eb65abd 100644
--- a/ScoutSuite/utils.py
+++ b/ScoutSuite/utils.py
@@ -36,7 +36,8 @@
'securitycenter': 'Security Center',
'keyvault': 'Key Vault',
'appgateway': 'Application Gateway',
- 'rediscache': 'Redis Cache'
+ 'rediscache': 'Redis Cache',
+ 'network': 'Network',
}