From 0a2d7a5d47b38af0d1506c9995252a7a41fc0b39 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Fri, 21 Jun 2019 19:00:47 +0200 Subject: [PATCH 001/145] Add field for default / non default user-managed SAs --- .../gcp/services.iam.projects.id.service_accounts.html | 1 + .../providers/gcp/resources/iam/service_accounts.py | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/ScoutSuite/output/data/html/partials/gcp/services.iam.projects.id.service_accounts.html b/ScoutSuite/output/data/html/partials/gcp/services.iam.projects.id.service_accounts.html index f24f17021..14a94c55a 100644 --- a/ScoutSuite/output/data/html/partials/gcp/services.iam.projects.id.service_accounts.html +++ b/ScoutSuite/output/data/html/partials/gcp/services.iam.projects.id.service_accounts.html @@ -10,6 +10,7 @@

Information

ID: {{id}}
email: {{email}}
Display Name: {{display_name}}
+
Default Service Account: {{default_service_account}}

Keys:

diff --git a/ScoutSuite/providers/gcp/resources/iam/service_accounts.py b/ScoutSuite/providers/gcp/resources/iam/service_accounts.py index c78f80a8b..a76bf75d3 100644 --- a/ScoutSuite/providers/gcp/resources/iam/service_accounts.py +++ b/ScoutSuite/providers/gcp/resources/iam/service_accounts.py @@ -2,6 +2,7 @@ from ScoutSuite.providers.gcp.resources.base import GCPCompositeResources from ScoutSuite.providers.gcp.resources.iam.bindings import Bindings from ScoutSuite.providers.gcp.resources.iam.keys import Keys +import re class ServiceAccounts(GCPCompositeResources): @@ -32,4 +33,11 @@ def _parse_service_account(self, raw_service_account): service_account_dict['name'] = raw_service_account['email'] service_account_dict['email'] = raw_service_account['email'] service_account_dict['project_id'] = raw_service_account['projectId'] + + pattern = re.compile('.+@{}\.iam\.gserviceaccount\.com'.format(service_account_dict['project_id'])) + if pattern.match(service_account_dict['email']): + service_account_dict['default_service_account'] = False + else: + service_account_dict['default_service_account'] = True + return service_account_dict['id'], service_account_dict From 545d87072ee84480b7f4f6fa8c41d15cd97cb61a Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Tue, 12 Nov 2019 12:57:29 +0100 Subject: [PATCH 002/145] Fix minor bug --- .../partials/gcp/services.cloudstorage.projects.id.buckets.html | 2 +- ScoutSuite/providers/gcp/resources/cloudstorage/buckets.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ScoutSuite/output/data/html/partials/gcp/services.cloudstorage.projects.id.buckets.html b/ScoutSuite/output/data/html/partials/gcp/services.cloudstorage.projects.id.buckets.html index 6484528b8..0e6999f53 100644 --- a/ScoutSuite/output/data/html/partials/gcp/services.cloudstorage.projects.id.buckets.html +++ b/ScoutSuite/output/data/html/partials/gcp/services.cloudstorage.projects.id.buckets.html @@ -11,7 +11,7 @@

Information

Location: {{location}}
Storage Class: {{storage_class}}
Logging: {{convert_bool_to_enabled logging_enabled}}
-
Versioning: {{convert_bool_to_enabled versioning_status}}
+
Versioning: {{convert_bool_to_enabled versioning_status_enabled}}
Uniform Bucket-Level Access: {{convert_bool_to_enabled uniform_bucket_level_access}}
diff --git a/ScoutSuite/providers/gcp/resources/cloudstorage/buckets.py b/ScoutSuite/providers/gcp/resources/cloudstorage/buckets.py index 79b772735..be96a44a6 100644 --- a/ScoutSuite/providers/gcp/resources/cloudstorage/buckets.py +++ b/ScoutSuite/providers/gcp/resources/cloudstorage/buckets.py @@ -23,8 +23,8 @@ def _parse_bucket(self, raw_bucket): bucket_dict['creation_date'] = raw_bucket.time_created bucket_dict['location'] = raw_bucket.location bucket_dict['storage_class'] = raw_bucket.storage_class.lower() - bucket_dict['versioning_status_enabled'] = raw_bucket.versioning_enabled bucket_dict['uniform_bucket_level_access'] = raw_bucket.iam_configuration['bucketPolicyOnly']['enabled'] + bucket_dict['versioning_status_enabled'] = raw_bucket.versioning_enabled bucket_dict['logging_enabled'] = raw_bucket.logging is not None bucket_dict['acls'] = list(raw_bucket.acl) bucket_dict['acl_configuration'] = self._get_cloudstorage_bucket_acl(raw_bucket) # FIXME this should be "IAM" From 2d90e0c33c12cab4ad5b4208cbd597e1ac0879de Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Tue, 12 Nov 2019 18:17:07 +0100 Subject: [PATCH 003/145] Fix edge case --- ScoutSuite/providers/gcp/resources/cloudstorage/buckets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ScoutSuite/providers/gcp/resources/cloudstorage/buckets.py b/ScoutSuite/providers/gcp/resources/cloudstorage/buckets.py index be96a44a6..931832c77 100644 --- a/ScoutSuite/providers/gcp/resources/cloudstorage/buckets.py +++ b/ScoutSuite/providers/gcp/resources/cloudstorage/buckets.py @@ -26,7 +26,7 @@ def _parse_bucket(self, raw_bucket): bucket_dict['uniform_bucket_level_access'] = raw_bucket.iam_configuration['bucketPolicyOnly']['enabled'] bucket_dict['versioning_status_enabled'] = raw_bucket.versioning_enabled bucket_dict['logging_enabled'] = raw_bucket.logging is not None - bucket_dict['acls'] = list(raw_bucket.acl) + bucket_dict['acls'] = list(raw_bucket.acl) if not bucket_dict['uniform_bucket_level_access'] else [] bucket_dict['acl_configuration'] = self._get_cloudstorage_bucket_acl(raw_bucket) # FIXME this should be "IAM" return bucket_dict['id'], bucket_dict From 822deeeded023f4747326917a7f2f4536a5c7ed9 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Wed, 13 Nov 2019 13:45:17 +0100 Subject: [PATCH 004/145] Improve partial --- .../gcp/services.cloudstorage.projects.id.buckets.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ScoutSuite/output/data/html/partials/gcp/services.cloudstorage.projects.id.buckets.html b/ScoutSuite/output/data/html/partials/gcp/services.cloudstorage.projects.id.buckets.html index 0e6999f53..b5e2eeae2 100644 --- a/ScoutSuite/output/data/html/partials/gcp/services.cloudstorage.projects.id.buckets.html +++ b/ScoutSuite/output/data/html/partials/gcp/services.cloudstorage.projects.id.buckets.html @@ -6,10 +6,10 @@

{{name}}

Information

-
Project ID: {{project_id}}
-
Creation Date: {{creation_date}}
-
Location: {{location}}
-
Storage Class: {{storage_class}}
+
Project ID: {{project_id}}
+
Creation Date: {{format_date creation_date}}
+
Location: {{location}}
+
Storage Class: {{storage_class}}
Logging: {{convert_bool_to_enabled logging_enabled}}
Versioning: {{convert_bool_to_enabled versioning_status_enabled}}
Uniform Bucket-Level Access: {{convert_bool_to_enabled uniform_bucket_level_access}}
From 36dd0cd22d61bceed904a38edc02a288202b3118 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Wed, 13 Nov 2019 15:12:53 +0100 Subject: [PATCH 005/145] Exclude ACLs with uniform buckets --- .../partials/gcp/services.cloudstorage.projects.id.buckets.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ScoutSuite/output/data/html/partials/gcp/services.cloudstorage.projects.id.buckets.html b/ScoutSuite/output/data/html/partials/gcp/services.cloudstorage.projects.id.buckets.html index b5e2eeae2..99643d9a2 100644 --- a/ScoutSuite/output/data/html/partials/gcp/services.cloudstorage.projects.id.buckets.html +++ b/ScoutSuite/output/data/html/partials/gcp/services.cloudstorage.projects.id.buckets.html @@ -31,6 +31,7 @@

ACL Permissions

@@ -46,6 +47,7 @@

Date: Wed, 13 Nov 2019 15:44:46 +0100 Subject: [PATCH 007/145] Add appropriate suffixes --- .../partials/gcp/services.cloudstorage.projects.id.buckets.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ScoutSuite/output/data/html/partials/gcp/services.cloudstorage.projects.id.buckets.html b/ScoutSuite/output/data/html/partials/gcp/services.cloudstorage.projects.id.buckets.html index 99643d9a2..7a23cdf58 100644 --- a/ScoutSuite/output/data/html/partials/gcp/services.cloudstorage.projects.id.buckets.html +++ b/ScoutSuite/output/data/html/partials/gcp/services.cloudstorage.projects.id.buckets.html @@ -37,7 +37,7 @@

    {{#each acls}} -
  • {{entity}}
  • +
  • {{entity}}
    • {{role}}
    From 0a0569cc4f98b697a2e840d424a99c15d466d778 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Wed, 13 Nov 2019 16:14:44 +0100 Subject: [PATCH 008/145] Improvements to partial --- ...ervices.cloudresourcemanager.projects.id.bindings.html | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ScoutSuite/output/data/html/partials/gcp/services.cloudresourcemanager.projects.id.bindings.html b/ScoutSuite/output/data/html/partials/gcp/services.cloudresourcemanager.projects.id.bindings.html index 55db26420..93158386b 100644 --- a/ScoutSuite/output/data/html/partials/gcp/services.cloudresourcemanager.projects.id.bindings.html +++ b/ScoutSuite/output/data/html/partials/gcp/services.cloudresourcemanager.projects.id.bindings.html @@ -30,11 +30,13 @@
    Attached Groups:
    {{/each}}

-
Attached Service Accounts:
-
+
Attached Service Accounts:
+
    {{#each members.service_accounts}} -
  • {{this}}
  • +
  • + {{this}} +
  • {{else}}
  • None
  • {{/each}} From 051408207b80e76e4c1f1de680fe88820fb4071a Mon Sep 17 00:00:00 2001 From: rracterr Date: Mon, 27 Jan 2020 19:09:28 +0000 Subject: [PATCH 009/145] Makes reading auth files allot more reliable --- ScoutSuite/core/cli_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ScoutSuite/core/cli_parser.py b/ScoutSuite/core/cli_parser.py index 367acc9eb..4e68a034b 100644 --- a/ScoutSuite/core/cli_parser.py +++ b/ScoutSuite/core/cli_parser.py @@ -172,7 +172,7 @@ def _init_azure_parser(self): azure_auth_modes.add_argument('--file-auth', action='store', - type=argparse.FileType('r'), + type=argparse.FileType('rb'), dest='file_auth', metavar="FILE", help='Run Scout with the specified credential file') From 4d49456b3897f401b384cc34ea0cf1795d4e7d51 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Wed, 29 Jan 2020 10:18:53 +0100 Subject: [PATCH 010/145] Suppress info logs --- ScoutSuite/providers/oci/authentication_strategy.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ScoutSuite/providers/oci/authentication_strategy.py b/ScoutSuite/providers/oci/authentication_strategy.py index 993513181..f5778875c 100644 --- a/ScoutSuite/providers/oci/authentication_strategy.py +++ b/ScoutSuite/providers/oci/authentication_strategy.py @@ -1,3 +1,5 @@ +import logging + from oci.config import from_file from oci.identity import IdentityClient @@ -20,6 +22,9 @@ def authenticate(self, profile=None, **kwargs): try: + # Set logging level to error for libraries as otherwise generates a lot of warnings + logging.getLogger('oci').setLevel(logging.ERROR) + config = from_file(profile_name=profile) compartment_id = config["tenancy"] From 7c21f6020c994f8b80d61a99a50ecf7b5862e490 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Tue, 4 Feb 2020 17:50:39 +0100 Subject: [PATCH 011/145] Add support for compartments --- ScoutSuite/providers/oci/authentication_strategy.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ScoutSuite/providers/oci/authentication_strategy.py b/ScoutSuite/providers/oci/authentication_strategy.py index f5778875c..d7748779f 100644 --- a/ScoutSuite/providers/oci/authentication_strategy.py +++ b/ScoutSuite/providers/oci/authentication_strategy.py @@ -26,7 +26,10 @@ def authenticate(self, profile=None, **kwargs): logging.getLogger('oci').setLevel(logging.ERROR) config = from_file(profile_name=profile) - compartment_id = config["tenancy"] + if 'compartment-id' in config: + compartment_id = config['compartment-id'] + else: + compartment_id = config['tenancy'] # Get the current user identity = IdentityClient(config) From 24145eb6f562452a9cd7294c8b6a0179dca8dde4 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Tue, 4 Feb 2020 17:50:49 +0100 Subject: [PATCH 012/145] Improve error handling --- ScoutSuite/providers/oci/facade/identity.py | 68 ++++++++++++++------- 1 file changed, 47 insertions(+), 21 deletions(-) diff --git a/ScoutSuite/providers/oci/facade/identity.py b/ScoutSuite/providers/oci/facade/identity.py index bca3eb133..f050770dc 100644 --- a/ScoutSuite/providers/oci/facade/identity.py +++ b/ScoutSuite/providers/oci/facade/identity.py @@ -1,7 +1,9 @@ from oci.identity import IdentityClient -from ScoutSuite.providers.oci.authentication_strategy import OracleCredentials from oci.pagination import list_call_get_all_results +from ScoutSuite.providers.oci.authentication_strategy import OracleCredentials +from ScoutSuite.core.console import print_exception + from ScoutSuite.providers.utils import run_concurrently @@ -11,33 +13,57 @@ def __init__(self, credentials: OracleCredentials): self._client = IdentityClient(self._credentials.config) async def get_users(self): - response = await run_concurrently( - lambda: list_call_get_all_results(self._client.list_users, self._credentials.compartment_id)) - return response.data + try: + response = await run_concurrently( + lambda: list_call_get_all_results(self._client.list_users, self._credentials.compartment_id)) + return response.data + except Exception as e: + print_exception('Failed to retrieve users: {}'.format(e)) + return None async def get_user_api_keys(self, user_id): - response = await run_concurrently( - lambda: list_call_get_all_results(self._client.list_api_keys, user_id)) - return response.data + try: + response = await run_concurrently( + lambda: list_call_get_all_results(self._client.list_api_keys, user_id)) + return response.data + except Exception as e: + print_exception('Failed to retrieve user api keys: {}'.format(e)) + return None async def get_groups(self): - response = await run_concurrently( - lambda: list_call_get_all_results(self._client.list_groups, self._credentials.compartment_id)) - return response.data + try: + response = await run_concurrently( + lambda: list_call_get_all_results(self._client.list_groups, self._credentials.compartment_id)) + return response.data + except Exception as e: + print_exception('Failed to retrieve groups: {}'.format(e)) + return None async def get_group_users(self, group_id): - response = await run_concurrently( - lambda: list_call_get_all_results(self._client.list_user_group_memberships, - self._credentials.compartment_id, - group_id=group_id)) - return response.data + try: + response = await run_concurrently( + lambda: list_call_get_all_results(self._client.list_user_group_memberships, + self._credentials.compartment_id, + group_id=group_id)) + return response.data + except Exception as e: + print_exception('Failed to retrieve group users: {}'.format(e)) + return None async def get_policies(self): - response = await run_concurrently( - lambda: list_call_get_all_results(self._client.list_policies, self._credentials.compartment_id)) - return response.data + try: + response = await run_concurrently( + lambda: list_call_get_all_results(self._client.list_policies, self._credentials.compartment_id)) + return response.data + except Exception as e: + print_exception('Failed to retrieve policies: {}'.format(e)) + return None async def get_authentication_policy(self): - response = await run_concurrently( - lambda: self._client.get_authentication_policy(self._credentials.compartment_id)) - return response.data + try: + response = await run_concurrently( + lambda: self._client.get_authentication_policy(self._credentials.compartment_id)) + return response.data + except Exception as e: + print_exception('Failed to retrieve authentication policy: {}'.format(e)) + return None From 906d9bdf297f12468b8c4cc492d784d565dcf42a Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Tue, 4 Feb 2020 17:51:00 +0100 Subject: [PATCH 013/145] Improve error handling --- .../oci/resources/identity/authentication_policy.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ScoutSuite/providers/oci/resources/identity/authentication_policy.py b/ScoutSuite/providers/oci/resources/identity/authentication_policy.py index be8c229e4..425993e72 100644 --- a/ScoutSuite/providers/oci/resources/identity/authentication_policy.py +++ b/ScoutSuite/providers/oci/resources/identity/authentication_policy.py @@ -8,7 +8,10 @@ def __init__(self, facade: OracleFacade): async def fetch_all(self): raw_authentication_policy = await self.facade.identity.get_authentication_policy() - password_policy = self._parse_authentication_policy(raw_authentication_policy) + if raw_authentication_policy: + password_policy = self._parse_authentication_policy(raw_authentication_policy) + else: + password_policy = {} self.update(password_policy) def _parse_authentication_policy(self, raw_authentication_policy): From 9bf9e827dc65f06548efc8c20a08ffd94063eba1 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Wed, 5 Feb 2020 09:37:17 +0100 Subject: [PATCH 014/145] Fix for https://github.com/nccgroup/ScoutSuite/issues/635 --- ScoutSuite/providers/aws/resources/ec2/ami.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ScoutSuite/providers/aws/resources/ec2/ami.py b/ScoutSuite/providers/aws/resources/ec2/ami.py index 2e8993643..6c6ec3565 100644 --- a/ScoutSuite/providers/aws/resources/ec2/ami.py +++ b/ScoutSuite/providers/aws/resources/ec2/ami.py @@ -14,7 +14,6 @@ async def fetch_all(self): self[name] = resource def _parse_image(self, raw_image): - raw_image['id'] = raw_image['ImageId'] - raw_image['name'] = raw_image['Name'] - + raw_image['id'] = raw_image.get('ImageId') + raw_image['name'] = raw_image.get('Name') return raw_image['id'], raw_image From 714ff5f72c08e387356353edecdde591a51b938d Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Wed, 5 Feb 2020 09:46:11 +0100 Subject: [PATCH 015/145] Add notice --- ScoutSuite/output/data/inc-scoutsuite/helpers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ScoutSuite/output/data/inc-scoutsuite/helpers.js b/ScoutSuite/output/data/inc-scoutsuite/helpers.js index 07435e44a..fb2947d60 100644 --- a/ScoutSuite/output/data/inc-scoutsuite/helpers.js +++ b/ScoutSuite/output/data/inc-scoutsuite/helpers.js @@ -386,7 +386,7 @@ Handlebars.registerHelper('each_dict_as_sorted_list', function (context, options } else { if (context[a].level.toLowerCase() === 'danger') return -1 if (context[b].level.toLowerCase() === 'danger') return 1 - if (context[a].level.toLowerCase() === 'warning') return -1 + if (context[a].level.toLowerCase() === 'warning') return -1 // FIXME - these are duplicated for nothing? if (context[b].level.toLowerCase() === 'warning') return 1 if (context[a].level.toLowerCase() === 'warning') return -1 if (context[b].level.toLowerCase() === 'warning') return 1 From 2147f8401991187fae2dc552bcdf269868b901f8 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Wed, 5 Feb 2020 09:47:17 +0100 Subject: [PATCH 016/145] Update version --- ScoutSuite/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ScoutSuite/__init__.py b/ScoutSuite/__init__.py index 646fd8081..6840fb6bc 100644 --- a/ScoutSuite/__init__.py +++ b/ScoutSuite/__init__.py @@ -1,5 +1,5 @@ __author__ = 'NCC Group' -__version__ = '5.7.0' +__version__ = '5.8.0' ERRORS_LIST = [] From cf20d8dcd2db275e0044a739fe406cbcd5278034 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Wed, 5 Feb 2020 10:49:08 +0100 Subject: [PATCH 017/145] Move processing to resource --- ScoutSuite/providers/aws/provider.py | 28 ++----------------- .../aws/resources/cloudtrail/base.py | 19 +++++++++++++ 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/ScoutSuite/providers/aws/provider.py b/ScoutSuite/providers/aws/provider.py index f411b3291..fee5482a4 100644 --- a/ScoutSuite/providers/aws/provider.py +++ b/ScoutSuite/providers/aws/provider.py @@ -64,8 +64,10 @@ def preprocessing(self, ip_ranges=None, ip_ranges_name_key=None): # Various data processing calls # Note that order of processing can matter + # TODO - this should be moved to the `finalize` method of the base resource, as it's not cross-service self._map_all_subnets() + # TODO - this should be moved to the `finalize` method of the base resource, as it's not cross-service if 'ec2' in self.service_list: self._map_all_sgs() self._add_security_group_name_to_ec2_grants() @@ -78,15 +80,13 @@ def preprocessing(self, ip_ranges=None, ip_ranges_name_key=None): if 'ec2' in self.service_list and 'iam' in self.service_list: self._match_instances_and_roles() - if 'cloudtrail' in self.service_list: - self._process_cloudtrail_trails(self.services['cloudtrail']) - if 'elbv2' in self.service_list and 'ec2' in self.service_list: self._add_security_group_data_to_elbv2() if 's3' in self.service_list and 'iam' in self.service_list: self._match_iam_policies_and_buckets() + # TODO - this should be moved to the `finalize` method of the base resource, as it's not cross-service if 'elb' in self.services: self._parse_elb_policies() @@ -190,28 +190,6 @@ def add_security_group_name_to_ec2_grants_callback(self, current_config, path, c else: print_exception('Failed to handle EC2 grant: %s' % ec2_grant) - @staticmethod - def _process_cloudtrail_trails(cloudtrail_config): - global_events_logging = [] - data_logging_trails_count = 0 - for region in cloudtrail_config['regions']: - for trail_id in cloudtrail_config['regions'][region]['trails']: - trail = cloudtrail_config['regions'][region]['trails'][trail_id] - if 'HomeRegion' in trail and trail['HomeRegion'] != region: - # Part of a multi-region trail, skip until we find the whole object - continue - if trail['IncludeGlobalServiceEvents'] and trail['IsLogging']: - global_events_logging.append((region, trail_id,)) - # Any wildcard logging? - if trail.get('wildcard_data_logging', False): - data_logging_trails_count += 1 - - cloudtrail_config['data_logging_trails_count'] = data_logging_trails_count - cloudtrail_config['IncludeGlobalServiceEvents'] = len( - global_events_logging) > 0 - cloudtrail_config['DuplicatedGlobalServiceEvents'] = len( - global_events_logging) > 1 - def process_network_acls_callback(self, current_config, path, current_path, privateip_id, callback_args): # Check if the network ACL allows all traffic from all IP addresses self._process_network_acls_check_for_allow_all( diff --git a/ScoutSuite/providers/aws/resources/cloudtrail/base.py b/ScoutSuite/providers/aws/resources/cloudtrail/base.py index 3320d5f39..766a83223 100644 --- a/ScoutSuite/providers/aws/resources/cloudtrail/base.py +++ b/ScoutSuite/providers/aws/resources/cloudtrail/base.py @@ -11,3 +11,22 @@ class CloudTrail(Regions): def __init__(self, facade: AWSFacade): super(CloudTrail, self).__init__('cloudtrail', facade) + + async def finalize(self): + global_events_logging = [] + data_logging_trails_count = 0 + + for region in self['regions']: + for trail_id, trail in self['regions'][region]['trails'].items(): + if 'HomeRegion' in trail and trail['HomeRegion'] != region: + # Part of a multi-region trail, skip until we find the whole object + continue + if trail['IncludeGlobalServiceEvents'] and trail['IsLogging']: + global_events_logging.append((region, trail_id,)) + # Any wildcard logging? + if trail.get('wildcard_data_logging', False): + data_logging_trails_count += 1 + + self['data_logging_trails_count'] = data_logging_trails_count + self['IncludeGlobalServiceEvents'] = len(global_events_logging) > 0 + self['DuplicatedGlobalServiceEvents'] = len(global_events_logging) > 1 From 9e59a29c29afaf524597749e0971f98bd85cb5c9 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Wed, 5 Feb 2020 10:57:01 +0100 Subject: [PATCH 018/145] Update rationale --- .../aws/rules/findings/cloudtrail-no-log-file-validation.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ScoutSuite/providers/aws/rules/findings/cloudtrail-no-log-file-validation.json b/ScoutSuite/providers/aws/rules/findings/cloudtrail-no-log-file-validation.json index 909bb6514..087080fcd 100644 --- a/ScoutSuite/providers/aws/rules/findings/cloudtrail-no-log-file-validation.json +++ b/ScoutSuite/providers/aws/rules/findings/cloudtrail-no-log-file-validation.json @@ -1,6 +1,6 @@ { "description": "Log file validation disabled", - "rationale": "Description:

    The lack of log file validation prevents one from verifying the integrity of the log files.", + "rationale": "Description:

    The lack of log file validation prevents from verifying the integrity of the log files.", "path": "cloudtrail.regions.id.trails.id", "dashboard_name": "Trails", "display_path": "cloudtrail.regions.id.trails.id", From a06d3c1c55317cffeb3a7917a97cc1a45cdeee72 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Wed, 5 Feb 2020 10:57:50 +0100 Subject: [PATCH 019/145] Validate directly in the rule --- .../cloudtrail-no-global-services-logging.json | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/ScoutSuite/providers/aws/rules/findings/cloudtrail-no-global-services-logging.json b/ScoutSuite/providers/aws/rules/findings/cloudtrail-no-global-services-logging.json index e96702747..5a5b740c5 100644 --- a/ScoutSuite/providers/aws/rules/findings/cloudtrail-no-global-services-logging.json +++ b/ScoutSuite/providers/aws/rules/findings/cloudtrail-no-global-services-logging.json @@ -1,11 +1,17 @@ { "description": "Global services logging disabled", "rationale": "Description:

    API activity for global services such as IAM and STS is not logged. Investigation of incidents will be incomplete due to the lack of information.", - "path": "cloudtrail", - "display_path": "cloudtrail.regions.id.trails.id", - "dashboard_name": "Configuration", - "conditions": [ "and", - [ "IncludeGlobalServiceEvents", "false", "" ] + "path": "cloudtrail.regions.id.trails.id", + "dashboard_name": "Trails", + "conditions": [ "or", + [ "and", + [ "cloudtrail.regions.id.trails.id.", "withKey", "IncludeGlobalServiceEvents" ], + [ "cloudtrail.regions.id.trails.id.IncludeGlobalServiceEvents", "false", "" ] + ], + [ "and", + [ "cloudtrail.regions.id.trails.id.", "withKey", "IsLogging" ], + [ "cloudtrail.regions.id.trails.id.IsLogging", "false", "" ] + ] ], "id_suffix": "IncludeGlobalServiceEvents" } \ No newline at end of file From 86fc4ece5dcc254a23909a045947f4869e26382f Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Wed, 5 Feb 2020 10:58:04 +0100 Subject: [PATCH 020/145] Minor change --- .../findings/cloudtrail-duplicated-global-services-logging.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ScoutSuite/providers/aws/rules/findings/cloudtrail-duplicated-global-services-logging.json b/ScoutSuite/providers/aws/rules/findings/cloudtrail-duplicated-global-services-logging.json index 180b30994..02176427d 100644 --- a/ScoutSuite/providers/aws/rules/findings/cloudtrail-duplicated-global-services-logging.json +++ b/ScoutSuite/providers/aws/rules/findings/cloudtrail-duplicated-global-services-logging.json @@ -2,8 +2,8 @@ "description": "Global service logging duplicated", "rationale": "Description:

    Global service logging is enabled in multiple Trails. While this does not jeopardize the security of the environment, duplicated entries in logs increase the difficulty to investate potential incidents.", "path": "cloudtrail", - "display_path": "cloudtrail.regions.id.trails.id", "dashboard_name": "Configuration", + "display_path": "cloudtrail.regions.id", "conditions": [ "and", [ "DuplicatedGlobalServiceEvents", "true", ""] ], From 8e35df6d9268a199d47b39522761ab8920240729 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Thu, 6 Feb 2020 09:57:09 +0100 Subject: [PATCH 021/145] Add requirement --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 405f5c42d..8e4351c98 100644 --- a/requirements.txt +++ b/requirements.txt @@ -28,6 +28,7 @@ google-api-python-client>=1.7.8 oauth2client>=4.1.3 # Azure Provider +requests>=2.22.0 azure-core>=1.2.1 adal>=0.4.7 azure-cli-core>=2.0.55 From c6802df9d3c6e75aede6c5f561901c8436488633 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Thu, 6 Feb 2020 10:08:49 +0100 Subject: [PATCH 022/145] Handle MS Graph auth for username/password --- .../azure/authentication_strategy.py | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/ScoutSuite/providers/azure/authentication_strategy.py b/ScoutSuite/providers/azure/authentication_strategy.py index 36537b21b..57edaa1d8 100644 --- a/ScoutSuite/providers/azure/authentication_strategy.py +++ b/ScoutSuite/providers/azure/authentication_strategy.py @@ -13,9 +13,13 @@ class AzureCredentials: - def __init__(self, arm_credentials, graph_credentials, tenant_id=None, default_subscription_id=None): + def __init__(self, + arm_credentials, aad_graph_credentials, microsoft_graph_credentials, + tenant_id=None, default_subscription_id=None): + self.arm_credentials = arm_credentials # Azure Resource Manager API credentials - self.graph_credentials = graph_credentials # Azure AD Graph API credentials + self.aad_graph_credentials = aad_graph_credentials # Azure AD Graph API credentials + self.microsoft_graph_credentials = microsoft_graph_credentials # Microsoft Graph API credentials self.tenant_id = tenant_id self.default_subscription_id = default_subscription_id @@ -23,7 +27,7 @@ def get_tenant_id(self): if self.tenant_id: return self.tenant_id else: - return self.graph_credentials.token['tenant_id'] + return self.aad_graph_credentials.token['tenant_id'] class AzureAuthenticationStrategy(AuthenticationStrategy): @@ -49,7 +53,7 @@ def authenticate(self, if cli: arm_credentials, subscription_id, tenant_id = get_azure_cli_credentials(with_tenant=True) - graph_credentials, placeholder_1, placeholder_2 = \ + aad_graph_credentials, placeholder_1, placeholder_2 = \ get_azure_cli_credentials(with_tenant=True, resource='https://graph.windows.net') elif user_account: @@ -62,8 +66,10 @@ def authenticate(self, raise AuthenticationException('Username and/or password not set') arm_credentials = UserPassCredentials(username, password) - graph_credentials = UserPassCredentials(username, password, + aad_graph_credentials = UserPassCredentials(username, password, resource='https://graph.windows.net') + microsoft_graph_credentials = UserPassCredentials(username, password, + resource='https://graph.microsoft.com/') elif user_account_browser: @@ -92,7 +98,7 @@ def authenticate(self, 'access {} and enter the {} code.'.format(code['verification_url'], code['user_code'])) mgmt_token = context.acquire_token_with_device_code(resource_uri, code, client_id) - graph_credentials = AADTokenCredentials(mgmt_token, client_id) + aad_graph_credentials = AADTokenCredentials(mgmt_token, client_id) elif service_principal: @@ -120,7 +126,7 @@ def authenticate(self, tenant=tenant_id ) - graph_credentials = ServicePrincipalCredentials( + aad_graph_credentials = ServicePrincipalCredentials( client_id=client_id, secret=client_secret, tenant=tenant_id, @@ -140,7 +146,7 @@ def authenticate(self, tenant=tenant_id ) - graph_credentials = ServicePrincipalCredentials( + aad_graph_credentials = ServicePrincipalCredentials( client_id=client_id, secret=client_secret, tenant=tenant_id, @@ -150,12 +156,12 @@ def authenticate(self, elif msi: arm_credentials = MSIAuthentication() - graph_credentials = MSIAuthentication(resource='https://graph.windows.net') + aad_graph_credentials = MSIAuthentication(resource='https://graph.windows.net') else: raise AuthenticationException('Unknown authentication method') - return AzureCredentials(arm_credentials, graph_credentials, + return AzureCredentials(arm_credentials, aad_graph_credentials, microsoft_graph_credentials, tenant_id, subscription_id) except Exception as e: From 5da41d979e5ee9ba013091c895ab1e1cffdc6a40 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Thu, 6 Feb 2020 10:10:51 +0100 Subject: [PATCH 023/145] Add query method --- ScoutSuite/providers/azure/facade/aad.py | 28 +++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/ScoutSuite/providers/azure/facade/aad.py b/ScoutSuite/providers/azure/facade/aad.py index 7271ec4aa..3b13dff33 100644 --- a/ScoutSuite/providers/azure/facade/aad.py +++ b/ScoutSuite/providers/azure/facade/aad.py @@ -1,14 +1,40 @@ from azure.graphrbac import GraphRbacManagementClient +import requests +import uuid + from ScoutSuite.core.console import print_exception from ScoutSuite.providers.utils import run_concurrently class AADFacade: def __init__(self, credentials): - self._client = GraphRbacManagementClient(credentials.graph_credentials, + self._client = GraphRbacManagementClient(credentials.aad_graph_credentials, tenant_id=credentials.get_tenant_id()) + self._microsoft_graph_session = requests.Session() + self._microsoft_graph_session.headers.update( + {'Authorization': 'Bearer {}'.format(credentials.microsoft_graph_credentials.token['access_token']), + 'User-Agent': 'adal-sample', + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'SdkVersion': 'sample-python-adal', + 'return-client-request-id': 'true'}) + + async def _get_microsoft_graph_response(self, api_resource, api_version='v1.0'): + endpoint = 'https://graph.microsoft.com/{}/{}'.format(api_version, api_resource) + http_headers = {'client-request-id': str(uuid.uuid4())} + try: + response = self._microsoft_graph_session.get(endpoint, headers=http_headers, stream=False) + if response.status_code == 200: + return response.json() + else: + print_exception('Failed to query Microsoft Graph endpoint {}: status code {}'. + format(api_resource, response.status_code)) + except Exception as e: + print_exception('Failed to query Microsoft Graph endpoint {}: {}'.format(api_resource, e)) + return {} + async def get_users(self): try: return await run_concurrently(lambda: list(self._client.users.list())) From d649ffcc6fffc314173af327d79af2d79171d8f9 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Thu, 6 Feb 2020 17:49:38 +0100 Subject: [PATCH 024/145] Sample MS Graph implementation --- ScoutSuite/providers/azure/facade/aad.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/ScoutSuite/providers/azure/facade/aad.py b/ScoutSuite/providers/azure/facade/aad.py index 3b13dff33..3c4c783bd 100644 --- a/ScoutSuite/providers/azure/facade/aad.py +++ b/ScoutSuite/providers/azure/facade/aad.py @@ -15,10 +15,10 @@ def __init__(self, credentials): self._microsoft_graph_session = requests.Session() self._microsoft_graph_session.headers.update( {'Authorization': 'Bearer {}'.format(credentials.microsoft_graph_credentials.token['access_token']), - 'User-Agent': 'adal-sample', + 'User-Agent': 'scout-suite', 'Accept': 'application/json', 'Content-Type': 'application/json', - 'SdkVersion': 'sample-python-adal', + 'SdkVersion': 'scout-suite', 'return-client-request-id': 'true'}) async def _get_microsoft_graph_response(self, api_resource, api_version='v1.0'): @@ -29,10 +29,10 @@ async def _get_microsoft_graph_response(self, api_resource, api_version='v1.0'): if response.status_code == 200: return response.json() else: - print_exception('Failed to query Microsoft Graph endpoint {}: status code {}'. + print_exception('Failed to query Microsoft Graph endpoint \"{}\": status code {}'. format(api_resource, response.status_code)) except Exception as e: - print_exception('Failed to query Microsoft Graph endpoint {}: {}'.format(api_resource, e)) + print_exception('Failed to query Microsoft Graph endpoint \"{}\": {}'.format(api_resource, e)) return {} async def get_users(self): @@ -49,6 +49,10 @@ async def get_groups(self): print_exception('Failed to retrieve groups: {}'.format(e)) return [] + async def get_group_details(self, group_id): + response = await self._get_microsoft_graph_response('groups/{}'.format(group_id)) + return response + async def get_user_groups(self, user_id): try: return await run_concurrently(lambda: list( From d8f2ebfc2117956f039b8596d9039af76ad49a98 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Thu, 6 Feb 2020 17:49:50 +0100 Subject: [PATCH 025/145] Sample MS Graph implementation --- ScoutSuite/providers/azure/resources/aad/groups.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ScoutSuite/providers/azure/resources/aad/groups.py b/ScoutSuite/providers/azure/resources/aad/groups.py index 5a97e3aae..bc255be5a 100644 --- a/ScoutSuite/providers/azure/resources/aad/groups.py +++ b/ScoutSuite/providers/azure/resources/aad/groups.py @@ -4,11 +4,13 @@ class Groups(AzureResources): async def fetch_all(self): for raw_group in await self.facade.aad.get_groups(): - id, group = self._parse_group(raw_group) + id, group = await self._parse_group(raw_group) self[id] = group - def _parse_group(self, raw_group): + async def _parse_group(self, raw_group): + group_dict = {} + group_dict['id'] = raw_group.object_id group_dict['name'] = raw_group.display_name group_dict['additional_properties'] = raw_group.additional_properties @@ -20,5 +22,8 @@ def _parse_group(self, raw_group): group_dict['mail'] = raw_group.mail group_dict['users'] = [] # this will be filled in `finalize()` group_dict['roles'] = [] # this will be filled in `finalize()` + + additional_details = await self.facade.aad.get_group_details(group_dict['id']) + return group_dict['id'], group_dict From b39c69570591df98175fe530fec13ead36551850 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Thu, 6 Feb 2020 18:26:41 +0100 Subject: [PATCH 026/145] Add query methods --- .../providers/azure/facade/virtualmachines.py | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/ScoutSuite/providers/azure/facade/virtualmachines.py b/ScoutSuite/providers/azure/facade/virtualmachines.py index d490bd3d1..258c5db18 100644 --- a/ScoutSuite/providers/azure/facade/virtualmachines.py +++ b/ScoutSuite/providers/azure/facade/virtualmachines.py @@ -21,3 +21,33 @@ async def get_instances(self, subscription_id: str): except Exception as e: print_exception('Failed to retrieve virtual machines: {}'.format(e)) return [] + + async def get_disks(self, subscription_id: str): + try: + client = self.get_client(subscription_id) + return await run_concurrently( + lambda: list(client.disks.list()) + ) + except Exception as e: + print_exception('Failed to retrieve disks: {}'.format(e)) + return [] + + async def get_snapshots(self, subscription_id: str): + try: + client = self.get_client(subscription_id) + return await run_concurrently( + lambda: list(client.snapshots.list()) + ) + except Exception as e: + print_exception('Failed to retrieve snapshots: {}'.format(e)) + return [] + + async def get_images(self, subscription_id: str): + try: + client = self.get_client(subscription_id) + return await run_concurrently( + lambda: list(client.images.list()) + ) + except Exception as e: + print_exception('Failed to retrieve images: {}'.format(e)) + return [] From 0aa5be2c04e7339eebbae9aa5eeacfd4f18eea77 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Thu, 6 Feb 2020 18:26:52 +0100 Subject: [PATCH 027/145] Add resources --- .../providers/azure/resources/virtualmachines/base.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ScoutSuite/providers/azure/resources/virtualmachines/base.py b/ScoutSuite/providers/azure/resources/virtualmachines/base.py index 008cb460c..0f80673bf 100644 --- a/ScoutSuite/providers/azure/resources/virtualmachines/base.py +++ b/ScoutSuite/providers/azure/resources/virtualmachines/base.py @@ -1,9 +1,15 @@ from ScoutSuite.providers.azure.resources.subscriptions import Subscriptions from .instances import Instances +from .disks import Disks +from .snapshots import Snapshots +from .images import Images class VirtualMachines(Subscriptions): _children = [ - (Instances, 'instances') + (Instances, 'instances'), + (Disks, 'disks'), + (Snapshots, 'snapshots'), + (Images, 'images'), ] From b4c9749b07c836a54fdeee17bbbf125c30a53e64 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Thu, 6 Feb 2020 18:54:20 +0100 Subject: [PATCH 028/145] Add resources for VM service --- ...irtualmachines.subscriptions.id.disks.html | 33 +++++++++++++ ...rtualmachines.subscriptions.id.images.html | 1 + ...almachines.subscriptions.id.snapshots.html | 32 +++++++++++++ .../azure/resources/virtualmachines/disks.py | 48 +++++++++++++++++++ .../azure/resources/virtualmachines/images.py | 19 ++++++++ .../resources/virtualmachines/snapshots.py | 46 ++++++++++++++++++ 6 files changed, 179 insertions(+) create mode 100644 ScoutSuite/output/data/html/partials/azure/services.virtualmachines.subscriptions.id.disks.html create mode 100644 ScoutSuite/output/data/html/partials/azure/services.virtualmachines.subscriptions.id.images.html create mode 100644 ScoutSuite/output/data/html/partials/azure/services.virtualmachines.subscriptions.id.snapshots.html create mode 100644 ScoutSuite/providers/azure/resources/virtualmachines/disks.py create mode 100644 ScoutSuite/providers/azure/resources/virtualmachines/images.py create mode 100644 ScoutSuite/providers/azure/resources/virtualmachines/snapshots.py diff --git a/ScoutSuite/output/data/html/partials/azure/services.virtualmachines.subscriptions.id.disks.html b/ScoutSuite/output/data/html/partials/azure/services.virtualmachines.subscriptions.id.disks.html new file mode 100644 index 000000000..96d60ad33 --- /dev/null +++ b/ScoutSuite/output/data/html/partials/azure/services.virtualmachines.subscriptions.id.disks.html @@ -0,0 +1,33 @@ + + + + + + + + diff --git a/ScoutSuite/output/data/html/partials/azure/services.virtualmachines.subscriptions.id.images.html b/ScoutSuite/output/data/html/partials/azure/services.virtualmachines.subscriptions.id.images.html new file mode 100644 index 000000000..4d3fbc92c --- /dev/null +++ b/ScoutSuite/output/data/html/partials/azure/services.virtualmachines.subscriptions.id.images.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ScoutSuite/output/data/html/partials/azure/services.virtualmachines.subscriptions.id.snapshots.html b/ScoutSuite/output/data/html/partials/azure/services.virtualmachines.subscriptions.id.snapshots.html new file mode 100644 index 000000000..eb69d2cf6 --- /dev/null +++ b/ScoutSuite/output/data/html/partials/azure/services.virtualmachines.subscriptions.id.snapshots.html @@ -0,0 +1,32 @@ + + + + + + + + diff --git a/ScoutSuite/providers/azure/resources/virtualmachines/disks.py b/ScoutSuite/providers/azure/resources/virtualmachines/disks.py new file mode 100644 index 000000000..40c91ef0d --- /dev/null +++ b/ScoutSuite/providers/azure/resources/virtualmachines/disks.py @@ -0,0 +1,48 @@ +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 Disks(AzureResources): + + def __init__(self, facade: AzureFacade, subscription_id: str): + super(Disks, self).__init__(facade) + self.subscription_id = subscription_id + + async def fetch_all(self): + for raw_disk in await self.facade.virtualmachines.get_disks(self.subscription_id): + id, disk = self._parse_disk(raw_disk) + self[id] = disk + + def _parse_disk(self, raw_disk): + disk_dict = {} + disk_dict['id'] = get_non_provider_id(raw_disk.id) + disk_dict['name'] = raw_disk.name + disk_dict['type'] = raw_disk.type + disk_dict['location'] = raw_disk.location + disk_dict['tags'] = raw_disk.tags + disk_dict['managed_by'] = raw_disk.managed_by + disk_dict['sku'] = raw_disk.sku + disk_dict['zones'] = raw_disk.zones + disk_dict['time_created'] = raw_disk.time_created + disk_dict['os_type'] = raw_disk.os_type + disk_dict['hyper_vgeneration'] = raw_disk.hyper_vgeneration + disk_dict['creation_data'] = raw_disk.creation_data + disk_dict['disk_size_gb'] = raw_disk.disk_size_gb + disk_dict['disk_size_bytes'] = raw_disk.disk_size_bytes + disk_dict['unique_id'] = raw_disk.unique_id + disk_dict['provisioning_state'] = raw_disk.provisioning_state + disk_dict['disk_iops_read_write'] = raw_disk.disk_iops_read_write + disk_dict['disk_mbps_read_write'] = raw_disk.disk_mbps_read_write + disk_dict['disk_state'] = raw_disk.disk_state + disk_dict['additional_properties'] = raw_disk.additional_properties + + disk_dict['encryption'] = raw_disk.encryption + disk_dict['encryption_settings_collection'] = raw_disk.encryption_settings_collection + if raw_disk.encryption_settings_collection and raw_disk.encryption_settings_collection.enabled: + disk_dict['encryption_enabled'] = True + else: + disk_dict['encryption_enabled'] = False + + return disk_dict['id'], disk_dict + diff --git a/ScoutSuite/providers/azure/resources/virtualmachines/images.py b/ScoutSuite/providers/azure/resources/virtualmachines/images.py new file mode 100644 index 000000000..3ef125777 --- /dev/null +++ b/ScoutSuite/providers/azure/resources/virtualmachines/images.py @@ -0,0 +1,19 @@ +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 Images(AzureResources): + + def __init__(self, facade: AzureFacade, subscription_id: str): + super(Images, self).__init__(facade) + self.subscription_id = subscription_id + + async def fetch_all(self): + for raw_image in await self.facade.virtualmachines.get_images(self.subscription_id): + id, image = self._parse_image(raw_image) + self[id] = image + + def _parse_image(self, raw_image): + # TODO + pass diff --git a/ScoutSuite/providers/azure/resources/virtualmachines/snapshots.py b/ScoutSuite/providers/azure/resources/virtualmachines/snapshots.py new file mode 100644 index 000000000..b7c9541b0 --- /dev/null +++ b/ScoutSuite/providers/azure/resources/virtualmachines/snapshots.py @@ -0,0 +1,46 @@ +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 Snapshots(AzureResources): + + def __init__(self, facade: AzureFacade, subscription_id: str): + super(Snapshots, self).__init__(facade) + self.subscription_id = subscription_id + + async def fetch_all(self): + for raw_snapshot in await self.facade.virtualmachines.get_snapshots(self.subscription_id): + id, snapshot = self._parse_snapshot(raw_snapshot) + self[id] = snapshot + + def _parse_snapshot(self, raw_snapshot): + snapshot_dict = {} + + snapshot_dict['id'] = get_non_provider_id(raw_snapshot.id) + snapshot_dict['name'] = raw_snapshot.name + snapshot_dict['type'] = raw_snapshot.type + snapshot_dict['location'] = raw_snapshot.location + snapshot_dict['tags'] = raw_snapshot.tags + snapshot_dict['managed_by'] = raw_snapshot.managed_by + snapshot_dict['sku'] = raw_snapshot.sku + snapshot_dict['time_created'] = raw_snapshot.time_created + snapshot_dict['os_type'] = raw_snapshot.os_type + snapshot_dict['hyper_vgeneration'] = raw_snapshot.hyper_vgeneration + snapshot_dict['creation_data'] = raw_snapshot.creation_data + snapshot_dict['disk_size_gb'] = raw_snapshot.disk_size_gb + snapshot_dict['disk_size_bytes'] = raw_snapshot.disk_size_bytes + snapshot_dict['unique_id'] = raw_snapshot.unique_id + snapshot_dict['provisioning_state'] = raw_snapshot.provisioning_state + snapshot_dict['incremental'] = raw_snapshot.incremental + snapshot_dict['additional_properties'] = raw_snapshot.additional_properties + + snapshot_dict['encryption'] = raw_snapshot.encryption + snapshot_dict['encryption_settings_collection'] = raw_snapshot.encryption_settings_collection + if raw_snapshot.encryption_settings_collection and raw_snapshot.encryption_settings_collection.enabled: + snapshot_dict['encryption_enabled'] = True + else: + snapshot_dict['encryption_enabled'] = False + + return snapshot_dict['id'], snapshot_dict + From 25ee505665ccf215704da79c7587ed45ed53c709 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Thu, 6 Feb 2020 18:54:39 +0100 Subject: [PATCH 029/145] Show resources --- ScoutSuite/providers/azure/metadata.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ScoutSuite/providers/azure/metadata.json b/ScoutSuite/providers/azure/metadata.json index 68e0208a0..a2dc55553 100644 --- a/ScoutSuite/providers/azure/metadata.json +++ b/ScoutSuite/providers/azure/metadata.json @@ -15,6 +15,14 @@ "instances": { "cols": 2, "path": "services.virtualmachines.subscriptions.id.instances" + }, + "disks": { + "cols": 2, + "path": "services.virtualmachines.subscriptions.id.disks" + }, + "snapshots": { + "cols": 2, + "path": "services.virtualmachines.subscriptions.id.snapshots" } } } From 6f8d4333c5b5c48a59e0359b4f26d82c644f6925 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Fri, 7 Feb 2020 15:13:31 +0100 Subject: [PATCH 030/145] Improve error handling --- ScoutSuite/providers/oci/facade/identity.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ScoutSuite/providers/oci/facade/identity.py b/ScoutSuite/providers/oci/facade/identity.py index f050770dc..eaa2ce1bb 100644 --- a/ScoutSuite/providers/oci/facade/identity.py +++ b/ScoutSuite/providers/oci/facade/identity.py @@ -19,7 +19,7 @@ async def get_users(self): return response.data except Exception as e: print_exception('Failed to retrieve users: {}'.format(e)) - return None + return [] async def get_user_api_keys(self, user_id): try: @@ -28,7 +28,7 @@ async def get_user_api_keys(self, user_id): return response.data except Exception as e: print_exception('Failed to retrieve user api keys: {}'.format(e)) - return None + return [] async def get_groups(self): try: @@ -37,7 +37,7 @@ async def get_groups(self): return response.data except Exception as e: print_exception('Failed to retrieve groups: {}'.format(e)) - return None + return [] async def get_group_users(self, group_id): try: @@ -48,7 +48,7 @@ async def get_group_users(self, group_id): return response.data except Exception as e: print_exception('Failed to retrieve group users: {}'.format(e)) - return None + return [] async def get_policies(self): try: @@ -66,4 +66,4 @@ async def get_authentication_policy(self): return response.data except Exception as e: print_exception('Failed to retrieve authentication policy: {}'.format(e)) - return None + return [] From f04cfff53e6bc30bc264c2dbe97c88a8d743c57b Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Fri, 7 Feb 2020 15:41:19 +0100 Subject: [PATCH 031/145] Improve error handling and add support for compartment scans --- .../providers/oci/authentication_strategy.py | 15 ++++--- ScoutSuite/providers/oci/facade/identity.py | 10 ++--- ScoutSuite/providers/oci/facade/kms.py | 5 +-- .../providers/oci/facade/objectstorage.py | 44 +++++++++++++------ ScoutSuite/providers/oci/provider.py | 2 +- .../oci/resources/objectstorage/buckets.py | 32 +++++++------- 6 files changed, 64 insertions(+), 44 deletions(-) diff --git a/ScoutSuite/providers/oci/authentication_strategy.py b/ScoutSuite/providers/oci/authentication_strategy.py index d7748779f..06f2f90ac 100644 --- a/ScoutSuite/providers/oci/authentication_strategy.py +++ b/ScoutSuite/providers/oci/authentication_strategy.py @@ -8,9 +8,14 @@ class OracleCredentials: - def __init__(self, config, compartment_id): + def __init__(self, config): self.config = config - self.compartment_id = compartment_id + + def get_scope(self): + if 'compartment-id' in self.config: + return self.config['compartment-id'] + else: + return self.config['tenancy'] class OracleAuthenticationStrategy(AuthenticationStrategy): @@ -26,16 +31,12 @@ def authenticate(self, profile=None, **kwargs): logging.getLogger('oci').setLevel(logging.ERROR) config = from_file(profile_name=profile) - if 'compartment-id' in config: - compartment_id = config['compartment-id'] - else: - compartment_id = config['tenancy'] # Get the current user identity = IdentityClient(config) identity.get_user(config["user"]).data - return OracleCredentials(config, compartment_id) + return OracleCredentials(config) except Exception as e: raise AuthenticationException(e) diff --git a/ScoutSuite/providers/oci/facade/identity.py b/ScoutSuite/providers/oci/facade/identity.py index eaa2ce1bb..19f651e20 100644 --- a/ScoutSuite/providers/oci/facade/identity.py +++ b/ScoutSuite/providers/oci/facade/identity.py @@ -15,7 +15,7 @@ def __init__(self, credentials: OracleCredentials): async def get_users(self): try: response = await run_concurrently( - lambda: list_call_get_all_results(self._client.list_users, self._credentials.compartment_id)) + lambda: list_call_get_all_results(self._client.list_users, self._credentials.get_scope())) return response.data except Exception as e: print_exception('Failed to retrieve users: {}'.format(e)) @@ -33,7 +33,7 @@ async def get_user_api_keys(self, user_id): async def get_groups(self): try: response = await run_concurrently( - lambda: list_call_get_all_results(self._client.list_groups, self._credentials.compartment_id)) + lambda: list_call_get_all_results(self._client.list_groups, self._credentials.get_scope())) return response.data except Exception as e: print_exception('Failed to retrieve groups: {}'.format(e)) @@ -43,7 +43,7 @@ async def get_group_users(self, group_id): try: response = await run_concurrently( lambda: list_call_get_all_results(self._client.list_user_group_memberships, - self._credentials.compartment_id, + self._credentials.get_scope(), group_id=group_id)) return response.data except Exception as e: @@ -53,7 +53,7 @@ async def get_group_users(self, group_id): async def get_policies(self): try: response = await run_concurrently( - lambda: list_call_get_all_results(self._client.list_policies, self._credentials.compartment_id)) + lambda: list_call_get_all_results(self._client.list_policies, self._credentials.get_scope())) return response.data except Exception as e: print_exception('Failed to retrieve policies: {}'.format(e)) @@ -62,7 +62,7 @@ async def get_policies(self): async def get_authentication_policy(self): try: response = await run_concurrently( - lambda: self._client.get_authentication_policy(self._credentials.compartment_id)) + lambda: self._client.get_authentication_policy(self._credentials.config['tenancy'])) return response.data except Exception as e: print_exception('Failed to retrieve authentication policy: {}'.format(e)) diff --git a/ScoutSuite/providers/oci/facade/kms.py b/ScoutSuite/providers/oci/facade/kms.py index 358dd2edf..937573b9f 100644 --- a/ScoutSuite/providers/oci/facade/kms.py +++ b/ScoutSuite/providers/oci/facade/kms.py @@ -9,13 +9,12 @@ class KMSFacade: def __init__(self, credentials: OracleCredentials): self._credentials = credentials - # FIXME does this require regional support? self._vault_client = KmsVaultClient(self._credentials.config) async def get_vaults(self): try: response = await run_concurrently( - lambda: list_call_get_all_results(self._vault_client.list_vaults, self._credentials.compartment_id)) + lambda: list_call_get_all_results(self._vault_client.list_vaults, self._credentials.get_scope())) return response.data except Exception as e: print_exception('Failed to get KMS vaults: {}'.format(e)) @@ -25,7 +24,7 @@ async def get_keys(self, keyvault): try: key_client = KmsManagementClient(self._credentials.config, keyvault['management_endpoint']) response = await run_concurrently( - lambda: list_call_get_all_results(key_client.list_keys, self._credentials.compartment_id)) + lambda: list_call_get_all_results(key_client.list_keys, self._credentials.get_scope())) return response.data except Exception as e: print_exception('Failed to get KMS vaults: {}'.format(e)) diff --git a/ScoutSuite/providers/oci/facade/objectstorage.py b/ScoutSuite/providers/oci/facade/objectstorage.py index 1dd637817..7ae836080 100644 --- a/ScoutSuite/providers/oci/facade/objectstorage.py +++ b/ScoutSuite/providers/oci/facade/objectstorage.py @@ -3,6 +3,7 @@ from oci.pagination import list_call_get_all_results from ScoutSuite.providers.utils import run_concurrently +from ScoutSuite.core.console import print_exception class ObjectStorageFacade: @@ -11,22 +12,39 @@ def __init__(self, credentials: OracleCredentials): self._client = ObjectStorageClient(self._credentials.config) async def get_namespace(self): - response = await run_concurrently( - lambda: list_call_get_all_results(self._client.get_namespace)) - # for some reason it returns a list of chars instead of a string - return ''.join(response.data) + try: + response = await run_concurrently( + lambda: list_call_get_all_results(self._client.get_namespace)) + # for some reason it returns a list of chars instead of a string + return ''.join(response.data) + except Exception as e: + print_exception('Failed to get Object Storage namespace: {}'.format(e)) + return None async def get_bucket_details(self, namespace, bucket_name): - response = await run_concurrently( - lambda: self._client.get_bucket(namespace, bucket_name)) - return response.data + try: + response = await run_concurrently( + lambda: self._client.get_bucket(namespace, bucket_name) + ) + return response.data + except Exception as e: + print_exception('Failed to get Object Storage bucket details: {}'.format(e)) + return None async def get_buckets(self, namespace): - response = await run_concurrently( - lambda: list_call_get_all_results(self._client.list_buckets, namespace, self._credentials.compartment_id)) - return response.data + try: + response = await run_concurrently( + lambda: list_call_get_all_results(self._client.list_buckets, namespace, self._credentials.get_scope())) + return response.data + except Exception as e: + print_exception('Failed to get Object Storage buckets: {}'.format(e)) + return [] async def get_bucket_objects(self, namespace, bucket_name): - response = await run_concurrently( - lambda: list_call_get_all_results(self._client.list_objects, namespace, bucket_name)) - return response.data + try: + response = await run_concurrently( + lambda: list_call_get_all_results(self._client.list_objects, namespace, bucket_name)) + return response.data + except Exception as e: + print_exception('Failed to get Object Storage bucket objects: {}'.format(e)) + return [] diff --git a/ScoutSuite/providers/oci/provider.py b/ScoutSuite/providers/oci/provider.py index 862096558..f92995fe0 100644 --- a/ScoutSuite/providers/oci/provider.py +++ b/ScoutSuite/providers/oci/provider.py @@ -24,7 +24,7 @@ def __init__(self, self.services_config = OracleServicesConfig self.credentials = kwargs['credentials'] - self.account_id = self.credentials.compartment_id + self.account_id = self.credentials.get_scope() super(OracleProvider, self).__init__(report_dir, timestamp, services, skipped_services) diff --git a/ScoutSuite/providers/oci/resources/objectstorage/buckets.py b/ScoutSuite/providers/oci/resources/objectstorage/buckets.py index eb9e24c29..50f35502a 100644 --- a/ScoutSuite/providers/oci/resources/objectstorage/buckets.py +++ b/ScoutSuite/providers/oci/resources/objectstorage/buckets.py @@ -17,24 +17,26 @@ async def fetch_all(self): async def _parse_bucket(self, raw_bucket): bucket_dict = {} + bucket_dict['id'] = bucket_dict['name'] = raw_bucket.name + bucket_dict['compartment_id'] = raw_bucket.compartment_id + bucket_dict['namespace'] = raw_bucket.namespace + bucket_dict['created_by'] = raw_bucket.created_by + bucket_dict['etag'] = raw_bucket.etag + bucket_dict['freeform_tags'] = list(raw_bucket.freeform_tags) if raw_bucket.freeform_tags else [] + bucket_dict['defined_tags'] = list(raw_bucket.defined_tags) if raw_bucket.defined_tags else [] + raw_bucket_details = await self.facade.objectstorage.get_bucket_details(raw_bucket.namespace, raw_bucket.name) - bucket_dict['id'] = bucket_dict['name'] = raw_bucket_details.name - bucket_dict['kms_key_id'] = raw_bucket_details.kms_key_id - bucket_dict['compartment_id'] = raw_bucket_details.compartment_id - bucket_dict['approximate_count'] = raw_bucket_details.approximate_count - bucket_dict['namespace'] = raw_bucket_details.namespace - bucket_dict['created_by'] = raw_bucket_details.created_by - bucket_dict['etag'] = raw_bucket_details.etag - bucket_dict['time_created'] = raw_bucket_details.time_created - bucket_dict['public_access_type'] = raw_bucket_details.public_access_type - bucket_dict['approximate_size'] = raw_bucket_details.approximate_size - bucket_dict['storage_tier'] = raw_bucket_details.storage_tier - bucket_dict['metadata'] = list(raw_bucket_details.metadata) - bucket_dict['freeform_tags'] = list(raw_bucket_details.freeform_tags) - bucket_dict['defined_tags'] = list(raw_bucket_details.defined_tags) - bucket_dict['object_lifecycle_policy_etag'] = raw_bucket_details.object_lifecycle_policy_etag + bucket_dict['kms_key_id'] = raw_bucket_details.kms_key_id if raw_bucket_details else None + bucket_dict['approximate_count'] = raw_bucket_details.approximate_count if raw_bucket_details else None + bucket_dict['time_created'] = raw_bucket_details.time_created if raw_bucket_details else None + bucket_dict['public_access_type'] = raw_bucket_details.public_access_type if raw_bucket_details else None + bucket_dict['approximate_size'] = raw_bucket_details.approximate_size if raw_bucket_details else None + bucket_dict['storage_tier'] = raw_bucket_details.storage_tier if raw_bucket_details else None + bucket_dict['metadata'] = list(raw_bucket_details.metadata) if raw_bucket_details else None + bucket_dict['object_lifecycle_policy_etag'] = raw_bucket_details.object_lifecycle_policy_etag if \ + raw_bucket_details else None # objects = await self.facade.objectstorage.get_bucket_objects(bucket_dict['namespace'], # bucket_dict['name']) From 008d456df1b19fe2cc97b35fd4f2ce8d0c14fba0 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Fri, 7 Feb 2020 15:48:29 +0100 Subject: [PATCH 032/145] Set python version --- scout.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scout.py b/scout.py index 6d2ae751a..4758e6d06 100755 --- a/scout.py +++ b/scout.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import sys From af098715324b8652cb5266866f8fa399242795bb Mon Sep 17 00:00:00 2001 From: Nick Klauer Date: Fri, 7 Feb 2020 10:43:02 -0600 Subject: [PATCH 033/145] feat: introduce kms resource --- ScoutSuite/providers/aws/facade/kms.py | 34 +++++++++++++++++++ .../providers/aws/resources/kms/__init__.py | 0 .../providers/aws/resources/kms/aliases.py | 21 ++++++++++++ .../providers/aws/resources/kms/base.py | 14 ++++++++ ScoutSuite/providers/aws/services.py | 2 ++ 5 files changed, 71 insertions(+) create mode 100644 ScoutSuite/providers/aws/facade/kms.py create mode 100644 ScoutSuite/providers/aws/resources/kms/__init__.py create mode 100644 ScoutSuite/providers/aws/resources/kms/aliases.py create mode 100644 ScoutSuite/providers/aws/resources/kms/base.py diff --git a/ScoutSuite/providers/aws/facade/kms.py b/ScoutSuite/providers/aws/facade/kms.py new file mode 100644 index 000000000..2a19ca185 --- /dev/null +++ b/ScoutSuite/providers/aws/facade/kms.py @@ -0,0 +1,34 @@ +from ScoutSuite.core.console import print_exception +from ScoutSuite.providers.aws.facade.basefacade import AWSBaseFacade +from ScoutSuite.providers.aws.facade.utils import AWSFacadeUtils + + +class KMSFacade(AWSBaseFacade): + + async def get_aliases(self): + try: + return await AWSFacadeUtils.get_all_pages('kms', None, self.session, 'list_aliases', 'Aliases') + except Exception as e: + print_exception('Failed to list KMS Aliases: {}'.format(e)) + return [] + + async def get_grants(self): + try: + return await AWSFacadeUtils.get_all_pages('kms', None, self.session, 'list_grants', 'Grants') + except Exception as e: + print_exception('Failed to list KMS Grants: {}'.format(e)) + return [] + + async def get_key_policies(self): + try: + return await AWSFacadeUtils.get_all_pages('kms', None, self.session, 'list_key_policies', 'PolicyNames') + except Exception as e: + print_exception('Failed to list KMS Key Policies: {}'.format(e)) + return [] + + async def get_keys(self): + try: + return await AWSFacadeUtils.get_all_pages('kms', None, self.session, 'list_keys', 'Keys') + except Exception as e: + print_exception('Failed to list KMS Keys: {}'.format(e)) + return [] diff --git a/ScoutSuite/providers/aws/resources/kms/__init__.py b/ScoutSuite/providers/aws/resources/kms/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/ScoutSuite/providers/aws/resources/kms/aliases.py b/ScoutSuite/providers/aws/resources/kms/aliases.py new file mode 100644 index 000000000..c33603004 --- /dev/null +++ b/ScoutSuite/providers/aws/resources/kms/aliases.py @@ -0,0 +1,21 @@ +from ScoutSuite.providers.aws.facade.base import AWSFacade +from ScoutSuite.providers.aws.resources.base import AWSResources + + +class Aliases(AWSResources): + def __init__(self, facade: AWSFacade, region: str): + super(Aliases, self).__init__(facade) + self.region = region + + async def fetch_all(self): + raw_aliases = await self.facade.kms.get_aliases() + for raw_alias in raw_aliases: + id, alias = self._parse_alias(raw_alias) + self[id] = alias + + def _parse_alias(self, raw_alias): + alias_dict = { + 'name': raw_alias.get('AliasName'), + 'arn': raw_alias.get('AliasArn'), + 'key_id': raw_alias.get('TargetKeyId')} + return alias_dict['name'], alias_dict diff --git a/ScoutSuite/providers/aws/resources/kms/base.py b/ScoutSuite/providers/aws/resources/kms/base.py new file mode 100644 index 000000000..895a0a1d5 --- /dev/null +++ b/ScoutSuite/providers/aws/resources/kms/base.py @@ -0,0 +1,14 @@ +from ScoutSuite.providers.aws.facade.base import AWSFacade +from ScoutSuite.providers.aws.resources.base import AWSCompositeResources + +class KMS(AWSCompositeResources): + _children = [ + (Aliases, 'aliases') + ] + + def __init__(self, facade: AWSFacade): + super(KMS, self).__init__(facade) + self.service = 'kms' + + async def fetch_all(self, partition_name='aws', **kwargs): + await self._fetch_children(self) diff --git a/ScoutSuite/providers/aws/services.py b/ScoutSuite/providers/aws/services.py index b65ee9993..8ce5a9318 100644 --- a/ScoutSuite/providers/aws/services.py +++ b/ScoutSuite/providers/aws/services.py @@ -12,6 +12,7 @@ from ScoutSuite.providers.aws.resources.elbv2.base import ELBv2 from ScoutSuite.providers.aws.resources.emr.base import EMR from ScoutSuite.providers.aws.resources.iam.base import IAM +from ScoutSuite.providers.aws.resources.kms.base import KMS from ScoutSuite.providers.aws.resources.rds.base import RDS from ScoutSuite.providers.aws.resources.redshift.base import Redshift from ScoutSuite.providers.aws.resources.route53.base import Route53 @@ -71,6 +72,7 @@ def __init__(self, credentials=None, **kwargs): self.elbv2 = ELBv2(facade) self.emr = EMR(facade) self.iam = IAM(facade) + self.kms = KMS(facade) self.rds = RDS(facade) self.redshift = Redshift(facade) self.route53 = Route53(facade) From a9fcda77b22c80435767ceec7d6ba18eb863f140 Mon Sep 17 00:00:00 2001 From: Nick Klauer Date: Fri, 7 Feb 2020 11:16:33 -0600 Subject: [PATCH 034/145] fix: get KMS aliases to parse out --- ScoutSuite/providers/aws/facade/base.py | 2 ++ ScoutSuite/providers/aws/facade/kms.py | 17 ++++++++--------- .../providers/aws/resources/kms/aliases.py | 5 +++-- ScoutSuite/providers/aws/resources/kms/base.py | 12 ++++++------ .../providers/aws/resources/route53/domains.py | 3 +-- 5 files changed, 20 insertions(+), 19 deletions(-) diff --git a/ScoutSuite/providers/aws/facade/base.py b/ScoutSuite/providers/aws/facade/base.py index 66f96695d..b2bc1cf98 100644 --- a/ScoutSuite/providers/aws/facade/base.py +++ b/ScoutSuite/providers/aws/facade/base.py @@ -14,6 +14,7 @@ from ScoutSuite.providers.aws.facade.elbv2 import ELBv2Facade from ScoutSuite.providers.aws.facade.emr import EMRFacade from ScoutSuite.providers.aws.facade.iam import IAMFacade +from ScoutSuite.providers.aws.facade.kms import KMSFacade from ScoutSuite.providers.aws.facade.rds import RDSFacade from ScoutSuite.providers.aws.facade.redshift import RedshiftFacade from ScoutSuite.providers.aws.facade.route53 import Route53Facade @@ -88,6 +89,7 @@ def _instantiate_facades(self): self.elb = ELBFacade(self.session) self.elbv2 = ELBv2Facade(self.session) self.iam = IAMFacade(self.session) + self.kms = KMSFacade(self.session) self.rds = RDSFacade(self.session) self.redshift = RedshiftFacade(self.session) self.s3 = S3Facade(self.session) diff --git a/ScoutSuite/providers/aws/facade/kms.py b/ScoutSuite/providers/aws/facade/kms.py index 2a19ca185..355252bcc 100644 --- a/ScoutSuite/providers/aws/facade/kms.py +++ b/ScoutSuite/providers/aws/facade/kms.py @@ -4,31 +4,30 @@ class KMSFacade(AWSBaseFacade): - - async def get_aliases(self): + async def get_aliases(self, region): try: - return await AWSFacadeUtils.get_all_pages('kms', None, self.session, 'list_aliases', 'Aliases') + return await AWSFacadeUtils.get_all_pages('kms', region, self.session, 'list_aliases', 'Aliases') except Exception as e: print_exception('Failed to list KMS Aliases: {}'.format(e)) return [] - async def get_grants(self): + async def get_grants(self, region): try: - return await AWSFacadeUtils.get_all_pages('kms', None, self.session, 'list_grants', 'Grants') + return await AWSFacadeUtils.get_all_pages('kms', region, self.session, 'list_grants', 'Grants') except Exception as e: print_exception('Failed to list KMS Grants: {}'.format(e)) return [] - async def get_key_policies(self): + async def get_key_policies(self, region): try: - return await AWSFacadeUtils.get_all_pages('kms', None, self.session, 'list_key_policies', 'PolicyNames') + return await AWSFacadeUtils.get_all_pages('kms', region, self.session, 'list_key_policies', 'PolicyNames') except Exception as e: print_exception('Failed to list KMS Key Policies: {}'.format(e)) return [] - async def get_keys(self): + async def get_keys(self, region): try: - return await AWSFacadeUtils.get_all_pages('kms', None, self.session, 'list_keys', 'Keys') + return await AWSFacadeUtils.get_all_pages('kms', region, self.session, 'list_keys', 'Keys') except Exception as e: print_exception('Failed to list KMS Keys: {}'.format(e)) return [] diff --git a/ScoutSuite/providers/aws/resources/kms/aliases.py b/ScoutSuite/providers/aws/resources/kms/aliases.py index c33603004..29c473ca3 100644 --- a/ScoutSuite/providers/aws/resources/kms/aliases.py +++ b/ScoutSuite/providers/aws/resources/kms/aliases.py @@ -8,14 +8,15 @@ def __init__(self, facade: AWSFacade, region: str): self.region = region async def fetch_all(self): - raw_aliases = await self.facade.kms.get_aliases() + raw_aliases = await self.facade.kms.get_aliases(self.region) for raw_alias in raw_aliases: id, alias = self._parse_alias(raw_alias) self[id] = alias def _parse_alias(self, raw_alias): alias_dict = { - 'name': raw_alias.get('AliasName'), + # all KMS Aliases are prefixed with alias/, so we'll strip that off + 'name': raw_alias.get('AliasName').lstrip('alias/'), 'arn': raw_alias.get('AliasArn'), 'key_id': raw_alias.get('TargetKeyId')} return alias_dict['name'], alias_dict diff --git a/ScoutSuite/providers/aws/resources/kms/base.py b/ScoutSuite/providers/aws/resources/kms/base.py index 895a0a1d5..27560e469 100644 --- a/ScoutSuite/providers/aws/resources/kms/base.py +++ b/ScoutSuite/providers/aws/resources/kms/base.py @@ -1,14 +1,14 @@ from ScoutSuite.providers.aws.facade.base import AWSFacade -from ScoutSuite.providers.aws.resources.base import AWSCompositeResources +from ScoutSuite.providers.aws.resources.regions import Regions +from ScoutSuite.providers.aws.resources.kms.aliases import Aliases -class KMS(AWSCompositeResources): +from .aliases import Aliases + +class KMS(Regions): _children = [ (Aliases, 'aliases') ] def __init__(self, facade: AWSFacade): - super(KMS, self).__init__(facade) - self.service = 'kms' + super(KMS, self).__init__('kms', facade) - async def fetch_all(self, partition_name='aws', **kwargs): - await self._fetch_children(self) diff --git a/ScoutSuite/providers/aws/resources/route53/domains.py b/ScoutSuite/providers/aws/resources/route53/domains.py index 4e907c0c8..ee86434f5 100644 --- a/ScoutSuite/providers/aws/resources/route53/domains.py +++ b/ScoutSuite/providers/aws/resources/route53/domains.py @@ -1,5 +1,5 @@ -from ScoutSuite.providers.aws.resources.base import AWSResources from ScoutSuite.providers.aws.facade.base import AWSFacade +from ScoutSuite.providers.aws.resources.base import AWSResources from ScoutSuite.providers.utils import get_non_provider_id @@ -22,4 +22,3 @@ def _parse_domain(self, raw_domain): domain_dict['transfer_lock'] = raw_domain.get('TransferLock') domain_dict['expiry'] = raw_domain.get('Expiry') return domain_dict['id'], domain_dict - From 5740edff215d4b723e18bd9c7109abc68873fe19 Mon Sep 17 00:00:00 2001 From: Nick Klauer Date: Fri, 7 Feb 2020 11:33:01 -0600 Subject: [PATCH 035/145] feat: first stab at getting KMS Grants --- .../providers/aws/resources/kms/base.py | 5 ++-- .../providers/aws/resources/kms/grants.py | 27 +++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 ScoutSuite/providers/aws/resources/kms/grants.py diff --git a/ScoutSuite/providers/aws/resources/kms/base.py b/ScoutSuite/providers/aws/resources/kms/base.py index 27560e469..b43f3465d 100644 --- a/ScoutSuite/providers/aws/resources/kms/base.py +++ b/ScoutSuite/providers/aws/resources/kms/base.py @@ -1,12 +1,13 @@ from ScoutSuite.providers.aws.facade.base import AWSFacade from ScoutSuite.providers.aws.resources.regions import Regions -from ScoutSuite.providers.aws.resources.kms.aliases import Aliases from .aliases import Aliases +from .grants import Grants class KMS(Regions): _children = [ - (Aliases, 'aliases') + (Aliases, 'aliases'), + (Grants, 'grants') ] def __init__(self, facade: AWSFacade): diff --git a/ScoutSuite/providers/aws/resources/kms/grants.py b/ScoutSuite/providers/aws/resources/kms/grants.py new file mode 100644 index 000000000..185c978f3 --- /dev/null +++ b/ScoutSuite/providers/aws/resources/kms/grants.py @@ -0,0 +1,27 @@ +from ScoutSuite.providers.aws.facade.base import AWSFacade +from ScoutSuite.providers.aws.resources.base import AWSResources + +class Grants(AWSResources): + def __init__(self, facade: AWSFacade, region: str): + super(Grants, self).__init__(facade) + self.region = region + + async def fetch_all(self): + raw_grants = await self.facade.kms.get_grants(self.region) + for raw_grant in raw_grants: + id, grant = self._parse_grant(raw_grant) + self[id] = grant + + def _parse_grant(self, raw_grant): + grant_dict = { + 'key_id': raw_grant.get('KeyId'), + 'grant_id': raw_grant.get('GrantId'), + 'name': raw_grant.get('Name'), + 'create_date': raw_grant.get('CreationDate'), + 'grantee_principal': raw_grant.get('GranteePrincipal'), + 'retiring_principal': raw_grant.get('ReitirngPrincipal'), + 'issuing_account': raw_grant.get('IssuingAccount'), + 'operations': raw_grant.get('Operations'), + 'constraints': raw_grant.get('Constraints') + } + return grant_dict['grant_id'], grant_dict From 5aa7b2adf6d3632dc318c6a1e4f2be3a2b42735f Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Fri, 7 Feb 2020 20:47:41 +0100 Subject: [PATCH 036/145] Add VM finding --- .../virtual-machines-disk-encryption.json | 22 +++++++++++++++++++ .../azure/rules/rulesets/cis-1.0.0.json | 6 +++++ .../azure/rules/rulesets/default.json | 6 +++++ 3 files changed, 34 insertions(+) create mode 100644 ScoutSuite/providers/azure/rules/findings/virtual-machines-disk-encryption.json diff --git a/ScoutSuite/providers/azure/rules/findings/virtual-machines-disk-encryption.json b/ScoutSuite/providers/azure/rules/findings/virtual-machines-disk-encryption.json new file mode 100644 index 000000000..63b91d8e6 --- /dev/null +++ b/ScoutSuite/providers/azure/rules/findings/virtual-machines-disk-encryption.json @@ -0,0 +1,22 @@ +{ + "description": "Disks Lacking Encryption", + "rationale": "Encrypting disks ensures that their entire content is fully unrecoverable without a key and thus protects the volume from unwarranted reads.", + "remediation": "Ensure that disks are encrypted, where possible.", + "compliance": [ + {"name": "CIS Microsoft Azure Foundations", "version": "1.0.0", "reference": "7.2"}, + {"name": "CIS Microsoft Azure Foundations", "version": "1.0.0", "reference": "7.3"}, + {"name": "CIS Microsoft Azure Foundations", "version": "1.1.0", "reference": "7.1"}, + {"name": "CIS Microsoft Azure Foundations", "version": "1.1.0", "reference": "7.2"}, + {"name": "CIS Microsoft Azure Foundations", "version": "1.1.0", "reference": "7.3"} + ], + "references": [ + "https://docs.microsoft.com/en-us/azure/security/azure-security-disk-encryption-overview", + "https://docs.microsoft.com/en-us/azure/security-center/security-center-apply-disk-encryption" + ], + "dashboard_name": "Disks", + "path": "virtualmachines.subscriptions.id.disks.id", + "conditions": [ "and", + ["virtualmachines.subscriptions.id.disks.id.encryption_enabled", "false", ""] + ], + "id_suffix": "encryption_enabled" +} diff --git a/ScoutSuite/providers/azure/rules/rulesets/cis-1.0.0.json b/ScoutSuite/providers/azure/rules/rulesets/cis-1.0.0.json index 5ff66293f..1bf79ea7d 100644 --- a/ScoutSuite/providers/azure/rules/rulesets/cis-1.0.0.json +++ b/ScoutSuite/providers/azure/rules/rulesets/cis-1.0.0.json @@ -200,6 +200,12 @@ "enabled": true, "level": "danger" } + ], + "virtual-machines-disk-encryption.json": [ + { + "enabled": true, + "level": "warning" + } ] } } diff --git a/ScoutSuite/providers/azure/rules/rulesets/default.json b/ScoutSuite/providers/azure/rules/rulesets/default.json index b8b5c2e11..6ba8853f1 100644 --- a/ScoutSuite/providers/azure/rules/rulesets/default.json +++ b/ScoutSuite/providers/azure/rules/rulesets/default.json @@ -218,6 +218,12 @@ "enabled": true, "level": "warning" } + ], + "virtual-machines-disk-encryption.json": [ + { + "enabled": true, + "level": "warning" + } ] } } From 22f03159e3940f628eec92b7f9a9302e4a6ca16f Mon Sep 17 00:00:00 2001 From: Nick Klauer Date: Fri, 7 Feb 2020 14:19:07 -0600 Subject: [PATCH 037/145] feat: add KeyPolicies and Keys to KMS resource --- .../providers/aws/resources/kms/base.py | 8 +++++-- .../providers/aws/resources/kms/grants.py | 1 + .../aws/resources/kms/key_policies.py | 23 +++++++++++++++++++ .../providers/aws/resources/kms/keys.py | 21 +++++++++++++++++ 4 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 ScoutSuite/providers/aws/resources/kms/key_policies.py create mode 100644 ScoutSuite/providers/aws/resources/kms/keys.py diff --git a/ScoutSuite/providers/aws/resources/kms/base.py b/ScoutSuite/providers/aws/resources/kms/base.py index b43f3465d..f24887568 100644 --- a/ScoutSuite/providers/aws/resources/kms/base.py +++ b/ScoutSuite/providers/aws/resources/kms/base.py @@ -3,13 +3,17 @@ from .aliases import Aliases from .grants import Grants +from .keys import Keys +from .key_policies import KeyPolicies + class KMS(Regions): _children = [ (Aliases, 'aliases'), - (Grants, 'grants') + (Keys, 'keys'), + (Grants, 'grants'), + (KeyPolicies, 'key_policies') ] def __init__(self, facade: AWSFacade): super(KMS, self).__init__('kms', facade) - diff --git a/ScoutSuite/providers/aws/resources/kms/grants.py b/ScoutSuite/providers/aws/resources/kms/grants.py index 185c978f3..4d3beca0c 100644 --- a/ScoutSuite/providers/aws/resources/kms/grants.py +++ b/ScoutSuite/providers/aws/resources/kms/grants.py @@ -1,6 +1,7 @@ from ScoutSuite.providers.aws.facade.base import AWSFacade from ScoutSuite.providers.aws.resources.base import AWSResources + class Grants(AWSResources): def __init__(self, facade: AWSFacade, region: str): super(Grants, self).__init__(facade) diff --git a/ScoutSuite/providers/aws/resources/kms/key_policies.py b/ScoutSuite/providers/aws/resources/kms/key_policies.py new file mode 100644 index 000000000..c44fbe844 --- /dev/null +++ b/ScoutSuite/providers/aws/resources/kms/key_policies.py @@ -0,0 +1,23 @@ +from ScoutSuite.providers.aws.facade.base import AWSFacade +from ScoutSuite.providers.aws.resources.base import AWSResources +from ScoutSuite.providers.utils import get_non_provider_id + + +class KeyPolicies(AWSResources): + def __init__(self, facade: AWSFacade, region: str): + super(KeyPolicies, self).__init__(facade) + self.region = region + + async def fetch_all(self): + raw_key_policies = await self.facade.kms.get_key_policies(self.region) + for raw_key_policy in raw_key_policies: + key_id, policy = self._parse_key_policy(raw_key_policy) + self[key_id] = policy + + def _parse_key_policy(self, raw_key_policy): + key_policy_dict = { + 'id': get_non_provider_id(raw_key_policy.get('PolicyNames')), + 'policy_names': raw_key_policy.get('PolicyNames') + + } + return key_policy_dict['id'], key_policy_dict diff --git a/ScoutSuite/providers/aws/resources/kms/keys.py b/ScoutSuite/providers/aws/resources/kms/keys.py new file mode 100644 index 000000000..158c578f3 --- /dev/null +++ b/ScoutSuite/providers/aws/resources/kms/keys.py @@ -0,0 +1,21 @@ +from ScoutSuite.providers.aws.facade.base import AWSFacade +from ScoutSuite.providers.aws.resources.base import AWSResources + + +class Keys(AWSResources): + def __init__(self, facade: AWSFacade, region: str): + super(Keys, self).__init__(facade) + self.region = region + + async def fetch_all(self): + raw_keys = await self.facade.kms.get_keys(self.region) + for raw_key in raw_keys: + id, key = self._parse_key(raw_key) + self[id] = key + + def _parse_key(self, raw_key): + key_dict = { + 'key_id': raw_key.get('KeyId'), + 'arn': raw_key.get('KeyArn') + } + return key_dict['key_id'], key_dict From 526e89dfbf2c111ed3588c2a968a92fb37f70dc8 Mon Sep 17 00:00:00 2001 From: Nick Klauer Date: Fri, 7 Feb 2020 14:55:39 -0600 Subject: [PATCH 038/145] feat: fix child/parent relationship for KMS items Grants and KeyPolicies are children of Keys, so we just pull them down --- ScoutSuite/providers/aws/facade/kms.py | 14 +++++++------ .../providers/aws/resources/kms/base.py | 7 +++---- .../providers/aws/resources/kms/grants.py | 5 +++-- .../aws/resources/kms/key_policies.py | 5 +++-- .../providers/aws/resources/kms/keys.py | 21 +++++++++++++++---- 5 files changed, 34 insertions(+), 18 deletions(-) diff --git a/ScoutSuite/providers/aws/facade/kms.py b/ScoutSuite/providers/aws/facade/kms.py index 355252bcc..98679acb5 100644 --- a/ScoutSuite/providers/aws/facade/kms.py +++ b/ScoutSuite/providers/aws/facade/kms.py @@ -4,28 +4,30 @@ class KMSFacade(AWSBaseFacade): - async def get_aliases(self, region): + async def get_aliases(self, region: str): try: return await AWSFacadeUtils.get_all_pages('kms', region, self.session, 'list_aliases', 'Aliases') except Exception as e: print_exception('Failed to list KMS Aliases: {}'.format(e)) return [] - async def get_grants(self, region): + async def get_grants(self, region: str, key_id: str): try: - return await AWSFacadeUtils.get_all_pages('kms', region, self.session, 'list_grants', 'Grants') + return await AWSFacadeUtils.get_all_pages('kms', region, self.session, 'list_grants', 'Grants', + KeyId=key_id) except Exception as e: print_exception('Failed to list KMS Grants: {}'.format(e)) return [] - async def get_key_policies(self, region): + async def get_key_policies(self, region: str, key_id: str): try: - return await AWSFacadeUtils.get_all_pages('kms', region, self.session, 'list_key_policies', 'PolicyNames') + return await AWSFacadeUtils.get_all_pages('kms', region, self.session, 'list_key_policies', 'PolicyNames', + KeyId=key_id) except Exception as e: print_exception('Failed to list KMS Key Policies: {}'.format(e)) return [] - async def get_keys(self, region): + async def get_keys(self, region: str): try: return await AWSFacadeUtils.get_all_pages('kms', region, self.session, 'list_keys', 'Keys') except Exception as e: diff --git a/ScoutSuite/providers/aws/resources/kms/base.py b/ScoutSuite/providers/aws/resources/kms/base.py index f24887568..2060f434f 100644 --- a/ScoutSuite/providers/aws/resources/kms/base.py +++ b/ScoutSuite/providers/aws/resources/kms/base.py @@ -2,18 +2,17 @@ from ScoutSuite.providers.aws.resources.regions import Regions from .aliases import Aliases -from .grants import Grants from .keys import Keys -from .key_policies import KeyPolicies class KMS(Regions): _children = [ (Aliases, 'aliases'), (Keys, 'keys'), - (Grants, 'grants'), - (KeyPolicies, 'key_policies') ] def __init__(self, facade: AWSFacade): super(KMS, self).__init__('kms', facade) + + async def fetch_all(self, regions=None, excluded_regions=None, partition_name='aws', **kwargs): + await super(KMS, self).fetch_all(regions, excluded_regions, partition_name) diff --git a/ScoutSuite/providers/aws/resources/kms/grants.py b/ScoutSuite/providers/aws/resources/kms/grants.py index 4d3beca0c..072900891 100644 --- a/ScoutSuite/providers/aws/resources/kms/grants.py +++ b/ScoutSuite/providers/aws/resources/kms/grants.py @@ -3,12 +3,13 @@ class Grants(AWSResources): - def __init__(self, facade: AWSFacade, region: str): + def __init__(self, facade: AWSFacade, region: str, key_id: str): super(Grants, self).__init__(facade) self.region = region + self.key_id = key_id async def fetch_all(self): - raw_grants = await self.facade.kms.get_grants(self.region) + raw_grants = await self.facade.kms.get_grants(self.region, self.key_id) for raw_grant in raw_grants: id, grant = self._parse_grant(raw_grant) self[id] = grant diff --git a/ScoutSuite/providers/aws/resources/kms/key_policies.py b/ScoutSuite/providers/aws/resources/kms/key_policies.py index c44fbe844..fc14456b3 100644 --- a/ScoutSuite/providers/aws/resources/kms/key_policies.py +++ b/ScoutSuite/providers/aws/resources/kms/key_policies.py @@ -4,12 +4,13 @@ class KeyPolicies(AWSResources): - def __init__(self, facade: AWSFacade, region: str): + def __init__(self, facade: AWSFacade, region: str, key_id: str): super(KeyPolicies, self).__init__(facade) self.region = region + self.key_id = key_id async def fetch_all(self): - raw_key_policies = await self.facade.kms.get_key_policies(self.region) + raw_key_policies = await self.facade.kms.get_key_policies(self.region, self.key_id) for raw_key_policy in raw_key_policies: key_id, policy = self._parse_key_policy(raw_key_policy) self[key_id] = policy diff --git a/ScoutSuite/providers/aws/resources/kms/keys.py b/ScoutSuite/providers/aws/resources/kms/keys.py index 158c578f3..fbc41de47 100644 --- a/ScoutSuite/providers/aws/resources/kms/keys.py +++ b/ScoutSuite/providers/aws/resources/kms/keys.py @@ -1,8 +1,15 @@ from ScoutSuite.providers.aws.facade.base import AWSFacade -from ScoutSuite.providers.aws.resources.base import AWSResources +from ScoutSuite.providers.aws.resources.base import AWSCompositeResources +from .grants import Grants +from .key_policies import KeyPolicies -class Keys(AWSResources): +class Keys(AWSCompositeResources): + _children = [ + (Grants, 'grants'), + (KeyPolicies, 'key_policies') + ] + def __init__(self, facade: AWSFacade, region: str): super(Keys, self).__init__(facade) self.region = region @@ -10,8 +17,14 @@ def __init__(self, facade: AWSFacade, region: str): async def fetch_all(self): raw_keys = await self.facade.kms.get_keys(self.region) for raw_key in raw_keys: - id, key = self._parse_key(raw_key) - self[id] = key + key_id, key = self._parse_key(raw_key) + self[key_id] = key + + await self._fetch_children_of_all_resources( + resources=self, + scopes={key_id: {'region': self.region, 'key_id': key['key_id']} + for (key_id, key) in self.items()} + ) def _parse_key(self, raw_key): key_dict = { From 57652b5682fae26f7a459b2fc5dd2fc96673af1f Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Sat, 8 Feb 2020 18:05:59 +0100 Subject: [PATCH 039/145] Add VM finding --- ...ualmachines.subscriptions.id.instances.html | 8 ++++++++ .../providers/azure/facade/virtualmachines.py | 14 ++++++++++++++ .../resources/virtualmachines/instances.py | 12 +++++++++--- .../virtual-machines-extensions-installed.json | 18 ++++++++++++++++++ .../azure/rules/rulesets/cis-1.0.0.json | 6 ++++++ .../azure/rules/rulesets/default.json | 6 ++++++ 6 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 ScoutSuite/providers/azure/rules/findings/virtual-machines-extensions-installed.json diff --git a/ScoutSuite/output/data/html/partials/azure/services.virtualmachines.subscriptions.id.instances.html b/ScoutSuite/output/data/html/partials/azure/services.virtualmachines.subscriptions.id.instances.html index 1b7149cfc..f19cd93fc 100644 --- a/ScoutSuite/output/data/html/partials/azure/services.virtualmachines.subscriptions.id.instances.html +++ b/ScoutSuite/output/data/html/partials/azure/services.virtualmachines.subscriptions.id.instances.html @@ -25,6 +25,14 @@

    Network Interfaces

    {{/each}}
+
+

Extensions

+ {{#each extensions}} +
    +
  • {{this.name}}
  • +
+ {{/each}} +
+ + + + + From 277b67bf6e49212efdeacf7cd7e88d2e123619fb Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Tue, 11 Feb 2020 18:06:45 +0100 Subject: [PATCH 054/145] Merge --- ScoutSuite/providers/aws/facade/kms.py | 57 ++++++++++++++----- .../aws/resources/kms/key_policies.py | 24 -------- .../providers/aws/resources/kms/keys.py | 6 +- 3 files changed, 45 insertions(+), 42 deletions(-) delete mode 100644 ScoutSuite/providers/aws/resources/kms/key_policies.py diff --git a/ScoutSuite/providers/aws/facade/kms.py b/ScoutSuite/providers/aws/facade/kms.py index 98679acb5..afc895946 100644 --- a/ScoutSuite/providers/aws/facade/kms.py +++ b/ScoutSuite/providers/aws/facade/kms.py @@ -1,9 +1,51 @@ from ScoutSuite.core.console import print_exception from ScoutSuite.providers.aws.facade.basefacade import AWSBaseFacade from ScoutSuite.providers.aws.facade.utils import AWSFacadeUtils +from ScoutSuite.providers.utils import run_concurrently, get_and_set_concurrently +import json + class KMSFacade(AWSBaseFacade): + + async def get_keys(self, region: str): + + try: + keys = await AWSFacadeUtils.get_all_pages('kms', region, self.session, 'list_keys', 'Keys') + await get_and_set_concurrently( + [self._get_and_set_key_policy, self._get_and_set_key_metadata, self._get_and_set_key_rotation_status], + keys, region=region) + except Exception as e: + print_exception('Failed to get KMS keys: {}'.format(e)) + keys = [] + finally: + return keys + + async def _get_and_set_key_policy(self, key: {}, region: str): + client = AWSFacadeUtils.get_client('kms', self.session, region) + try: + response = await run_concurrently( + lambda: client.get_key_policy(KeyId=key['KeyId'], + PolicyName='default')) + key['policy'] = json.loads(response.get('Policy')) + except Exception as e: + print_exception('Failed to get KMS key policy: {}'.format(e)) + + async def _get_and_set_key_rotation_status(self, key: {}, region: str): + client = AWSFacadeUtils.get_client('kms', self.session, region) + try: + key['rotation_status'] = await run_concurrently( + lambda: client.get_key_rotation_status(KeyId=key['KeyId'])) + except Exception as e: + print_exception('Failed to get KMS key rotation: {}'.format(e)) + + async def _get_and_set_key_metadata(self, key: {}, region: str): + client = AWSFacadeUtils.get_client('kms', self.session, region) + try: + key['metadata'] = await run_concurrently(lambda: client.describe_key(KeyId=key['KeyId'])) + except Exception as e: + print_exception('Failed to describe KMS key: {}'.format(e)) + async def get_aliases(self, region: str): try: return await AWSFacadeUtils.get_all_pages('kms', region, self.session, 'list_aliases', 'Aliases') @@ -18,18 +60,3 @@ async def get_grants(self, region: str, key_id: str): except Exception as e: print_exception('Failed to list KMS Grants: {}'.format(e)) return [] - - async def get_key_policies(self, region: str, key_id: str): - try: - return await AWSFacadeUtils.get_all_pages('kms', region, self.session, 'list_key_policies', 'PolicyNames', - KeyId=key_id) - except Exception as e: - print_exception('Failed to list KMS Key Policies: {}'.format(e)) - return [] - - async def get_keys(self, region: str): - try: - return await AWSFacadeUtils.get_all_pages('kms', region, self.session, 'list_keys', 'Keys') - except Exception as e: - print_exception('Failed to list KMS Keys: {}'.format(e)) - return [] diff --git a/ScoutSuite/providers/aws/resources/kms/key_policies.py b/ScoutSuite/providers/aws/resources/kms/key_policies.py deleted file mode 100644 index fc14456b3..000000000 --- a/ScoutSuite/providers/aws/resources/kms/key_policies.py +++ /dev/null @@ -1,24 +0,0 @@ -from ScoutSuite.providers.aws.facade.base import AWSFacade -from ScoutSuite.providers.aws.resources.base import AWSResources -from ScoutSuite.providers.utils import get_non_provider_id - - -class KeyPolicies(AWSResources): - def __init__(self, facade: AWSFacade, region: str, key_id: str): - super(KeyPolicies, self).__init__(facade) - self.region = region - self.key_id = key_id - - async def fetch_all(self): - raw_key_policies = await self.facade.kms.get_key_policies(self.region, self.key_id) - for raw_key_policy in raw_key_policies: - key_id, policy = self._parse_key_policy(raw_key_policy) - self[key_id] = policy - - def _parse_key_policy(self, raw_key_policy): - key_policy_dict = { - 'id': get_non_provider_id(raw_key_policy.get('PolicyNames')), - 'policy_names': raw_key_policy.get('PolicyNames') - - } - return key_policy_dict['id'], key_policy_dict diff --git a/ScoutSuite/providers/aws/resources/kms/keys.py b/ScoutSuite/providers/aws/resources/kms/keys.py index de9ae684c..02a6bdde3 100644 --- a/ScoutSuite/providers/aws/resources/kms/keys.py +++ b/ScoutSuite/providers/aws/resources/kms/keys.py @@ -1,13 +1,11 @@ from ScoutSuite.providers.aws.facade.base import AWSFacade from ScoutSuite.providers.aws.resources.base import AWSCompositeResources from .grants import Grants -from .key_policies import KeyPolicies class Keys(AWSCompositeResources): _children = [ (Grants, 'grants'), - (KeyPolicies, 'key_policies') ] def __init__(self, facade: AWSFacade, region: str): @@ -28,11 +26,13 @@ async def fetch_all(self): def _parse_key(self, raw_key): key_dict = {} - key_dict['id'] = raw_key.get('KeyId') + key_dict['id'] = key_dict['name'] = raw_key.get('KeyId') key_dict['arn'] = raw_key.get('KeyArn') key_dict['rotation_enabled'] = raw_key['rotation_status']['KeyRotationEnabled'] \ if 'rotation_status' in raw_key else None + key_dict['policy'] = raw_key.get('policy') + if 'metadata' in raw_key: key_dict['creation_date'] = raw_key['metadata']['KeyMetadata']['CreationDate'] if \ raw_key['metadata']['KeyMetadata']['CreationDate'] else None From 42653fe9a6a1c63625132f661ff16b4fb32da677 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Tue, 11 Feb 2020 18:25:40 +0100 Subject: [PATCH 055/145] Close element --- .../data/html/partials/aws/services.cloudtrail.regions.html | 1 + 1 file changed, 1 insertion(+) diff --git a/ScoutSuite/output/data/html/partials/aws/services.cloudtrail.regions.html b/ScoutSuite/output/data/html/partials/aws/services.cloudtrail.regions.html index 262d33e00..07356891b 100644 --- a/ScoutSuite/output/data/html/partials/aws/services.cloudtrail.regions.html +++ b/ScoutSuite/output/data/html/partials/aws/services.cloudtrail.regions.html @@ -12,6 +12,7 @@

Information

{{#ifPositive trails_count}}true{{else}}false{{/ifPositive}} +
From af49d5cfc7c2dc1afe4072014d40a3030b0e9c22 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Tue, 11 Feb 2020 18:36:18 +0100 Subject: [PATCH 056/145] Improve partial --- .../partials/aws/services.kms.regions.id.keys.html | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/ScoutSuite/output/data/html/partials/aws/services.kms.regions.id.keys.html b/ScoutSuite/output/data/html/partials/aws/services.kms.regions.id.keys.html index 6dd3ca4ef..c06c5b44b 100644 --- a/ScoutSuite/output/data/html/partials/aws/services.kms.regions.id.keys.html +++ b/ScoutSuite/output/data/html/partials/aws/services.kms.regions.id.keys.html @@ -18,8 +18,20 @@

Information

  • Key Manager: {{value_or_none key_manager}}
  • - {{#if policy}}
    + {{#if aliases.length}} +

    Aliases

    +
      + {{#each this.aliases}} +
    • {{name}}
    • + {{/each}} +
    + {{else}} +

    Aliases

    + {{/if}} +
    +
    + {{#if policy}} {{> accordion_policy name = 'Key Policy' document = policy policy_path = (concat 'kms.regions' region 'keys' @key 'policy')}} {{else}}

    Key Policy

    From 5b9c0b80849ea6ca679d5a84c55ff3ad685dc9f6 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Tue, 11 Feb 2020 18:36:38 +0100 Subject: [PATCH 057/145] Improve parsing --- ScoutSuite/providers/aws/facade/kms.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/ScoutSuite/providers/aws/facade/kms.py b/ScoutSuite/providers/aws/facade/kms.py index afc895946..c85e246ca 100644 --- a/ScoutSuite/providers/aws/facade/kms.py +++ b/ScoutSuite/providers/aws/facade/kms.py @@ -13,7 +13,10 @@ async def get_keys(self, region: str): try: keys = await AWSFacadeUtils.get_all_pages('kms', region, self.session, 'list_keys', 'Keys') await get_and_set_concurrently( - [self._get_and_set_key_policy, self._get_and_set_key_metadata, self._get_and_set_key_rotation_status], + [self._get_and_set_key_policy, + self._get_and_set_key_metadata, + self._get_and_set_key_rotation_status, + self._get_and_set_key_aliases], keys, region=region) except Exception as e: print_exception('Failed to get KMS keys: {}'.format(e)) @@ -46,12 +49,15 @@ async def _get_and_set_key_metadata(self, key: {}, region: str): except Exception as e: print_exception('Failed to describe KMS key: {}'.format(e)) - async def get_aliases(self, region: str): + async def _get_and_set_key_aliases(self, key: {}, region: str): + client = AWSFacadeUtils.get_client('kms', self.session, region) try: - return await AWSFacadeUtils.get_all_pages('kms', region, self.session, 'list_aliases', 'Aliases') + response = await run_concurrently( + lambda: client.list_aliases(KeyId=key['KeyId']) + ) + key['aliases'] = response.get('Aliases') except Exception as e: - print_exception('Failed to list KMS Aliases: {}'.format(e)) - return [] + print_exception('Failed to get KMS aliases: {}'.format(e)) async def get_grants(self, region: str, key_id: str): try: From ef9e37186f2718db403fda31b45eb0d071c4170a Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Tue, 11 Feb 2020 18:36:44 +0100 Subject: [PATCH 058/145] Improve parsing --- ScoutSuite/providers/aws/resources/kms/keys.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/ScoutSuite/providers/aws/resources/kms/keys.py b/ScoutSuite/providers/aws/resources/kms/keys.py index 02a6bdde3..663f174bb 100644 --- a/ScoutSuite/providers/aws/resources/kms/keys.py +++ b/ScoutSuite/providers/aws/resources/kms/keys.py @@ -1,5 +1,6 @@ from ScoutSuite.providers.aws.facade.base import AWSFacade from ScoutSuite.providers.aws.resources.base import AWSCompositeResources +from ScoutSuite.providers.utils import get_non_provider_id from .grants import Grants @@ -44,4 +45,18 @@ def _parse_key(self, raw_key): key_dict['key_manager'] = raw_key['metadata']['KeyMetadata']['KeyManager'] if len( raw_key['metadata']['KeyMetadata']['KeyManager'].strip()) > 0 else None + key_dict['aliases'] = {} + for raw_alias in raw_key.get('aliases', []): + alias_id, alias = self._parse_alias(raw_alias) + key_dict['aliases'][alias_id] = alias + return key_dict['id'], key_dict + + def _parse_alias(self, raw_alias): + alias_dict = { + # all KMS Aliases are prefixed with alias/, so we'll strip that off + 'id': get_non_provider_id(raw_alias.get('AliasArn')), + 'name': raw_alias.get('AliasName').split('alias/', 1)[-1], + 'arn': raw_alias.get('AliasArn'), + 'key_id': raw_alias.get('TargetKeyId')} + return alias_dict['id'], alias_dict From 6518cdee82e054ca54d9d853b8fc457775567068 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Tue, 11 Feb 2020 18:48:55 +0100 Subject: [PATCH 059/145] Merge --- ScoutSuite/providers/aws/resources/kms/base.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/ScoutSuite/providers/aws/resources/kms/base.py b/ScoutSuite/providers/aws/resources/kms/base.py index ffce9ca95..bc318763d 100644 --- a/ScoutSuite/providers/aws/resources/kms/base.py +++ b/ScoutSuite/providers/aws/resources/kms/base.py @@ -1,13 +1,11 @@ from ScoutSuite.providers.aws.facade.base import AWSFacade from ScoutSuite.providers.aws.resources.regions import Regions -from .aliases import Aliases from .keys import Keys class KMS(Regions): _children = [ - (Aliases, 'aliases'), (Keys, 'keys'), ] From 98321fe84d8e567fb72f9c9d7eef0a9d8213a7ee Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Wed, 12 Feb 2020 16:41:49 +0100 Subject: [PATCH 060/145] Bug fix --- ScoutSuite/providers/azure/facade/storageaccounts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ScoutSuite/providers/azure/facade/storageaccounts.py b/ScoutSuite/providers/azure/facade/storageaccounts.py index 60ffba705..f37492e67 100644 --- a/ScoutSuite/providers/azure/facade/storageaccounts.py +++ b/ScoutSuite/providers/azure/facade/storageaccounts.py @@ -41,7 +41,7 @@ async def get_blob_containers(self, resource_group_name, storage_account_name, s return containers async def _get_and_set_activity_logs(self, storage_account, subscription_id: str): - client = MonitorManagementClient(self.credentials, subscription_id) + client = MonitorManagementClient(self.credentials.arm_credentials, subscription_id) # Time format used by Azure API: time_format = "%Y-%m-%dT%H:%M:%S.%f" From 12aa6bd2cf1acfb4c7ed6e3ca2f569742798f363 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Wed, 12 Feb 2020 18:05:42 +0100 Subject: [PATCH 061/145] Handle uniform bucket ACLs --- ScoutSuite/providers/gcp/resources/cloudstorage/buckets.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ScoutSuite/providers/gcp/resources/cloudstorage/buckets.py b/ScoutSuite/providers/gcp/resources/cloudstorage/buckets.py index 79b772735..5cdac47a6 100644 --- a/ScoutSuite/providers/gcp/resources/cloudstorage/buckets.py +++ b/ScoutSuite/providers/gcp/resources/cloudstorage/buckets.py @@ -26,7 +26,10 @@ def _parse_bucket(self, raw_bucket): bucket_dict['versioning_status_enabled'] = raw_bucket.versioning_enabled bucket_dict['uniform_bucket_level_access'] = raw_bucket.iam_configuration['bucketPolicyOnly']['enabled'] bucket_dict['logging_enabled'] = raw_bucket.logging is not None - bucket_dict['acls'] = list(raw_bucket.acl) + if bucket_dict['uniform_bucket_level_access']: + bucket_dict['acls'] = None + else: + bucket_dict['acls'] = list(raw_bucket.acl) bucket_dict['acl_configuration'] = self._get_cloudstorage_bucket_acl(raw_bucket) # FIXME this should be "IAM" return bucket_dict['id'], bucket_dict From e770dcbaf03b09cb8646a37596218150b81f34b1 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Wed, 12 Feb 2020 18:10:55 +0100 Subject: [PATCH 062/145] Fix GCS versioning --- .../gcp/rules/findings/cloudstorage-bucket-no-versioning.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ScoutSuite/providers/gcp/rules/findings/cloudstorage-bucket-no-versioning.json b/ScoutSuite/providers/gcp/rules/findings/cloudstorage-bucket-no-versioning.json index a9860ade0..ab6db4a86 100644 --- a/ScoutSuite/providers/gcp/rules/findings/cloudstorage-bucket-no-versioning.json +++ b/ScoutSuite/providers/gcp/rules/findings/cloudstorage-bucket-no-versioning.json @@ -4,7 +4,7 @@ "rationale": "Description:

    Enable Object Versioning to protect Cloud Storage data from being overwritten or accidentally deleted.", "path": "cloudstorage.projects.id.buckets.id", "conditions": [ "and", - [ "cloudstorage.projects.id.buckets.id.versioning_status_enabled", "false", "" ] + [ "cloudstorage.projects.id.buckets.id.versioning_enabled", "false", "" ] ], - "id_suffix": "versioning" + "id_suffix": "versioning_enabled" } From 995522a741a3e0c074e3d2cadfbb7966f60ec910 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Wed, 12 Feb 2020 18:11:47 +0100 Subject: [PATCH 063/145] Fix GCS versioning --- .../partials/gcp/services.cloudstorage.projects.id.buckets.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ScoutSuite/output/data/html/partials/gcp/services.cloudstorage.projects.id.buckets.html b/ScoutSuite/output/data/html/partials/gcp/services.cloudstorage.projects.id.buckets.html index 6484528b8..fa8171175 100644 --- a/ScoutSuite/output/data/html/partials/gcp/services.cloudstorage.projects.id.buckets.html +++ b/ScoutSuite/output/data/html/partials/gcp/services.cloudstorage.projects.id.buckets.html @@ -11,7 +11,7 @@

    Information

    Location: {{location}}
    Storage Class: {{storage_class}}
    Logging: {{convert_bool_to_enabled logging_enabled}}
    -
    Versioning: {{convert_bool_to_enabled versioning_status}}
    +
    Versioning: {{convert_bool_to_enabled versioning_enabled}}
    Uniform Bucket-Level Access: {{convert_bool_to_enabled uniform_bucket_level_access}}
    From de692103ffb472cb0a1087ba7d8cfbd177da7409 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Wed, 12 Feb 2020 18:11:54 +0100 Subject: [PATCH 064/145] Fix GCS versioning --- ScoutSuite/providers/gcp/resources/cloudstorage/buckets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ScoutSuite/providers/gcp/resources/cloudstorage/buckets.py b/ScoutSuite/providers/gcp/resources/cloudstorage/buckets.py index 5cdac47a6..61d0cfbf5 100644 --- a/ScoutSuite/providers/gcp/resources/cloudstorage/buckets.py +++ b/ScoutSuite/providers/gcp/resources/cloudstorage/buckets.py @@ -23,7 +23,7 @@ def _parse_bucket(self, raw_bucket): bucket_dict['creation_date'] = raw_bucket.time_created bucket_dict['location'] = raw_bucket.location bucket_dict['storage_class'] = raw_bucket.storage_class.lower() - bucket_dict['versioning_status_enabled'] = raw_bucket.versioning_enabled + bucket_dict['versioning_enabled'] = raw_bucket.versioning_enabled bucket_dict['uniform_bucket_level_access'] = raw_bucket.iam_configuration['bucketPolicyOnly']['enabled'] bucket_dict['logging_enabled'] = raw_bucket.logging is not None if bucket_dict['uniform_bucket_level_access']: From 31bc439cd6e259959b51e05ede51405941ea7ad7 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Wed, 12 Feb 2020 18:15:33 +0100 Subject: [PATCH 065/145] Improve partial --- .../gcp/services.cloudstorage.projects.id.buckets.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ScoutSuite/output/data/html/partials/gcp/services.cloudstorage.projects.id.buckets.html b/ScoutSuite/output/data/html/partials/gcp/services.cloudstorage.projects.id.buckets.html index fa8171175..bf6085126 100644 --- a/ScoutSuite/output/data/html/partials/gcp/services.cloudstorage.projects.id.buckets.html +++ b/ScoutSuite/output/data/html/partials/gcp/services.cloudstorage.projects.id.buckets.html @@ -6,10 +6,10 @@

    {{name}}

    Information

    -
    Project ID: {{project_id}}
    -
    Creation Date: {{creation_date}}
    -
    Location: {{location}}
    -
    Storage Class: {{storage_class}}
    +
    Project ID: {{project_id}}
    +
    Creation Date: {{format_date creation_date}}
    +
    Location: {{location}}
    +
    Storage Class: {{storage_class}}
    Logging: {{convert_bool_to_enabled logging_enabled}}
    Versioning: {{convert_bool_to_enabled versioning_enabled}}
    Uniform Bucket-Level Access: {{convert_bool_to_enabled uniform_bucket_level_access}}
    From 5f32f241cc4e1afd11e80ca7d038343cb975eec3 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Wed, 12 Feb 2020 18:32:42 +0100 Subject: [PATCH 066/145] Include default object acls --- ...services.cloudstorage.projects.id.buckets.html | 15 +++++++++++++++ .../gcp/resources/cloudstorage/buckets.py | 4 +++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/ScoutSuite/output/data/html/partials/gcp/services.cloudstorage.projects.id.buckets.html b/ScoutSuite/output/data/html/partials/gcp/services.cloudstorage.projects.id.buckets.html index bf6085126..9a8240b65 100644 --- a/ScoutSuite/output/data/html/partials/gcp/services.cloudstorage.projects.id.buckets.html +++ b/ScoutSuite/output/data/html/partials/gcp/services.cloudstorage.projects.id.buckets.html @@ -46,6 +46,21 @@

    +

    Default Object ACL Permissions

    +
    +
      + {{#each default_object_acl}} +
    • {{entity}}
    • +
        +
      • {{role}}
      • +
      + {{else}} +
    • None
    • + {{/each}} +
    +
    +

    - + +
    + + From d3ed286a68d562db1b8b0b57dbcbb3408073f6e5 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Fri, 14 Feb 2020 12:38:43 +0100 Subject: [PATCH 081/145] Improve partial --- .../aws/services.redshift.regions.id.parameter_groups.html | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ScoutSuite/output/data/html/partials/aws/services.redshift.regions.id.parameter_groups.html b/ScoutSuite/output/data/html/partials/aws/services.redshift.regions.id.parameter_groups.html index 237cb24d7..db86abf53 100644 --- a/ScoutSuite/output/data/html/partials/aws/services.redshift.regions.id.parameter_groups.html +++ b/ScoutSuite/output/data/html/partials/aws/services.redshift.regions.id.parameter_groups.html @@ -6,8 +6,9 @@

    {{name}}

    Information

    -
    Description: {{Description}}
    -
    Group Family: {{ParameterGroupFamily}}
    +
    Description: {{description}}
    +
    Group Family: {{family}}
    +
    Default Parameter Group: {{is_default}}

    Parameters

    From f41595637675509c055f09470f9d79e101790511 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Fri, 14 Feb 2020 12:38:50 +0100 Subject: [PATCH 082/145] Add rationale --- .../findings/redshift-parameter-group-ssl-not-required.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ScoutSuite/providers/aws/rules/findings/redshift-parameter-group-ssl-not-required.json b/ScoutSuite/providers/aws/rules/findings/redshift-parameter-group-ssl-not-required.json index f6c665b20..4eefd4cc8 100644 --- a/ScoutSuite/providers/aws/rules/findings/redshift-parameter-group-ssl-not-required.json +++ b/ScoutSuite/providers/aws/rules/findings/redshift-parameter-group-ssl-not-required.json @@ -1,5 +1,6 @@ { - "description": "SSL not required", + "description": "SSL Not Required", + "rationale": "Description:

    Parameter groups associated with Redshift clusters should have the \"require_ssl\" parameter enabled, to ensure that data in transit is encrypted.

    Note that this rule will flag default parameter groups, even though they cannot be changed. It is recommended to use custom groups and configure them according to security best practice.", "path": "redshift.regions.id.parameter_groups.id", "dashboard_name": "Parameter Groups", "conditions": [ "and", From 0d55aeea5235d6e18ad830601ba422463255385d Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Fri, 14 Feb 2020 16:45:49 +0100 Subject: [PATCH 083/145] Fix https://github.com/nccgroup/ScoutSuite/issues/606 --- .../aws/rules/findings/sns-topic-world-policy.json | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ScoutSuite/providers/aws/rules/findings/sns-topic-world-policy.json b/ScoutSuite/providers/aws/rules/findings/sns-topic-world-policy.json index cee6566f2..2a3115240 100644 --- a/ScoutSuite/providers/aws/rules/findings/sns-topic-world-policy.json +++ b/ScoutSuite/providers/aws/rules/findings/sns-topic-world-policy.json @@ -8,8 +8,13 @@ "conditions": [ "and", [ "sns.regions.id.topics.id.Policy.Statement.id.Effect", "equal", "Allow" ], [ "sns.regions.id.topics.id.Policy.Statement.id.", "containAction", "SNS:_ARG_0_" ], - [ "sns.regions.id.topics.id.Policy.Statement.id.Principal", "withKey", "AWS" ], - [ "sns.regions.id.topics.id.Policy.Statement.id.Principal.AWS", "containAtLeastOneOf", "*" ], + [ "or", + [ "sns.regions.id.topics.id.Policy.Statement.id.Principal", "equal", "*" ], + [ "and", + [ "sns.regions.id.topics.id.Policy.Statement.id.Principal", "withKey", "AWS" ], + [ "sns.regions.id.topics.id.Policy.Statement.id.Principal.AWS", "containAtLeastOneOf", "*" ] + ] + ], [ "or", [ "sns.regions.id.topics.id.Policy.Statement.id.", "withoutKey", "Condition" ], [ "sns.regions.id.topics.id.Policy.Statement.id.Condition.", "withoutKey", "StringEquals" ], From 2da2a5763e21d97dd88ebb2d538499ba33f046f5 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Fri, 14 Feb 2020 16:58:34 +0100 Subject: [PATCH 084/145] Minor change --- .../providers/aws/rules/findings/sqs-queue-world-policy.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ScoutSuite/providers/aws/rules/findings/sqs-queue-world-policy.json b/ScoutSuite/providers/aws/rules/findings/sqs-queue-world-policy.json index fa7ca08f9..0aa1995ac 100644 --- a/ScoutSuite/providers/aws/rules/findings/sqs-queue-world-policy.json +++ b/ScoutSuite/providers/aws/rules/findings/sqs-queue-world-policy.json @@ -6,7 +6,7 @@ "path": "sqs.regions.id.queues.id.Policy.Statement.id", "display_path": "sqs.regions.id.queues.id", "conditions": [ "and", - [ "sqs.regions.id.queues.id.Policy", "notNull", ""], + [ "sqs.regions.id.queues.id.Policy", "notNull", "" ], [ "sqs.regions.id.queues.id.Policy.Statement.id.Effect", "equal", "Allow" ], [ "sqs.regions.id.queues.id.Policy.Statement.id.", "containAction", "sqs:_ARG_0_" ], [ "_INCLUDE_(conditions/policy-statement-any-principal.json)", [ "_STATEMENT_" ], [ "sqs.regions.id.queues.id.Policy.Statement.id" ] ], From 7f8af5ae9f5d7287fa61ace1a53d68c2f590d130 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Fri, 14 Feb 2020 16:59:58 +0100 Subject: [PATCH 085/145] Remove duplicate lines --- .../providers/aws/rules/findings/sns-topic-world-policy.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ScoutSuite/providers/aws/rules/findings/sns-topic-world-policy.json b/ScoutSuite/providers/aws/rules/findings/sns-topic-world-policy.json index 2a3115240..06c40630e 100644 --- a/ScoutSuite/providers/aws/rules/findings/sns-topic-world-policy.json +++ b/ScoutSuite/providers/aws/rules/findings/sns-topic-world-policy.json @@ -6,6 +6,7 @@ "path": "sns.regions.id.topics.id.Policy.Statement.id", "display_path": "sns.regions.id.topics.id", "conditions": [ "and", + [ "sns.regions.id.topics.id.Policy", "notNull", "" ], [ "sns.regions.id.topics.id.Policy.Statement.id.Effect", "equal", "Allow" ], [ "sns.regions.id.topics.id.Policy.Statement.id.", "containAction", "SNS:_ARG_0_" ], [ "or", @@ -19,8 +20,6 @@ [ "sns.regions.id.topics.id.Policy.Statement.id.", "withoutKey", "Condition" ], [ "sns.regions.id.topics.id.Policy.Statement.id.Condition.", "withoutKey", "StringEquals" ], [ "and", - [ "sns.regions.id.topics.id.Policy.Statement.id.Condition.StringEquals.", "withoutKey", "aws:SourceArn" ], - [ "sns.regions.id.topics.id.Policy.Statement.id.Condition.StringEquals.", "withoutKey", "aws:SourceOwner" ], [ "sns.regions.id.topics.id.Policy.Statement.id.Condition.StringEquals.", "withoutKey", "AWS:SourceArn" ], [ "sns.regions.id.topics.id.Policy.Statement.id.Condition.StringEquals.", "withoutKey", "AWS:SourceOwner" ] ] @@ -29,8 +28,6 @@ [ "sns.regions.id.topics.id.Policy.Statement.id.", "withoutKey", "Condition" ], [ "sns.regions.id.topics.id.Policy.Statement.id.Condition.", "withoutKey", "ArnLike" ], [ "and", - [ "sns.regions.id.topics.id.Policy.Statement.id.Condition.ArnLike.", "withoutKey", "aws:SourceArn" ], - [ "sns.regions.id.topics.id.Policy.Statement.id.Condition.ArnLike.", "withoutKey", "aws:SourceOwner" ], [ "sns.regions.id.topics.id.Policy.Statement.id.Condition.ArnLike.", "withoutKey", "AWS:SourceArn" ], [ "sns.regions.id.topics.id.Policy.Statement.id.Condition.ArnLike.", "withoutKey", "AWS:SourceOwner" ] ] From 4e676ef8d752a77ab70ca3f40182086456b8950e Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Fri, 14 Feb 2020 17:02:53 +0100 Subject: [PATCH 086/145] Improve check --- .../rules/conditions/policy-statement-poor-condition.json | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ScoutSuite/providers/aws/rules/conditions/policy-statement-poor-condition.json b/ScoutSuite/providers/aws/rules/conditions/policy-statement-poor-condition.json index c037508a0..9654837ab 100644 --- a/ScoutSuite/providers/aws/rules/conditions/policy-statement-poor-condition.json +++ b/ScoutSuite/providers/aws/rules/conditions/policy-statement-poor-condition.json @@ -5,11 +5,13 @@ [ "_STATEMENT_.Condition.", "withoutKey", "ArnEquals" ], [ "or", [ "_STATEMENT_.Condition.", "withoutKey", "StringEquals" ], - [ "_STATEMENT_.Condition.StringEquals.", "withoutKey", "aws:SourceOwner" ], - [ "_STATEMENT_.Condition.StringEquals.", "withoutKey", "aws:SourceArn" ] + [ "_STATEMENT_.Condition.StringEquals.", "withoutKey", "aws:SourceArn" ], + [ "_STATEMENT_.Condition.StringEquals.", "withoutKey", "aws:SourceOwner" ] ], [ "or", - [ "_STATEMENT_.Condition.", "withoutKey", "ArnLike" ] + [ "_STATEMENT_.Condition.", "withoutKey", "ArnLike" ], + [ "_STATEMENT_.Condition.ArnLike.", "withoutKey", "aws:SourceArn" ], + [ "_STATEMENT_.Condition.ArnLike.", "withoutKey", "aws:SourceOwner" ] ] ] ] From 1968914c2c8bc60c2a8cbfe7a0bd996817b70e56 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Fri, 14 Feb 2020 17:03:35 +0100 Subject: [PATCH 087/145] Replace first check --- .../aws/rules/findings/sns-topic-world-policy.json | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/ScoutSuite/providers/aws/rules/findings/sns-topic-world-policy.json b/ScoutSuite/providers/aws/rules/findings/sns-topic-world-policy.json index 06c40630e..66af0ebc9 100644 --- a/ScoutSuite/providers/aws/rules/findings/sns-topic-world-policy.json +++ b/ScoutSuite/providers/aws/rules/findings/sns-topic-world-policy.json @@ -9,13 +9,8 @@ [ "sns.regions.id.topics.id.Policy", "notNull", "" ], [ "sns.regions.id.topics.id.Policy.Statement.id.Effect", "equal", "Allow" ], [ "sns.regions.id.topics.id.Policy.Statement.id.", "containAction", "SNS:_ARG_0_" ], - [ "or", - [ "sns.regions.id.topics.id.Policy.Statement.id.Principal", "equal", "*" ], - [ "and", - [ "sns.regions.id.topics.id.Policy.Statement.id.Principal", "withKey", "AWS" ], - [ "sns.regions.id.topics.id.Policy.Statement.id.Principal.AWS", "containAtLeastOneOf", "*" ] - ] - ], + [ "_INCLUDE_(conditions/policy-statement-any-principal.json)", [ "_STATEMENT_" ], [ "sns.regions.id.topics.id.Policy.Statement.id" ] ], + [ "or", [ "sns.regions.id.topics.id.Policy.Statement.id.", "withoutKey", "Condition" ], [ "sns.regions.id.topics.id.Policy.Statement.id.Condition.", "withoutKey", "StringEquals" ], @@ -24,6 +19,7 @@ [ "sns.regions.id.topics.id.Policy.Statement.id.Condition.StringEquals.", "withoutKey", "AWS:SourceOwner" ] ] ], + [ "or", [ "sns.regions.id.topics.id.Policy.Statement.id.", "withoutKey", "Condition" ], [ "sns.regions.id.topics.id.Policy.Statement.id.Condition.", "withoutKey", "ArnLike" ], From 3d9b10b66fec3fe949cff0c6ce05d456445a9117 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Fri, 14 Feb 2020 17:17:12 +0100 Subject: [PATCH 088/145] Fix logic --- .../conditions/policy-statement-poor-condition.json | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/ScoutSuite/providers/aws/rules/conditions/policy-statement-poor-condition.json b/ScoutSuite/providers/aws/rules/conditions/policy-statement-poor-condition.json index 9654837ab..221d63437 100644 --- a/ScoutSuite/providers/aws/rules/conditions/policy-statement-poor-condition.json +++ b/ScoutSuite/providers/aws/rules/conditions/policy-statement-poor-condition.json @@ -3,15 +3,13 @@ [ "_STATEMENT_.", "withoutKey", "Condition" ], [ "and", [ "_STATEMENT_.Condition.", "withoutKey", "ArnEquals" ], + [ "_STATEMENT_.Condition.", "withoutKey", "ArnLike" ], [ "or", [ "_STATEMENT_.Condition.", "withoutKey", "StringEquals" ], - [ "_STATEMENT_.Condition.StringEquals.", "withoutKey", "aws:SourceArn" ], - [ "_STATEMENT_.Condition.StringEquals.", "withoutKey", "aws:SourceOwner" ] - ], - [ "or", - [ "_STATEMENT_.Condition.", "withoutKey", "ArnLike" ], - [ "_STATEMENT_.Condition.ArnLike.", "withoutKey", "aws:SourceArn" ], - [ "_STATEMENT_.Condition.ArnLike.", "withoutKey", "aws:SourceOwner" ] + [ "and", + [ "_STATEMENT_.Condition.StringEquals.", "withoutKey", "AWS:SourceArn" ], + [ "_STATEMENT_.Condition.StringEquals.", "withoutKey", "AWS:SourceOwner" ] + ] ] ] ] From f4c6dcd990ceb45b1042e30b7afc35947547ca05 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Fri, 14 Feb 2020 17:17:54 +0100 Subject: [PATCH 089/145] Simplify policy --- .../findings/sns-topic-world-policy.json | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/ScoutSuite/providers/aws/rules/findings/sns-topic-world-policy.json b/ScoutSuite/providers/aws/rules/findings/sns-topic-world-policy.json index 66af0ebc9..3c72aab0a 100644 --- a/ScoutSuite/providers/aws/rules/findings/sns-topic-world-policy.json +++ b/ScoutSuite/providers/aws/rules/findings/sns-topic-world-policy.json @@ -10,23 +10,6 @@ [ "sns.regions.id.topics.id.Policy.Statement.id.Effect", "equal", "Allow" ], [ "sns.regions.id.topics.id.Policy.Statement.id.", "containAction", "SNS:_ARG_0_" ], [ "_INCLUDE_(conditions/policy-statement-any-principal.json)", [ "_STATEMENT_" ], [ "sns.regions.id.topics.id.Policy.Statement.id" ] ], - - [ "or", - [ "sns.regions.id.topics.id.Policy.Statement.id.", "withoutKey", "Condition" ], - [ "sns.regions.id.topics.id.Policy.Statement.id.Condition.", "withoutKey", "StringEquals" ], - [ "and", - [ "sns.regions.id.topics.id.Policy.Statement.id.Condition.StringEquals.", "withoutKey", "AWS:SourceArn" ], - [ "sns.regions.id.topics.id.Policy.Statement.id.Condition.StringEquals.", "withoutKey", "AWS:SourceOwner" ] - ] - ], - - [ "or", - [ "sns.regions.id.topics.id.Policy.Statement.id.", "withoutKey", "Condition" ], - [ "sns.regions.id.topics.id.Policy.Statement.id.Condition.", "withoutKey", "ArnLike" ], - [ "and", - [ "sns.regions.id.topics.id.Policy.Statement.id.Condition.ArnLike.", "withoutKey", "AWS:SourceArn" ], - [ "sns.regions.id.topics.id.Policy.Statement.id.Condition.ArnLike.", "withoutKey", "AWS:SourceOwner" ] - ] - ] + [ "_INCLUDE_(conditions/policy-statement-poor-condition.json)", [ "_STATEMENT_" ], [ "sns.regions.id.topics.id.Policy.Statement.id" ] ] ] } From ac7d2480703c8994f2a878e6437eed3d58bf96e2 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Fri, 14 Feb 2020 17:28:21 +0100 Subject: [PATCH 090/145] Simplify rule --- .../rules/findings/iam-assume-role-policy-allows-all.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ScoutSuite/providers/aws/rules/findings/iam-assume-role-policy-allows-all.json b/ScoutSuite/providers/aws/rules/findings/iam-assume-role-policy-allows-all.json index 2c2dcd0a3..73ab88c9d 100644 --- a/ScoutSuite/providers/aws/rules/findings/iam-assume-role-policy-allows-all.json +++ b/ScoutSuite/providers/aws/rules/findings/iam-assume-role-policy-allows-all.json @@ -1,13 +1,12 @@ { "description": "AssumeRole policy allows all principals", - "rationale": "Description:

    Setting the AssumeRole policy's principal attribute to AWS:* means that anyone is authorized to assume the role and access the AWS account.", + "rationale": "Description:

    Setting the AssumeRole policy's principal attribute to \"AWS:*\" means that anyone is authorized to assume the role and access the AWS account.", "path": "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id", "display_path": "iam.roles.id", "dashboard_name": "Roles", "conditions": [ "and", [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.Effect", "equal", "Allow" ], [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.", "containAction", "sts:AssumeRole" ], - [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.Principal", "withKey", "AWS" ], - [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id.Principal.AWS", "containAtLeastOneOf", "*" ] + [ "_INCLUDE_(conditions/policy-statement-any-principal.json)", [ "_STATEMENT_" ], [ "iam.roles.id.assume_role_policy.PolicyDocument.Statement.id" ] ] ] } From 702f9f9614ff2584d59fda94e5424d6e3dfae188 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Fri, 14 Feb 2020 17:29:02 +0100 Subject: [PATCH 091/145] Simplify rule --- .../findings/s3-bucket-world-policy-star.json | 30 ++++++++----------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/ScoutSuite/providers/aws/rules/findings/s3-bucket-world-policy-star.json b/ScoutSuite/providers/aws/rules/findings/s3-bucket-world-policy-star.json index 56e49522b..72c1b751c 100644 --- a/ScoutSuite/providers/aws/rules/findings/s3-bucket-world-policy-star.json +++ b/ScoutSuite/providers/aws/rules/findings/s3-bucket-world-policy-star.json @@ -1,20 +1,14 @@ { - "arg_names" : [ "Action shortname", "Service:Action" ], - "dashboard_name": "Buckets", - "description": "All actions authorized to all principals", - "path": "s3.buckets.id.policy.Statement.id", - "display_path": "s3.buckets.id", - "conditions": [ "and", - [ "s3.buckets.id.", "withKey", "policy" ], - [ "s3.buckets.id.policy.Statement.id.Effect", "equal", "Allow" ], - [ "s3.buckets.id.policy.Statement.id.", "withoutKey", "Condition" ], - [ "s3.buckets.id.policy.Statement.id.Action", "containAtLeastOneOf", [ "s3:*", "*" ] ], - [ "or", - [ "s3.buckets.id.policy.Statement.id.Principal", "containAtLeastOneOf", [ "*" ] ], - [ "and", - [ "s3.buckets.id.policy.Statement.id.Principal", "withKey", "AWS" ], - [ "s3.buckets.id.policy.Statement.id.Principal.AWS", "containAtLeastOneOf", [ "*" ] ] - ] - ] - ] + "arg_names" : [ "Action shortname", "Service:Action" ], + "dashboard_name": "Buckets", + "description": "All actions authorized to all principals", + "path": "s3.buckets.id.policy.Statement.id", + "display_path": "s3.buckets.id", + "conditions": [ "and", + [ "s3.buckets.id.", "withKey", "policy" ], + [ "s3.buckets.id.policy.Statement.id.Effect", "equal", "Allow" ], + [ "s3.buckets.id.policy.Statement.id.Action", "containAtLeastOneOf", [ "s3:*", "*" ] ], + [ "_INCLUDE_(conditions/policy-statement-any-principal.json)", [ "_STATEMENT_" ], [ "s3.buckets.id.policy.Statement.id" ] ], + [ "_INCLUDE_(conditions/policy-statement-poor-condition.json)", [ "_STATEMENT_" ], [ "s3.buckets.id.policy.Statement.id" ] ] + ] } From 2ca1cfbced0fe9f0e43682f538c322482d297d37 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Fri, 14 Feb 2020 17:31:34 +0100 Subject: [PATCH 092/145] Simplify rule --- .../aws/rules/findings/ses-identity-world-policy.json | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/ScoutSuite/providers/aws/rules/findings/ses-identity-world-policy.json b/ScoutSuite/providers/aws/rules/findings/ses-identity-world-policy.json index 096b90270..a2a6a5955 100644 --- a/ScoutSuite/providers/aws/rules/findings/ses-identity-world-policy.json +++ b/ScoutSuite/providers/aws/rules/findings/ses-identity-world-policy.json @@ -8,12 +8,7 @@ "conditions": [ "and", [ "ses.regions.id.identities.id.policies.id.Statement.id.Effect", "equal", "Allow" ], [ "ses.regions.id.identities.id.policies.id.Statement.id.", "containAction", "ses:_ARG_0_" ], - [ "or", - [ "ses.regions.id.identities.id.policies.id.Statement.id.Principal", "containAtLeastOneOf", "*" ], - [ "and", - [ "ses.regions.id.identities.id.policies.id.Statement.id.Principal", "withKey", "AWS" ], - [ "ses.regions.id.identities.id.policies.id.Statement.id.Principal.AWS", "containAtLeastOneOf", "*" ] - ] - ] + [ "_INCLUDE_(conditions/policy-statement-any-principal.json)", [ "_STATEMENT_" ], [ "ses.regions.id.identities.id.policies.id.Statement.id" ] ], + [ "_INCLUDE_(conditions/policy-statement-poor-condition.json)", [ "_STATEMENT_" ], [ "ses.regions.id.identities.id.policies.id.Statement.id" ] ] ] } From 4ca6b63c699cda71507da1903dd1ddb36cb7e1cc Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Fri, 14 Feb 2020 17:36:33 +0100 Subject: [PATCH 093/145] Minor change --- .../data/html/partials/aws/services.sqs.regions.id.queues.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ScoutSuite/output/data/html/partials/aws/services.sqs.regions.id.queues.html b/ScoutSuite/output/data/html/partials/aws/services.sqs.regions.id.queues.html index b9ec9db4c..b8734062b 100644 --- a/ScoutSuite/output/data/html/partials/aws/services.sqs.regions.id.queues.html +++ b/ScoutSuite/output/data/html/partials/aws/services.sqs.regions.id.queues.html @@ -15,7 +15,7 @@

    Information

    {{#if Policy.Statement.length}}
    - {{> accordion_policy name = 'Access control policy' policy_path = (concat 'sqs.regions' region 'queues' @key 'Policy') document = Policy}} + {{> accordion_policy name = 'Access Control Policy' policy_path = (concat 'sqs.regions' region 'queues' @key 'Policy') document = Policy}}
    {{/if}} From a3d5598b8d54221a0d66744648d058a1303cd200 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Fri, 14 Feb 2020 17:36:38 +0100 Subject: [PATCH 094/145] Minor change --- .../html/partials/aws/services.sns.regions.id.topics.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ScoutSuite/output/data/html/partials/aws/services.sns.regions.id.topics.html b/ScoutSuite/output/data/html/partials/aws/services.sns.regions.id.topics.html index e81f07a5b..bb1464304 100644 --- a/ScoutSuite/output/data/html/partials/aws/services.sns.regions.id.topics.html +++ b/ScoutSuite/output/data/html/partials/aws/services.sns.regions.id.topics.html @@ -12,12 +12,12 @@

    Information

    {{#if Policy}}
    - {{> accordion_policy name = 'Access control policy' policy_path = (concat 'sns.regions' region 'topics' @key 'Policy') document = Policy}} + {{> accordion_policy name = 'Access Control Policy' policy_path = (concat 'sns.regions' region 'topics' @key 'Policy') document = Policy}}
    {{/if}} {{#if DeliveryPolicy}}
    - {{> accordion_policy name = 'Delivery policy' policy_path = (concat 'sns.regions' region 'topics' @key 'DeliveryPolicy') document = DeliveryPolicy}} + {{> accordion_policy name = 'Delivery Policy' policy_path = (concat 'sns.regions' region 'topics' @key 'DeliveryPolicy') document = DeliveryPolicy}}
    {{/if}} {{#if EffectiveDeliveryPolicy}} From 9a2538769abc435ad2932d0e42c96d3b898f0212 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Fri, 14 Feb 2020 18:53:35 +0100 Subject: [PATCH 095/145] Includes "ForAnyValue" in checks. Resolves https://github.com/nccgroup/ScoutSuite/issues/538 --- .../conditions/policy-statement-poor-condition.json | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ScoutSuite/providers/aws/rules/conditions/policy-statement-poor-condition.json b/ScoutSuite/providers/aws/rules/conditions/policy-statement-poor-condition.json index 221d63437..769df6f97 100644 --- a/ScoutSuite/providers/aws/rules/conditions/policy-statement-poor-condition.json +++ b/ScoutSuite/providers/aws/rules/conditions/policy-statement-poor-condition.json @@ -2,8 +2,14 @@ "conditions": [ "or", [ "_STATEMENT_.", "withoutKey", "Condition" ], [ "and", - [ "_STATEMENT_.Condition.", "withoutKey", "ArnEquals" ], - [ "_STATEMENT_.Condition.", "withoutKey", "ArnLike" ], + [ "and", + [ "_STATEMENT_.Condition.", "withoutKey", "ArnEquals" ], + [ "_STATEMENT_.Condition.", "withoutKey", "ForAnyValue:ArnEquals" ] + ], + [ "and", + [ "_STATEMENT_.Condition.", "withoutKey", "ArnLike" ], + [ "_STATEMENT_.Condition.", "withoutKey", "ForAnyValue:ArnLike" ] + ], [ "or", [ "_STATEMENT_.Condition.", "withoutKey", "StringEquals" ], [ "and", From 7a447bfaaf114b9136163d59d4e9581abb5b6c32 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Sun, 16 Feb 2020 19:24:07 +0100 Subject: [PATCH 096/145] Basic suport for alerts --- .../azure/resources/securitycenter/alerts.py | 21 +++++++++++++++++++ .../azure/resources/securitycenter/base.py | 2 ++ 2 files changed, 23 insertions(+) create mode 100644 ScoutSuite/providers/azure/resources/securitycenter/alerts.py diff --git a/ScoutSuite/providers/azure/resources/securitycenter/alerts.py b/ScoutSuite/providers/azure/resources/securitycenter/alerts.py new file mode 100644 index 000000000..9b81904bb --- /dev/null +++ b/ScoutSuite/providers/azure/resources/securitycenter/alerts.py @@ -0,0 +1,21 @@ +from ScoutSuite.providers.azure.facade.base import AzureFacade +from ScoutSuite.providers.azure.resources.base import AzureResources + + +class Alerts(AzureResources): + + def __init__(self, facade: AzureFacade, subscription_id: str): + super(Alerts, self).__init__(facade) + self.subscription_id = subscription_id + + async def fetch_all(self): + a = await self.facade.securitycenter.get_alerts(self.subscription_id) + for raw_alert in await self.facade.securitycenter.get_alerts(self.subscription_id): + id, alert = self._parse_alert(raw_alert) + self[id] = alert + + def _parse_alert(self, alert): + alert_dict = {} + alert_dict['id'] = alert.id + alert_dict['name'] = alert.name + return alert_dict['id'], alert_dict diff --git a/ScoutSuite/providers/azure/resources/securitycenter/base.py b/ScoutSuite/providers/azure/resources/securitycenter/base.py index 701d01077..0dbdbfdb0 100644 --- a/ScoutSuite/providers/azure/resources/securitycenter/base.py +++ b/ScoutSuite/providers/azure/resources/securitycenter/base.py @@ -2,6 +2,7 @@ from .auto_provisioning_settings import AutoProvisioningSettings from .pricings import Pricings +from .alerts import Alerts from .security_contacts import SecurityContacts # from .information_protection_policies import InformationProtectionPolicies # from .settings import Settings @@ -11,6 +12,7 @@ class SecurityCenter(Subscriptions): _children = [ (AutoProvisioningSettings, 'auto_provisioning_settings'), (Pricings, 'pricings'), + # (Alerts, 'alerts'), # FIXME this needs to be tested with alert results... (SecurityContacts, 'security_contacts'), # (InformationProtectionPolicies, 'information_protection_policies'), # FIXME this isn't properly implemented # (Settings, 'settings') # FIXME this isn't implemented From d658140eaa69fd56cbc63a80cc53290d9abc89d2 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Sun, 16 Feb 2020 19:39:46 +0100 Subject: [PATCH 097/145] Add support for Security Center compliance results --- ...r.subscriptions.id.compliance_results.html | 23 ++++++++++++++++++ .../providers/azure/facade/securitycenter.py | 21 ++++++++++++++++ ScoutSuite/providers/azure/metadata.json | 4 ++++ .../azure/resources/securitycenter/base.py | 2 ++ .../securitycenter/compliance_results.py | 24 +++++++++++++++++++ 5 files changed, 74 insertions(+) create mode 100644 ScoutSuite/output/data/html/partials/azure/services.securitycenter.subscriptions.id.compliance_results.html create mode 100644 ScoutSuite/providers/azure/resources/securitycenter/compliance_results.py diff --git a/ScoutSuite/output/data/html/partials/azure/services.securitycenter.subscriptions.id.compliance_results.html b/ScoutSuite/output/data/html/partials/azure/services.securitycenter.subscriptions.id.compliance_results.html new file mode 100644 index 000000000..bc8ca1e29 --- /dev/null +++ b/ScoutSuite/output/data/html/partials/azure/services.securitycenter.subscriptions.id.compliance_results.html @@ -0,0 +1,23 @@ + + + + + + + + diff --git a/ScoutSuite/providers/azure/facade/securitycenter.py b/ScoutSuite/providers/azure/facade/securitycenter.py index 7ca3a77a7..ded49a4e3 100644 --- a/ScoutSuite/providers/azure/facade/securitycenter.py +++ b/ScoutSuite/providers/azure/facade/securitycenter.py @@ -60,3 +60,24 @@ async def get_settings(self, subscription_id: str): except Exception as e: print_exception('Failed to retrieve settings: {}'.format(e)) return [] + + async def get_alerts(self, subscription_id: str): + try: + client = self.get_client(subscription_id) + return await run_concurrently( + lambda: list(client.alerts.list()) + ) + except Exception as e: + print_exception('Failed to retrieve alerts: {}'.format(e)) + return [] + + async def get_compliance_results(self, subscription_id: str): + try: + client = self.get_client(subscription_id) + scope = '/subscriptions/{}'.format(subscription_id) + return await run_concurrently( + lambda: list(client.compliance_results.list(scope=scope)) + ) + except Exception as e: + print_exception('Failed to retrieve compliance results: {}'.format(e)) + return [] diff --git a/ScoutSuite/providers/azure/metadata.json b/ScoutSuite/providers/azure/metadata.json index a2dc55553..e05305d91 100644 --- a/ScoutSuite/providers/azure/metadata.json +++ b/ScoutSuite/providers/azure/metadata.json @@ -95,6 +95,10 @@ "auto_provisioning_settings": { "cols": 2, "path": "services.securitycenter.subscriptions.id.auto_provisioning_settings" + }, + "compliance_results": { + "cols": 2, + "path": "services.securitycenter.subscriptions.id.compliance_results" } } } diff --git a/ScoutSuite/providers/azure/resources/securitycenter/base.py b/ScoutSuite/providers/azure/resources/securitycenter/base.py index 0dbdbfdb0..97786109d 100644 --- a/ScoutSuite/providers/azure/resources/securitycenter/base.py +++ b/ScoutSuite/providers/azure/resources/securitycenter/base.py @@ -6,6 +6,7 @@ from .security_contacts import SecurityContacts # from .information_protection_policies import InformationProtectionPolicies # from .settings import Settings +from .compliance_results import ComplianceResults class SecurityCenter(Subscriptions): @@ -16,4 +17,5 @@ class SecurityCenter(Subscriptions): (SecurityContacts, 'security_contacts'), # (InformationProtectionPolicies, 'information_protection_policies'), # FIXME this isn't properly implemented # (Settings, 'settings') # FIXME this isn't implemented + (ComplianceResults, 'compliance_results') ] diff --git a/ScoutSuite/providers/azure/resources/securitycenter/compliance_results.py b/ScoutSuite/providers/azure/resources/securitycenter/compliance_results.py new file mode 100644 index 000000000..ae4cee04a --- /dev/null +++ b/ScoutSuite/providers/azure/resources/securitycenter/compliance_results.py @@ -0,0 +1,24 @@ +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 ComplianceResults(AzureResources): + + def __init__(self, facade: AzureFacade, subscription_id: str): + super(ComplianceResults, self).__init__(facade) + self.subscription_id = subscription_id + + async def fetch_all(self): + for raw_compliance_result in await self.facade.securitycenter.get_compliance_results(self.subscription_id): + id, compliance_result = self._parse_compliance_result(raw_compliance_result) + self[id] = compliance_result + + def _parse_compliance_result(self, raw_compliance_result): + compliance_result_dict = {} + compliance_result_dict['id'] = get_non_provider_id(raw_compliance_result.id) + compliance_result_dict['name'] = raw_compliance_result.name + compliance_result_dict['type'] = raw_compliance_result.type + compliance_result_dict['resource_status'] = raw_compliance_result.resource_status + compliance_result_dict['additional_properties'] = raw_compliance_result.additional_properties + return compliance_result_dict['id'], compliance_result_dict From d4a068d9d1b023c88efb98c37756344b330fb5b9 Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Sun, 16 Feb 2020 20:40:22 +0100 Subject: [PATCH 098/145] Add support for Security Center regulatory compliance results --- ...ions.id.regulatory_compliance_results.html | 28 +++++++++++++++++ .../providers/azure/facade/securitycenter.py | 31 +++++++++++++++++++ ScoutSuite/providers/azure/metadata.json | 4 +++ .../azure/resources/securitycenter/base.py | 4 ++- .../regulatory_compliance_results.py | 31 +++++++++++++++++++ 5 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 ScoutSuite/output/data/html/partials/azure/services.securitycenter.subscriptions.id.regulatory_compliance_results.html create mode 100644 ScoutSuite/providers/azure/resources/securitycenter/regulatory_compliance_results.py diff --git a/ScoutSuite/output/data/html/partials/azure/services.securitycenter.subscriptions.id.regulatory_compliance_results.html b/ScoutSuite/output/data/html/partials/azure/services.securitycenter.subscriptions.id.regulatory_compliance_results.html new file mode 100644 index 000000000..dc7dc44ad --- /dev/null +++ b/ScoutSuite/output/data/html/partials/azure/services.securitycenter.subscriptions.id.regulatory_compliance_results.html @@ -0,0 +1,28 @@ + + + + + + + + diff --git a/ScoutSuite/providers/azure/facade/securitycenter.py b/ScoutSuite/providers/azure/facade/securitycenter.py index ded49a4e3..cd698e8ee 100644 --- a/ScoutSuite/providers/azure/facade/securitycenter.py +++ b/ScoutSuite/providers/azure/facade/securitycenter.py @@ -81,3 +81,34 @@ async def get_compliance_results(self, subscription_id: str): except Exception as e: print_exception('Failed to retrieve compliance results: {}'.format(e)) return [] + + async def get_regulatory_compliance_results(self, subscription_id: str): + try: + client = self.get_client(subscription_id) + results = [] + try: + compliance_standards = await run_concurrently( + lambda: list(client.regulatory_compliance_standards.list()) + ) + except Exception as e: + print_exception('Failed to retrieve regulatory compliance standards: {}'.format(e)) + return {} + else: + for standard in compliance_standards: + try: + compliance_controls = await run_concurrently( + lambda: list(client.regulatory_compliance_controls.list(regulatory_compliance_standard_name=standard.name)) + ) + for control in compliance_controls: + control.standard_name = standard.name + results.append(control) + except Exception as e: + print_exception('Failed to retrieve compliance controls: {}'.format(e)) + pass + finally: + return results + except Exception as e: + print_exception('Failed to retrieve regulatory compliance results: {}'.format(e)) + return [] + + diff --git a/ScoutSuite/providers/azure/metadata.json b/ScoutSuite/providers/azure/metadata.json index e05305d91..73067e6c6 100644 --- a/ScoutSuite/providers/azure/metadata.json +++ b/ScoutSuite/providers/azure/metadata.json @@ -99,6 +99,10 @@ "compliance_results": { "cols": 2, "path": "services.securitycenter.subscriptions.id.compliance_results" + }, + "regulatory_compliance_results": { + "cols": 2, + "path": "services.securitycenter.subscriptions.id.regulatory_compliance_results" } } } diff --git a/ScoutSuite/providers/azure/resources/securitycenter/base.py b/ScoutSuite/providers/azure/resources/securitycenter/base.py index 97786109d..aa7effc3b 100644 --- a/ScoutSuite/providers/azure/resources/securitycenter/base.py +++ b/ScoutSuite/providers/azure/resources/securitycenter/base.py @@ -7,6 +7,7 @@ # from .information_protection_policies import InformationProtectionPolicies # from .settings import Settings from .compliance_results import ComplianceResults +from .regulatory_compliance_results import RegulatoryComplianceResults class SecurityCenter(Subscriptions): @@ -17,5 +18,6 @@ class SecurityCenter(Subscriptions): (SecurityContacts, 'security_contacts'), # (InformationProtectionPolicies, 'information_protection_policies'), # FIXME this isn't properly implemented # (Settings, 'settings') # FIXME this isn't implemented - (ComplianceResults, 'compliance_results') + (ComplianceResults, 'compliance_results'), + (RegulatoryComplianceResults, 'regulatory_compliance_results') ] diff --git a/ScoutSuite/providers/azure/resources/securitycenter/regulatory_compliance_results.py b/ScoutSuite/providers/azure/resources/securitycenter/regulatory_compliance_results.py new file mode 100644 index 000000000..2d4b0876f --- /dev/null +++ b/ScoutSuite/providers/azure/resources/securitycenter/regulatory_compliance_results.py @@ -0,0 +1,31 @@ +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 RegulatoryComplianceResults(AzureResources): + + def __init__(self, facade: AzureFacade, subscription_id: str): + super(RegulatoryComplianceResults, self).__init__(facade) + self.subscription_id = subscription_id + + async def fetch_all(self): + for raw_regulatory_compliance_result in await self.facade.securitycenter.get_regulatory_compliance_results(self.subscription_id): + id, regulatory_compliance_result = self._parse_regulatory_compliance_result(raw_regulatory_compliance_result) + self[id] = regulatory_compliance_result + + def _parse_regulatory_compliance_result(self, raw_regulatory_compliance_result): + regulatory_compliance_result_dict = {} + regulatory_compliance_result_dict['id'] = get_non_provider_id(raw_regulatory_compliance_result.id) + regulatory_compliance_result_dict['name'] = '{} {}'.format(raw_regulatory_compliance_result.standard_name, + raw_regulatory_compliance_result.name) + regulatory_compliance_result_dict['reference'] = raw_regulatory_compliance_result.name + regulatory_compliance_result_dict['standard_name'] = raw_regulatory_compliance_result.standard_name + regulatory_compliance_result_dict['type'] = raw_regulatory_compliance_result.type + regulatory_compliance_result_dict['description'] = raw_regulatory_compliance_result.description + regulatory_compliance_result_dict['state'] = raw_regulatory_compliance_result.state + regulatory_compliance_result_dict['passed_assessments'] = raw_regulatory_compliance_result.passed_assessments + regulatory_compliance_result_dict['failed_assessments'] = raw_regulatory_compliance_result.failed_assessments + regulatory_compliance_result_dict['skipped_assessments'] = raw_regulatory_compliance_result.skipped_assessments + regulatory_compliance_result_dict['additional_properties'] = raw_regulatory_compliance_result.additional_properties + return regulatory_compliance_result_dict['id'], regulatory_compliance_result_dict From d045e2fe72a347fcbbd942024573959eef93f32b Mon Sep 17 00:00:00 2001 From: Xavier Garceau-Aranda Date: Sun, 16 Feb 2020 20:46:17 +0100 Subject: [PATCH 099/145] Reformat code --- .../securitycenter/regulatory_compliance_results.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ScoutSuite/providers/azure/resources/securitycenter/regulatory_compliance_results.py b/ScoutSuite/providers/azure/resources/securitycenter/regulatory_compliance_results.py index 2d4b0876f..5a2d06abc 100644 --- a/ScoutSuite/providers/azure/resources/securitycenter/regulatory_compliance_results.py +++ b/ScoutSuite/providers/azure/resources/securitycenter/regulatory_compliance_results.py @@ -10,8 +10,10 @@ def __init__(self, facade: AzureFacade, subscription_id: str): self.subscription_id = subscription_id async def fetch_all(self): - for raw_regulatory_compliance_result in await self.facade.securitycenter.get_regulatory_compliance_results(self.subscription_id): - id, regulatory_compliance_result = self._parse_regulatory_compliance_result(raw_regulatory_compliance_result) + for raw_regulatory_compliance_result in await \ + self.facade.securitycenter.get_regulatory_compliance_results(self.subscription_id): + id, regulatory_compliance_result = \ + self._parse_regulatory_compliance_result(raw_regulatory_compliance_result) self[id] = regulatory_compliance_result def _parse_regulatory_compliance_result(self, raw_regulatory_compliance_result): @@ -27,5 +29,6 @@ def _parse_regulatory_compliance_result(self, raw_regulatory_compliance_result): regulatory_compliance_result_dict['passed_assessments'] = raw_regulatory_compliance_result.passed_assessments regulatory_compliance_result_dict['failed_assessments'] = raw_regulatory_compliance_result.failed_assessments regulatory_compliance_result_dict['skipped_assessments'] = raw_regulatory_compliance_result.skipped_assessments - regulatory_compliance_result_dict['additional_properties'] = raw_regulatory_compliance_result.additional_properties + regulatory_compliance_result_dict['additional_properties'] = \ + raw_regulatory_compliance_result.additional_properties return regulatory_compliance_result_dict['id'], regulatory_compliance_result_dict From 8a08c040a317dc769b6dadb8eec35ade9e5daa59 Mon Sep 17 00:00:00 2001 From: Pau Risa Date: Tue, 18 Feb 2020 17:53:24 +0100 Subject: [PATCH 100/145] Deleted regions filter on navbar Deleted the regions filters on the navbar, added the name of the cloud provider to the CLI output and fixed some typos. #647 --- ScoutSuite/__main__.py | 2 +- ScoutSuite/core/server.py | 18 +++++++++--------- .../output/data/html/partials/metadata.html | 5 ----- .../data/html/partials/regionfilters.html | 10 ---------- 4 files changed, 10 insertions(+), 25 deletions(-) delete mode 100644 ScoutSuite/output/data/html/partials/regionfilters.html diff --git a/ScoutSuite/__main__.py b/ScoutSuite/__main__.py index 8e58dfcfb..98159a2e8 100644 --- a/ScoutSuite/__main__.py +++ b/ScoutSuite/__main__.py @@ -177,7 +177,7 @@ async def _run(provider, print_info('Launching Scout') - print_info('Authenticating to cloud provider') + print_info('Authenticating to %s cloud provider' % provider.upper()) auth_strategy = get_authentication_strategy(provider) try: credentials = auth_strategy.authenticate(profile=profile, diff --git a/ScoutSuite/core/server.py b/ScoutSuite/core/server.py index c7a388669..13d342777 100644 --- a/ScoutSuite/core/server.py +++ b/ScoutSuite/core/server.py @@ -25,7 +25,7 @@ def __init__(self, filename): @cherrypy.tools.json_out() def summary(self): """ - Returns the stripped down data of the results that doesn't scale up when using a lot of ressources, + Returns the stripped down data of the results that doesn't scale up when using a lot of resources, used to render the summary. Should be the first call from the server. Can be found at GET /api/summary @@ -48,8 +48,8 @@ def summary(self): @cherrypy.tools.json_out() def data(self, key=None): """ - Return the data at the requested key. Doesn't returns nested dictionnaries and lists. - If one of the value is a dictionnary, it will return {'type': 'dict', 'keys': } + Return the data at the requested key. Doesn't returns nested dictionaries and lists. + If one of the value is a dictionary, it will return {'type': 'dict', 'keys': } If one of the value is a list, it will return {'type': 'list', 'count': } Can be found at GET /api/data?key= @@ -85,9 +85,9 @@ def full(self, key=None): @cherrypy.tools.json_out() def page(self, key=None, page=None, pagesize=None): """ - Return a page of the data at the requested key. Doesn't returns nested dictionnaries and lists. + Return a page of the data at the requested key. Doesn't returns nested dictionaries and lists. For example, if you set pagesize=10 and page=2, it should return element 10-19 - If one of the value is a dictionnary, it will return {'type': 'dict', 'keys': } + If one of the value is a dictionary, it will return {'type': 'dict', 'keys': } If one of the value is a list, it will return {'type': 'list', 'count': } Can be found at GET /api/page?key=&page=&pagesize= @@ -138,9 +138,9 @@ def init(database_filename, host, port): @staticmethod def get_item(data, key): """ - Get a specific informations from its key. + Get a specific information from its key. - :param data: The dictionnary in which the information is stored. + :param data: The dictionary in which the information is stored. :param host: The key where the information is located. :return: The nested data at the requested location. """ @@ -158,10 +158,10 @@ def get_item(data, key): @staticmethod def strip_nested_data(data): """ - Strip nested lists and dictionnaries from the provided object to reduce its size. + Strip nested lists and dictionaries from the provided object to reduce its size. :param data: The object to strip. - :return: The input data stripped of its nested lists and dictionnaries. + :return: The input data stripped of its nested lists and dictionaries. """ if not isinstance(data, dict): return data diff --git a/ScoutSuite/output/data/html/partials/metadata.html b/ScoutSuite/output/data/html/partials/metadata.html index 49fc97e16..1fbfbbbed 100644 --- a/ScoutSuite/output/data/html/partials/metadata.html +++ b/ScoutSuite/output/data/html/partials/metadata.html @@ -70,11 +70,6 @@