From ddc4c68d4ef841cb9ced10851188f31520c42da1 Mon Sep 17 00:00:00 2001 From: "Li, Sam" Date: Thu, 15 Oct 2020 18:36:15 -0400 Subject: [PATCH 001/193] Implement code pulling aws cloudfront assets --- ScoutSuite/providers/aws/facade/base.py | 4 ++- ScoutSuite/providers/aws/facade/cloudfront.py | 35 +++++++++++++++++++ ScoutSuite/providers/aws/facade/utils.py | 5 ++- .../aws/resources/cloudfront/__init__.py | 0 .../aws/resources/cloudfront/base.py | 18 ++++++++++ .../aws/resources/cloudfront/distributions.py | 27 ++++++++++++++ ScoutSuite/providers/aws/resources/regions.py | 1 - ScoutSuite/providers/aws/services.py | 3 ++ ScoutSuite/providers/base/resources/base.py | 2 +- ScoutSuite/providers/base/services.py | 3 +- ScoutSuite/utils.py | 1 + 11 files changed, 91 insertions(+), 8 deletions(-) create mode 100644 ScoutSuite/providers/aws/facade/cloudfront.py create mode 100644 ScoutSuite/providers/aws/resources/cloudfront/__init__.py create mode 100644 ScoutSuite/providers/aws/resources/cloudfront/base.py create mode 100644 ScoutSuite/providers/aws/resources/cloudfront/distributions.py diff --git a/ScoutSuite/providers/aws/facade/base.py b/ScoutSuite/providers/aws/facade/base.py index a2e25f743..eb7b5f91c 100755 --- a/ScoutSuite/providers/aws/facade/base.py +++ b/ScoutSuite/providers/aws/facade/base.py @@ -6,6 +6,7 @@ from ScoutSuite.providers.aws.facade.cloudformation import CloudFormation from ScoutSuite.providers.aws.facade.cloudtrail import CloudTrailFacade from ScoutSuite.providers.aws.facade.cloudwatch import CloudWatch +from ScoutSuite.providers.aws.facade.cloudfront import CloudFront from ScoutSuite.providers.aws.facade.config import ConfigFacade from ScoutSuite.providers.aws.facade.directconnect import DirectConnectFacade from ScoutSuite.providers.aws.facade.ec2 import EC2Facade @@ -248,8 +249,8 @@ def _instantiate_facades(self): self.directconnect = DirectConnectFacade(self.session) self.efs = EFSFacade(self.session) self.elasticache = ElastiCacheFacade(self.session) - self.emr = EMRFacade(self.session) self.route53 = Route53Facade(self.session) + self.cloudfront = CloudFront(self.session) self.elb = ELBFacade(self.session) self.elbv2 = ELBv2Facade(self.session) self.iam = IAMFacade(self.session) @@ -261,6 +262,7 @@ def _instantiate_facades(self): self.sns = SNSFacade(self.session) self.sqs = SQSFacade(self.session) self.secretsmanager = SecretsManagerFacade(self.session) + self.emr = EMRFacade(self.session) # Instantiate facades for proprietary services try: diff --git a/ScoutSuite/providers/aws/facade/cloudfront.py b/ScoutSuite/providers/aws/facade/cloudfront.py new file mode 100644 index 000000000..63fde3f01 --- /dev/null +++ b/ScoutSuite/providers/aws/facade/cloudfront.py @@ -0,0 +1,35 @@ +import asyncio + +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 + +class CloudFront(AWSBaseFacade): + async def get_distributions(self): + client = AWSFacadeUtils.get_client('cloudfront',self.session) + # When no cloudfront distribution exists, we first need to initiate the creation + # of a new distributions generate_credential_report by calling + # client.list_distributions and then check for COMPLETE status before trying to download it: + aws_cloudfront_api_called, n_attempts = False, 3 + try: + while not aws_cloudfront_api_called and n_attempts > 0: + response = await run_concurrently(client.list_distributions) + if 'ResponseMetadata' in response: + aws_cloudfront_api_called = True + else: + n_attempts -= 1 + await asyncio.sleep(0.1) # Wait for 100ms before doing a new attempt. + except Exception as e: + print_exception('Failed to call aws cloudfront api: {}'.format(e)) + return [] + finally: + if not aws_cloudfront_api_called and n_attempts == 0: + print_exception('Failed to call aws cloudfront api in {} attempts'.format(n_attempts)) + return [] + + try: + return response['DistributionList']['Items'] + except Exception as e: + print_exception('Failed to parse cloudfront distribution list: {}'.format(e)) + return [] diff --git a/ScoutSuite/providers/aws/facade/utils.py b/ScoutSuite/providers/aws/facade/utils.py index 3f9e31f41..d53b6ab7f 100755 --- a/ScoutSuite/providers/aws/facade/utils.py +++ b/ScoutSuite/providers/aws/facade/utils.py @@ -18,7 +18,7 @@ async def get_all_pages(service: str, region: str, session: boto3.session.Sessio :param region:str: Region :param session:boto3.session.Session: Boto3 session used to authenticate the client :param paginator_name:str: Name of the paginator - :param entity:str: Key used to retreive the entities in the paginator's response + :param entity:str: Key used to retreive the entities in the paginator's response :param **paginator_args: Arguments passed to the paginator :return: A list of the fetched entities. @@ -26,7 +26,6 @@ async def get_all_pages(service: str, region: str, session: boto3.session.Sessio results = await AWSFacadeUtils.get_multiple_entities_from_all_pages( service, region, session, paginator_name, [entity], **paginator_args) - if len(results) > 0: return results[entity] else: @@ -41,7 +40,7 @@ async def get_multiple_entities_from_all_pages(service: str, region: str, sessio :param region:str: Region :param session:boto3.session.Session: Boto3 session used to authenticate the client :param paginator_name:str: Name of the paginator - :param entities:list: Keys used to retreive the entities in the paginator's response + :param entities:list: Keys used to retreive the entities in the paginator's response :param **paginator_args: Arguments passed to the paginator :return: A dictionary with the entity keys as keys, and the fetched entities lists as values. diff --git a/ScoutSuite/providers/aws/resources/cloudfront/__init__.py b/ScoutSuite/providers/aws/resources/cloudfront/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/ScoutSuite/providers/aws/resources/cloudfront/base.py b/ScoutSuite/providers/aws/resources/cloudfront/base.py new file mode 100644 index 000000000..cbb35f039 --- /dev/null +++ b/ScoutSuite/providers/aws/resources/cloudfront/base.py @@ -0,0 +1,18 @@ +from ScoutSuite.providers.aws.resources.base import AWSCompositeResources +from ScoutSuite.providers.aws.facade.base import AWSFacade +from ScoutSuite.providers.aws.resources.cloudfront.distributions import Distributions + +from .distributions import Distributions + + +class CloudFront(AWSCompositeResources): + _children = [ + (Distributions, 'distributions') + ] + + def __init__(self, facade: AWSFacade): + super(CloudFront, self).__init__(facade) + self.service = 'cloudfront' + + async def fetch_all(self, partition_name='aws', **kwargs): + await self._fetch_children(self) diff --git a/ScoutSuite/providers/aws/resources/cloudfront/distributions.py b/ScoutSuite/providers/aws/resources/cloudfront/distributions.py new file mode 100644 index 000000000..c6f922455 --- /dev/null +++ b/ScoutSuite/providers/aws/resources/cloudfront/distributions.py @@ -0,0 +1,27 @@ +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 Distributions(AWSResources): + async def fetch_all(self): + list_distributions = await self.facade.cloudfront.get_distributions() + for distribution in list_distributions: + id, distro = self._parse_distributions(distribution) + self[id] = distro + + def _parse_distributions(self, distribution): + distribution_dict = {} + distribution_dict['id'] = distribution.get('Id') + distribution_dict['arn'] = distribution.get('ARN') + distribution_dict['status'] = distribution.get('Status') + distribution_dict['last_modified_time'] = distribution.get('LastModifiedTime') + distribution_dict['aliases'] = distribution.get('Aliases') + distribution_dict['domain_name'] = distribution.get('DomainName') + distribution_dict['origins'] = distribution.get('Origins') + distribution_dict['viewer_protocol_policy'] = distribution.get('ViewerProtocolPolicy') + distribution_dict['allowed_methods'] = distribution.get('AllowedMethods') + distribution_dict['view_certificate'] = distribution.get('ViewerCertificate') + distribution_dict['restrictions'] = distribution.get('Restrictions') + distribution_dict['alias_icp_recordals'] = distribution.get('AliasICPRecordals') + return distribution_dict['id'], distribution_dict diff --git a/ScoutSuite/providers/aws/resources/regions.py b/ScoutSuite/providers/aws/resources/regions.py index 1581a1be7..fb84a3a2e 100755 --- a/ScoutSuite/providers/aws/resources/regions.py +++ b/ScoutSuite/providers/aws/resources/regions.py @@ -32,5 +32,4 @@ def _set_counts(self): # counting them would make the report confusing. if key == 'vpcs': continue - self[key + '_count'] = sum([region[key + '_count'] for region in self['regions'].values()]) diff --git a/ScoutSuite/providers/aws/services.py b/ScoutSuite/providers/aws/services.py index ba0141ea6..f0cea1aee 100755 --- a/ScoutSuite/providers/aws/services.py +++ b/ScoutSuite/providers/aws/services.py @@ -4,6 +4,7 @@ from ScoutSuite.providers.aws.resources.cloudformation.base import CloudFormation from ScoutSuite.providers.aws.resources.cloudtrail.base import CloudTrail from ScoutSuite.providers.aws.resources.cloudwatch.base import CloudWatch +from ScoutSuite.providers.aws.resources.cloudfront.base import CloudFront from ScoutSuite.providers.aws.resources.config.base import Config from ScoutSuite.providers.aws.resources.directconnect.base import DirectConnect from ScoutSuite.providers.aws.resources.ec2.base import EC2 @@ -58,6 +59,7 @@ class AWSServicesConfig(BaseServicesConfig): :ivar cloudtrail: CloudTrail configuration :ivar cloudwatch: CloudWatch configuration: + :ivar cloudfront: CloudFront configuration :ivar config: Config configuration :ivar dynamodb: DynomaDB configuration :ivar ec2: EC2 configuration @@ -85,6 +87,7 @@ def __init__(self, credentials=None, **kwargs): self.cloudformation = CloudFormation(facade) self.cloudtrail = CloudTrail(facade) self.cloudwatch = CloudWatch(facade) + self.cloudfront = CloudFront(facade) self.config = Config(facade) self.directconnect = DirectConnect(facade) self.ec2 = EC2(facade) diff --git a/ScoutSuite/providers/base/resources/base.py b/ScoutSuite/providers/base/resources/base.py index 656155a7d..71f27fb9e 100755 --- a/ScoutSuite/providers/base/resources/base.py +++ b/ScoutSuite/providers/base/resources/base.py @@ -34,7 +34,7 @@ async def fetch_all(self, **kwargs): class CompositeResources(Resources, metaclass=abc.ABCMeta): """This class represents a node in the hierarchical structure. As inherited from `Resources`, it still \ - stores instances of a given type of resources internally but also stores some kind of nested resources \ + stores instances of a given type of resources internally but also stores some kind of nested resources \ referred to as its 'children'. """ diff --git a/ScoutSuite/providers/base/services.py b/ScoutSuite/providers/base/services.py index 96db93b4b..2132a76c5 100755 --- a/ScoutSuite/providers/base/services.py +++ b/ScoutSuite/providers/base/services.py @@ -52,8 +52,7 @@ async def _fetch(self, service, regions=None, excluded_regions=None): if service != 'iam': method_args['partition_name'] = get_partition_name(self.credentials.session) - await service_config.fetch_all(**method_args) - + await service_config.fetch_all(**method_args) if hasattr(service_config, 'finalize'): await service_config.finalize() else: diff --git a/ScoutSuite/utils.py b/ScoutSuite/utils.py index 0c72b57da..5b8696077 100755 --- a/ScoutSuite/utils.py +++ b/ScoutSuite/utils.py @@ -14,6 +14,7 @@ 'cloudformation': 'CloudFormation', 'cloudtrail': 'CloudTrail', 'cloudwatch': 'CloudWatch', + 'cloudfront': 'CloudFront', 'credentials': 'Credentials', 'cognito': 'Cognito', 'config': 'Config', From cbe46c4561bc662ed5d3693faee89df803872e2d Mon Sep 17 00:00:00 2001 From: "Alessandro.Gonzalez" Date: Mon, 2 Nov 2020 11:46:09 +0000 Subject: [PATCH 002/193] Map property to support rules related to SNS topic encryption --- ScoutSuite/providers/aws/resources/sns/topics.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ScoutSuite/providers/aws/resources/sns/topics.py b/ScoutSuite/providers/aws/resources/sns/topics.py index b98fd3b10..7483bdc96 100755 --- a/ScoutSuite/providers/aws/resources/sns/topics.py +++ b/ScoutSuite/providers/aws/resources/sns/topics.py @@ -41,4 +41,7 @@ def _parse_topic(self, raw_topic): for k in ['Policy', 'DeliveryPolicy', 'EffectiveDeliveryPolicy']: raw_topic[k] = json.loads(attributes[k]) if k in attributes else None + if "KmsMasterKeyId" in attributes: + raw_topic["KmsMasterKeyId"] = attributes["KmsMasterKeyId"] + return raw_topic['name'], raw_topic From 2a47dc627a7b5709163030ea9572e8e76aa7d5ac Mon Sep 17 00:00:00 2001 From: "Alessandro.Gonzalez" Date: Mon, 2 Nov 2020 12:14:48 +0000 Subject: [PATCH 003/193] Added new encrypted field to SNS topic on the UI --- .../data/html/partials/aws/services.sns.regions.id.topics.html | 1 + 1 file changed, 1 insertion(+) 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 08c907ef1..46f207b46 100755 --- 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 @@ -9,6 +9,7 @@

Information

Region: {{region}}
ARN: {{arn}}
Display name: {{DisplayName}}
+
Encrypted: {{#if KmsMasterKeyId}} True {{else}} False {{/if}}
{{#if Policy}}
From e2dda4b46257b6b219f01ca81fe1f7f6e0bcbaf0 Mon Sep 17 00:00:00 2001 From: "Alessandro.Gonzalez" Date: Mon, 2 Nov 2020 12:18:30 +0000 Subject: [PATCH 004/193] Add highlighting options for encrypted SNS topics --- .../data/html/partials/aws/services.sns.regions.id.topics.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 46f207b46..c549613af 100755 --- 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 @@ -9,7 +9,7 @@

Information

Region: {{region}}
ARN: {{arn}}
Display name: {{DisplayName}}
-
Encrypted: {{#if KmsMasterKeyId}} True {{else}} False {{/if}}
+
Encrypted: {{#if KmsMasterKeyId}} True {{else}} False {{/if}}
{{#if Policy}}
From 9278ce2fd1f51fc82028c4a21b8edf7fc803feb7 Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Tue, 10 Nov 2020 15:57:19 +0100 Subject: [PATCH 005/193] Fix: update functionality to include previous results --- ScoutSuite/__main__.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/ScoutSuite/__main__.py b/ScoutSuite/__main__.py index 2111a2a9b..f9a879973 100755 --- a/ScoutSuite/__main__.py +++ b/ScoutSuite/__main__.py @@ -273,11 +273,14 @@ async def _run(provider, if update: try: print_info('Updating existing data') - current_run_services = copy.deepcopy(cloud_provider.services) + #Load previous results last_run_dict = report.encoder.load_from_file('RESULTS') - cloud_provider.services = last_run_dict['services'] - for service in cloud_provider.service_list: - cloud_provider.services[service] = current_run_services[service] + #Get list of previous services which were not updated during this run + previous_services = [prev_service for prev_service in last_run_dict['service_list'] if prev_service not in cloud_provider.service_list] + #Add previous services + for service in previous_services: + cloud_provider.service_list.append(service) + cloud_provider.services[service] = last_run_dict['services'][service] except Exception as e: print_exception('Failure while updating report: {}'.format(e)) From 065eb57c84b57f6dc4ff07d3644870939377d34f Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Wed, 11 Nov 2020 11:25:20 +0100 Subject: [PATCH 006/193] Fix: S3 bucket CreationDate incorrectly retrieved --- ScoutSuite/providers/aws/facade/s3.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/ScoutSuite/providers/aws/facade/s3.py b/ScoutSuite/providers/aws/facade/s3.py index 924e513e8..2e4e75091 100755 --- a/ScoutSuite/providers/aws/facade/s3.py +++ b/ScoutSuite/providers/aws/facade/s3.py @@ -52,6 +52,8 @@ async def get_buckets(self): # Non-async post-processing for bucket in buckets: self._set_s3_bucket_secure_transport(bucket) + #Update CreationDate of all buckets with the correct values from 'us-east-1' + self._get_and_set_s3_bucket_creationdate(buckets) return buckets @@ -192,6 +194,19 @@ async def _get_and_set_s3_bucket_block_public_access(self, bucket: {}): except Exception as e: print_exception('Failed to get the public access block configuration for {}: {}'.format(bucket['Name'], e)) + def _get_and_set_s3_bucket_creationdate(self, buckets): + #When using region other than 'us-east-1', the 'CreationDate' is the last modified time according to bucket's last replication in the respective region + #Source: https://github.com/aws/aws-cli/issues/3597#issuecomment-424167129 + #Fixes issue #858 + client = AWSFacadeUtils.get_client('s3', self.session, 'us-east-1') + try: + buckets_useast1 = client.list_buckets()['Buckets'] + for bucket in buckets: + #Find the bucket with the same name and update 'CreationDate' from the 'us-east-1' region data, if doesn't exist keep the original value + bucket['CreationDate'] = next((buck['CreationDate'] for buck in buckets_useast1 if buck['Name'] == bucket['Name']), bucket['CreationDate']) + except Exception as e: + print_exception('Failed to list buckets using region "us-east-1"') + def _set_s3_bucket_secure_transport(self, bucket: {}): try: if 'policy' in bucket: From 1426777a29053ecb2f234240ff0ca396fa599e26 Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Wed, 11 Nov 2020 14:38:28 +0100 Subject: [PATCH 007/193] Added epilog to argument parser for clarity --- 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 234ccb7af..8c1496cf8 100755 --- a/ScoutSuite/core/cli_parser.py +++ b/ScoutSuite/core/cli_parser.py @@ -5,7 +5,7 @@ class ScoutSuiteArgumentParser: def __init__(self): - self.parser = argparse.ArgumentParser() + self.parser = argparse.ArgumentParser(epilog='To get addtional help on a specific provider run: scout.py {provider} -h') # People will still be able to use the old --provider syntax self.parser.add_argument("--provider", From 8d692ea2195e5eda5f5c0f8db7086639f5347bac Mon Sep 17 00:00:00 2001 From: xga Date: Fri, 13 Nov 2020 11:26:21 +0100 Subject: [PATCH 008/193] Minor changes --- ScoutSuite/providers/aws/facade/s3.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/ScoutSuite/providers/aws/facade/s3.py b/ScoutSuite/providers/aws/facade/s3.py index 2e4e75091..d82d57dc6 100755 --- a/ScoutSuite/providers/aws/facade/s3.py +++ b/ScoutSuite/providers/aws/facade/s3.py @@ -2,7 +2,7 @@ from botocore.exceptions import ClientError -from ScoutSuite.core.console import print_exception +from ScoutSuite.core.console import print_exception, print_debug 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 @@ -52,7 +52,7 @@ async def get_buckets(self): # Non-async post-processing for bucket in buckets: self._set_s3_bucket_secure_transport(bucket) - #Update CreationDate of all buckets with the correct values from 'us-east-1' + # Try to update CreationDate of all buckets with the correct values from 'us-east-1' self._get_and_set_s3_bucket_creationdate(buckets) return buckets @@ -195,17 +195,21 @@ async def _get_and_set_s3_bucket_block_public_access(self, bucket: {}): print_exception('Failed to get the public access block configuration for {}: {}'.format(bucket['Name'], e)) def _get_and_set_s3_bucket_creationdate(self, buckets): - #When using region other than 'us-east-1', the 'CreationDate' is the last modified time according to bucket's last replication in the respective region - #Source: https://github.com/aws/aws-cli/issues/3597#issuecomment-424167129 - #Fixes issue #858 + # When using region other than 'us-east-1', the 'CreationDate' is the last modified time according to bucket's + # last replication in the respective region + # Source: https://github.com/aws/aws-cli/issues/3597#issuecomment-424167129 + # Fixes issue https://github.com/nccgroup/ScoutSuite/issues/858 client = AWSFacadeUtils.get_client('s3', self.session, 'us-east-1') try: buckets_useast1 = client.list_buckets()['Buckets'] for bucket in buckets: - #Find the bucket with the same name and update 'CreationDate' from the 'us-east-1' region data, if doesn't exist keep the original value - bucket['CreationDate'] = next((buck['CreationDate'] for buck in buckets_useast1 if buck['Name'] == bucket['Name']), bucket['CreationDate']) + # Find the bucket with the same name and update 'CreationDate' from the 'us-east-1' region data, + # if doesn't exist keep the original value + bucket['CreationDate'] = next((b['CreationDate'] for b in buckets_useast1 if + b['Name'] == bucket['Name']), bucket['CreationDate']) except Exception as e: - print_exception('Failed to list buckets using region "us-east-1"') + # Only output exception when in debug mode + print_debug('Failed to get bucket creation date from "us-east-1" region') def _set_s3_bucket_secure_transport(self, bucket: {}): try: From 5d6587fab9bf188797ec01da85a8586bfe7eb881 Mon Sep 17 00:00:00 2001 From: xga Date: Fri, 13 Nov 2020 17:44:27 +0100 Subject: [PATCH 009/193] Add CloudFront --- ScoutSuite/output/data/inc-scoutsuite/scoutsuite.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ScoutSuite/output/data/inc-scoutsuite/scoutsuite.js b/ScoutSuite/output/data/inc-scoutsuite/scoutsuite.js index 2f8961f18..b1a1c15d6 100755 --- a/ScoutSuite/output/data/inc-scoutsuite/scoutsuite.js +++ b/ScoutSuite/output/data/inc-scoutsuite/scoutsuite.js @@ -1196,6 +1196,8 @@ function makeTitle(title) { return 'CloudWatch' } else if (title === 'cloudformation') { return 'CloudFormation' + } else if (title === 'cloudfront') { + return 'CloudFront' } else if (title === 'config') { return 'Config' } else if (title === 'cognito') { From a519d0f2296772e3051848ace0f98ef4f67983be Mon Sep 17 00:00:00 2001 From: xga Date: Fri, 13 Nov 2020 17:50:08 +0100 Subject: [PATCH 010/193] Handle empty distributions --- ScoutSuite/providers/aws/facade/cloudfront.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ScoutSuite/providers/aws/facade/cloudfront.py b/ScoutSuite/providers/aws/facade/cloudfront.py index 63fde3f01..093729114 100644 --- a/ScoutSuite/providers/aws/facade/cloudfront.py +++ b/ScoutSuite/providers/aws/facade/cloudfront.py @@ -6,6 +6,7 @@ from ScoutSuite.providers.utils import run_concurrently class CloudFront(AWSBaseFacade): + async def get_distributions(self): client = AWSFacadeUtils.get_client('cloudfront',self.session) # When no cloudfront distribution exists, we first need to initiate the creation @@ -29,7 +30,7 @@ async def get_distributions(self): return [] try: - return response['DistributionList']['Items'] + return response.get('DistributionList', {}).get('Items', []) except Exception as e: - print_exception('Failed to parse cloudfront distribution list: {}'.format(e)) + print_exception(f'Failed to get CloudFront distribution lists: {e}') return [] From 46f76cb7ab50e4f82d79caf8dd8f36b262ea5efa Mon Sep 17 00:00:00 2001 From: xga Date: Fri, 13 Nov 2020 17:50:18 +0100 Subject: [PATCH 011/193] Add resource --- ScoutSuite/providers/aws/metadata.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ScoutSuite/providers/aws/metadata.json b/ScoutSuite/providers/aws/metadata.json index aa7b97e9f..8b20233be 100755 --- a/ScoutSuite/providers/aws/metadata.json +++ b/ScoutSuite/providers/aws/metadata.json @@ -147,6 +147,14 @@ "path": "services.directconnect.connections" } } + }, + "cloudfront": { + "resources": { + "distributions": { + "cols": 2, + "path": "services.cloudfront.distributions" + } + } } }, "compute": { From 48a15084e016f876ae38284d38df990f48f03587 Mon Sep 17 00:00:00 2001 From: xga Date: Fri, 13 Nov 2020 17:56:03 +0100 Subject: [PATCH 012/193] Improve formatting and add HTML partial --- .../services.cloudfront.distributions.html | 32 +++++++++++++++++ .../aws/resources/cloudfront/distributions.py | 34 ++++++++++++------- 2 files changed, 53 insertions(+), 13 deletions(-) create mode 100644 ScoutSuite/output/data/html/partials/aws/services.cloudfront.distributions.html diff --git a/ScoutSuite/output/data/html/partials/aws/services.cloudfront.distributions.html b/ScoutSuite/output/data/html/partials/aws/services.cloudfront.distributions.html new file mode 100644 index 000000000..6acbe0aa2 --- /dev/null +++ b/ScoutSuite/output/data/html/partials/aws/services.cloudfront.distributions.html @@ -0,0 +1,32 @@ + + + + + + + + diff --git a/ScoutSuite/providers/aws/resources/cloudfront/distributions.py b/ScoutSuite/providers/aws/resources/cloudfront/distributions.py index c6f922455..fa387da67 100644 --- a/ScoutSuite/providers/aws/resources/cloudfront/distributions.py +++ b/ScoutSuite/providers/aws/resources/cloudfront/distributions.py @@ -10,18 +10,26 @@ async def fetch_all(self): id, distro = self._parse_distributions(distribution) self[id] = distro - def _parse_distributions(self, distribution): + def _parse_distributions(self, raw_distribution): distribution_dict = {} - distribution_dict['id'] = distribution.get('Id') - distribution_dict['arn'] = distribution.get('ARN') - distribution_dict['status'] = distribution.get('Status') - distribution_dict['last_modified_time'] = distribution.get('LastModifiedTime') - distribution_dict['aliases'] = distribution.get('Aliases') - distribution_dict['domain_name'] = distribution.get('DomainName') - distribution_dict['origins'] = distribution.get('Origins') - distribution_dict['viewer_protocol_policy'] = distribution.get('ViewerProtocolPolicy') - distribution_dict['allowed_methods'] = distribution.get('AllowedMethods') - distribution_dict['view_certificate'] = distribution.get('ViewerCertificate') - distribution_dict['restrictions'] = distribution.get('Restrictions') - distribution_dict['alias_icp_recordals'] = distribution.get('AliasICPRecordals') + distribution_dict['id'] = distribution_dict['name'] = raw_distribution.get('Id') + distribution_dict['arn'] = raw_distribution.get('ARN') + distribution_dict['aliases'] = raw_distribution.get('Aliases') + distribution_dict['status'] = raw_distribution.get('Status') + distribution_dict['cache_behaviors'] = raw_distribution.get('CacheBehaviors') + distribution_dict['restrictions'] = raw_distribution.get('Restrictions') + distribution_dict['origins'] = raw_distribution.get('Origins') + distribution_dict['domain_name'] = raw_distribution.get('DomainName') + distribution_dict['web_acl_id'] = raw_distribution.get('WebACLId') + distribution_dict['price_class'] = raw_distribution.get('PriceClass') + distribution_dict['enabled'] = raw_distribution.get('Enabled') + distribution_dict['default_cache_behavior'] = raw_distribution.get('DefaultCacheBehavior') + distribution_dict['is_ipv6_enabled'] = raw_distribution.get('IsIPV6Enabled') + distribution_dict['comment'] = raw_distribution.get('Comment') + distribution_dict['http_version'] = raw_distribution.get('HttpVersion') + distribution_dict['viewer_certificate'] = raw_distribution.get('ViewerCertificate') + distribution_dict['custom_error_responses'] = raw_distribution.get('CustomErrorResponses') + distribution_dict['last_modified_time'] = raw_distribution.get('LastModifiedTime') + distribution_dict['origin_groups'] = raw_distribution.get('OriginGroups') return distribution_dict['id'], distribution_dict + From 7c947415ef4a42272a27eb182fcf615138b5cdb6 Mon Sep 17 00:00:00 2001 From: xga Date: Mon, 16 Nov 2020 13:18:23 +0100 Subject: [PATCH 013/193] Fix bug in user data parsing --- ScoutSuite/providers/aws/facade/ec2.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/ScoutSuite/providers/aws/facade/ec2.py b/ScoutSuite/providers/aws/facade/ec2.py index a5a78fb90..d50fb9835 100755 --- a/ScoutSuite/providers/aws/facade/ec2.py +++ b/ScoutSuite/providers/aws/facade/ec2.py @@ -34,17 +34,21 @@ async def get_instance_user_data(self, region: str, instance_id: str): else: try: return await self._decode_user_data(user_data_response['UserData']['Value']) - except base64.binascii.Error as e: - return await self._decode_user_data(base64.b64decode(user_data_response['UserData']['Value'] + "===")) except Exception as e: print_exception(f'Unable to decode EC2 instance user data: {e}') async def _decode_user_data(self, user_data): - value = base64.b64decode(user_data) + try: + value = base64.b64decode(user_data) + except base64.binascii.Error as e: + value = base64.b64decode(f'{user_data}===') if value[0:2] == b'\x1f\x8b': # GZIP magic number return zlib.decompress(value, zlib.MAX_WBITS | 32).decode('utf-8') else: - return value.decode('utf-8') + try: + return value.decode('utf-8') + except UnicodeDecodeError: + return value.decode('latin-1') async def get_instances(self, region: str, vpc: str): filters = [{'Name': 'vpc-id', 'Values': [vpc]}] From bb8904bdee32217679cd2ea996b9e834fe639f00 Mon Sep 17 00:00:00 2001 From: xga Date: Mon, 16 Nov 2020 13:38:44 +0100 Subject: [PATCH 014/193] Improve user data decoding --- ScoutSuite/providers/aws/facade/ec2.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ScoutSuite/providers/aws/facade/ec2.py b/ScoutSuite/providers/aws/facade/ec2.py index d50fb9835..c265946c4 100755 --- a/ScoutSuite/providers/aws/facade/ec2.py +++ b/ScoutSuite/providers/aws/facade/ec2.py @@ -45,6 +45,12 @@ async def _decode_user_data(self, user_data): if value[0:2] == b'\x1f\x8b': # GZIP magic number return zlib.decompress(value, zlib.MAX_WBITS | 32).decode('utf-8') else: + # Try another run of b64 decoding + try: + value = base64.b64decode(value) + except Exception as e: + value = value + # Return a string, not a byte string try: return value.decode('utf-8') except UnicodeDecodeError: From e11aa880ea71d063a7865ec6962f2213ad8df974 Mon Sep 17 00:00:00 2001 From: xga Date: Tue, 17 Nov 2020 10:54:07 +0100 Subject: [PATCH 015/193] Add fetching and parsing for S3 encryption keys --- .../partials/aws/services.s3.buckets.html | 24 +++++++++++-------- ScoutSuite/providers/aws/facade/s3.py | 6 ++++- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/ScoutSuite/output/data/html/partials/aws/services.s3.buckets.html b/ScoutSuite/output/data/html/partials/aws/services.s3.buckets.html index 56537fa1d..b0d8fe3b6 100755 --- a/ScoutSuite/output/data/html/partials/aws/services.s3.buckets.html +++ b/ScoutSuite/output/data/html/partials/aws/services.s3.buckets.html @@ -5,16 +5,20 @@

{{name}}

-

Information

-
ARN: {{arn}}
-
Region: {{region}}
-
Creation date: {{CreationDate}}
-
Logging: {{has_logging? logging}}
-
Default encryption: {{ convert_bool_to_enabled default_encryption_enabled}}
-
Versioning: {{convert_bool_to_enabled versioning_status_enabled}}
-
MFA Delete: {{convert_bool_to_enabled version_mfa_delete_enabled}}
-
Secure transport: {{convert_bool_to_enabled secure_transport_enabled}}
-
Static website hosting: {{convert_bool_to_enabled web_hosting_enabled}}
+

Information

+
ARN: {{arn}}
+
Region: {{region}}
+
Creation Date: {{CreationDate}}
+
Logging: {{has_logging? logging}}
+
Default Encryption: {{ convert_bool_to_enabled default_encryption_enabled}}
+ {{#if default_encryption_enabled}} +
Encryption Algorithm: {{ value_or_none default_encryption_algorithm}}
+
Encryption Key: {{ value_or_none default_encryption_key}}
+ {{/if}} +
Versioning: {{convert_bool_to_enabled versioning_status_enabled}}
+
MFA Delete: {{convert_bool_to_enabled version_mfa_delete_enabled}}
+
Secure Transport: {{convert_bool_to_enabled secure_transport_enabled}}
+
Static Website Hosting: {{convert_bool_to_enabled web_hosting_enabled}}
{{> services.s3.public_access_block_configuration resource_type = 'bucket' resource_path = (concat 's3.buckets' @key)}}
diff --git a/ScoutSuite/providers/aws/facade/s3.py b/ScoutSuite/providers/aws/facade/s3.py index 1ea4e963b..b93cad5b4 100755 --- a/ScoutSuite/providers/aws/facade/s3.py +++ b/ScoutSuite/providers/aws/facade/s3.py @@ -117,8 +117,12 @@ async def _get_and_set_s3_bucket_default_encryption(self, bucket: {}): bucket_name = bucket['Name'] client = AWSFacadeUtils.get_client('s3', self.session, bucket['region']) try: - await run_concurrently(lambda: client.get_bucket_encryption(Bucket=bucket['Name'])) + config = await run_concurrently(lambda: client.get_bucket_encryption(Bucket=bucket['Name'])) bucket['default_encryption_enabled'] = True + bucket['default_encryption_algorithm'] = config.get('ServerSideEncryptionConfiguration', {})\ + .get('Rules', [{}])[0].get('ApplyServerSideEncryptionByDefault', {}).get('SSEAlgorithm') + bucket['default_encryption_key'] = config.get('ServerSideEncryptionConfiguration', {})\ + .get('Rules', [{}])[0].get('ApplyServerSideEncryptionByDefault', {}).get('KMSMasterKeyID') except ClientError as e: if 'ServerSideEncryptionConfigurationNotFoundError' in e.response['Error']['Code']: bucket['default_encryption_enabled'] = False From 9156e4c225508e62fd592fad4db2dc606ba47dcf Mon Sep 17 00:00:00 2001 From: xga Date: Tue, 17 Nov 2020 11:05:10 +0100 Subject: [PATCH 016/193] Add fetching and parsing for S3 encryption keys --- .../data/html/partials/aws/services.s3.buckets.html | 4 ++-- ScoutSuite/providers/aws/facade/s3.py | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/ScoutSuite/output/data/html/partials/aws/services.s3.buckets.html b/ScoutSuite/output/data/html/partials/aws/services.s3.buckets.html index b0d8fe3b6..48e2fbfed 100755 --- a/ScoutSuite/output/data/html/partials/aws/services.s3.buckets.html +++ b/ScoutSuite/output/data/html/partials/aws/services.s3.buckets.html @@ -12,8 +12,8 @@

Information

Logging: {{has_logging? logging}}
Default Encryption: {{ convert_bool_to_enabled default_encryption_enabled}}
{{#if default_encryption_enabled}} -
Encryption Algorithm: {{ value_or_none default_encryption_algorithm}}
-
Encryption Key: {{ value_or_none default_encryption_key}}
+
Encryption Algorithm: {{ value_or_none default_encryption_algorithm}}
+
Encryption Key: {{ value_or_none default_encryption_key}}
{{/if}}
Versioning: {{convert_bool_to_enabled versioning_status_enabled}}
MFA Delete: {{convert_bool_to_enabled version_mfa_delete_enabled}}
diff --git a/ScoutSuite/providers/aws/facade/s3.py b/ScoutSuite/providers/aws/facade/s3.py index b93cad5b4..82d3f0f94 100755 --- a/ScoutSuite/providers/aws/facade/s3.py +++ b/ScoutSuite/providers/aws/facade/s3.py @@ -126,12 +126,18 @@ async def _get_and_set_s3_bucket_default_encryption(self, bucket: {}): except ClientError as e: if 'ServerSideEncryptionConfigurationNotFoundError' in e.response['Error']['Code']: bucket['default_encryption_enabled'] = False + bucket['default_encryption_algorithm'] = None + bucket['default_encryption_key'] = None else: bucket['default_encryption_enabled'] = None + bucket['default_encryption_algorithm'] = None + bucket['default_encryption_key'] = None print_exception(f'Failed to get encryption configuration for {bucket_name}: {e}') except Exception as e: - print_exception(f'Failed to get encryption configuration for {bucket_name}: {e}') bucket['default_encryption'] = 'Unknown' + bucket['default_encryption_algorithm'] = None + bucket['default_encryption_key'] = None + print_exception(f'Failed to get encryption configuration for {bucket_name}: {e}') async def _get_and_set_s3_acls(self, bucket: {}, key_name=None): bucket_name = bucket['Name'] From f17d41ecb4b372a9d7cbf25043ee1ab3503fb77c Mon Sep 17 00:00:00 2001 From: xga Date: Tue, 17 Nov 2020 11:15:43 +0100 Subject: [PATCH 017/193] Minor change --- .../partials/aws/services.rds.regions.id.vpcs.id.instances.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ScoutSuite/output/data/html/partials/aws/services.rds.regions.id.vpcs.id.instances.html b/ScoutSuite/output/data/html/partials/aws/services.rds.regions.id.vpcs.id.instances.html index 2ec695bfd..6694e2ad0 100755 --- a/ScoutSuite/output/data/html/partials/aws/services.rds.regions.id.vpcs.id.instances.html +++ b/ScoutSuite/output/data/html/partials/aws/services.rds.regions.id.vpcs.id.instances.html @@ -12,7 +12,7 @@

Information

  • Engine: {{Engine}}
  • Created: {{format_date InstanceCreateTime}}
  • Status: {{makeTitle DBInstanceStatus}}
  • -
  • Is read replica: {{is_read_replica}}
  • +
  • Is Read Replica: {{is_read_replica}}
  • Auto Minor Version Upgrade: {{convert_bool_to_enabled AutoMinorVersionUpgrade}}
  • Multi Availability Zones: {{convert_bool_to_enabled MultiAZ}}
  • Instance Class: {{DBInstanceClass}}
  • From 9e9e3549202d61ab0706e68afe5c3e16845c8caa Mon Sep 17 00:00:00 2001 From: xga Date: Tue, 17 Nov 2020 11:18:30 +0100 Subject: [PATCH 018/193] Improve rules --- .../aws/rules/findings/rds-instance-backup-disabled.json | 4 ++-- .../providers/aws/rules/findings/rds-snapshot-public.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ScoutSuite/providers/aws/rules/findings/rds-instance-backup-disabled.json b/ScoutSuite/providers/aws/rules/findings/rds-instance-backup-disabled.json index 1188e891f..4fda05b1c 100755 --- a/ScoutSuite/providers/aws/rules/findings/rds-instance-backup-disabled.json +++ b/ScoutSuite/providers/aws/rules/findings/rds-instance-backup-disabled.json @@ -1,6 +1,6 @@ { - "description": "Backup Disabled", - "rationale": "Backups should be enabled to enable disaster recovery.", + "description": "Instance Backups Disabled", + "rationale": "The backup retention period is a period of time between 0 and 35 days for which you can perform a point-in-time restore. Setting the backup retention period to 0 disables automated backups. Backups should be enabled to enable disaster recovery.", "references": [ "https://aws.amazon.com/rds/details/backup/" ], diff --git a/ScoutSuite/providers/aws/rules/findings/rds-snapshot-public.json b/ScoutSuite/providers/aws/rules/findings/rds-snapshot-public.json index 559b84160..f9cec3fed 100755 --- a/ScoutSuite/providers/aws/rules/findings/rds-snapshot-public.json +++ b/ScoutSuite/providers/aws/rules/findings/rds-snapshot-public.json @@ -1,5 +1,5 @@ { - "description": "Publicly Accessible RDS Snapshot", + "description": "Publicly Accessible Snapshot", "rationale": "Snapshots should never be public, as this risks exposing sensitive data.", "references": [ "https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_ShareSnapshot.html" From 5378da7a3b2db012943312544a2e08b26e26377d Mon Sep 17 00:00:00 2001 From: xga Date: Tue, 17 Nov 2020 11:19:47 +0100 Subject: [PATCH 019/193] Improve rules --- .../aws/rules/findings/rds-instance-backup-disabled.json | 4 +++- .../findings/rds-instance-short-backup-retention-period.json | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ScoutSuite/providers/aws/rules/findings/rds-instance-backup-disabled.json b/ScoutSuite/providers/aws/rules/findings/rds-instance-backup-disabled.json index 4fda05b1c..7a47a8e59 100755 --- a/ScoutSuite/providers/aws/rules/findings/rds-instance-backup-disabled.json +++ b/ScoutSuite/providers/aws/rules/findings/rds-instance-backup-disabled.json @@ -2,7 +2,9 @@ "description": "Instance Backups Disabled", "rationale": "The backup retention period is a period of time between 0 and 35 days for which you can perform a point-in-time restore. Setting the backup retention period to 0 disables automated backups. Backups should be enabled to enable disaster recovery.", "references": [ - "https://aws.amazon.com/rds/details/backup/" + "https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_WorkingWithAutomatedBackups.html", + "https://aws.amazon.com/rds/details/backup/", + "https://aws.amazon.com/rds/faqs/" ], "dashboard_name": "Instances", "path": "rds.regions.id.vpcs.id.instances.id", diff --git a/ScoutSuite/providers/aws/rules/findings/rds-instance-short-backup-retention-period.json b/ScoutSuite/providers/aws/rules/findings/rds-instance-short-backup-retention-period.json index ca2fb73a7..651b71e6c 100755 --- a/ScoutSuite/providers/aws/rules/findings/rds-instance-short-backup-retention-period.json +++ b/ScoutSuite/providers/aws/rules/findings/rds-instance-short-backup-retention-period.json @@ -2,7 +2,9 @@ "description": "Short Backup Retention Period", "rationale": "The backup retention period is a period of time between 0 and 35 days for which you can perform a point-in-time restore. Setting the backup retention period to 0 disables automated backups.

    It is recommended that the retention period is set to at least 30 days. Having a short retention period will impact how far back in time the database can be restored to, and may affect integrity and availability of data.", "references": [ - "https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_WorkingWithAutomatedBackups.html" + "https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_WorkingWithAutomatedBackups.html", + "https://aws.amazon.com/rds/details/backup/", + "https://aws.amazon.com/rds/faqs/" ], "dashboard_name": "Instances", "path": "rds.regions.id.vpcs.id.instances.id", From 770b8e41e3919a8f206c61407e74d35976d0d181 Mon Sep 17 00:00:00 2001 From: xga Date: Tue, 17 Nov 2020 11:53:11 +0100 Subject: [PATCH 020/193] Add condition --- ScoutSuite/core/conditions.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/ScoutSuite/core/conditions.py b/ScoutSuite/core/conditions.py index c20f920e5..df9f7fbe2 100755 --- a/ScoutSuite/core/conditions.py +++ b/ScoutSuite/core/conditions.py @@ -277,6 +277,19 @@ def pass_condition(b, test, a): if c == a or re.match(r'arn:aws:iam:.*?:%s:.*' % a, c): result = True break + elif test == 'isAccountRoot': + result = False + if type(b) != list: + b = [b] + for c in b: + if type(c) == dict and 'AWS' in c: + c = c['AWS'] + if type(c) != list: + c = [c] + for i in c: + if i == a or re.match(r'arn:aws:iam:.*?:%s:root' % a, i): + result = True + break # Unknown test case else: From 935bf8948b8b4b20c5e72604ccdb7cb0d21ba74c Mon Sep 17 00:00:00 2001 From: xga Date: Tue, 17 Nov 2020 13:02:14 +0100 Subject: [PATCH 021/193] Improve formatting --- .../output/data/html/partials/aliyun/services.ram.users.html | 2 +- .../partials/aws/services.dynamodb.regions.id.tables.html | 2 +- .../html/partials/aws/services.ec2.regions.id.snapshots.html | 2 +- .../partials/aws/services.route53.regions.id.domains.html | 4 ++-- .../output/data/html/partials/azure/services.aad.groups.html | 4 ++-- .../html/partials/azure/services.aad.service_principals.html | 2 +- .../output/data/html/partials/azure/services.aad.users.html | 2 +- .../azure/services.appservice.subscriptions.id.web_apps.html | 4 ++-- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ScoutSuite/output/data/html/partials/aliyun/services.ram.users.html b/ScoutSuite/output/data/html/partials/aliyun/services.ram.users.html index 5cb940f65..4fcc0566b 100755 --- a/ScoutSuite/output/data/html/partials/aliyun/services.ram.users.html +++ b/ScoutSuite/output/data/html/partials/aliyun/services.ram.users.html @@ -14,7 +14,7 @@

    Information

    Email: {{value_or_none email}}
    Mobile Phone: {{value_or_none mobile_phone}}
    -
    Console Access: {{convert_bool_to_enabled console_access}}
    +
    Console Access: {{convert_bool_to_enabled console_access}}
    MFA Configured: {{mfa_status}}
    diff --git a/ScoutSuite/output/data/html/partials/aws/services.dynamodb.regions.id.tables.html b/ScoutSuite/output/data/html/partials/aws/services.dynamodb.regions.id.tables.html index d72f7efdb..13476efe4 100644 --- a/ScoutSuite/output/data/html/partials/aws/services.dynamodb.regions.id.tables.html +++ b/ScoutSuite/output/data/html/partials/aws/services.dynamodb.regions.id.tables.html @@ -10,7 +10,7 @@

    Information

    ARN: {{value_or_none arn}}
    Status: {{value_or_none table_status}}
    Creation Date: {{format_date creation_date_time}}
    -
    Automatic Backups: {{convert_bool_to_enabled automatic_backups_enabled}}
    +
    Automatic Backups: {{convert_bool_to_enabled automatic_backups_enabled}}
    Item Count: {{value_or_none item_count}}
    {{#if tags}} diff --git a/ScoutSuite/output/data/html/partials/aws/services.ec2.regions.id.snapshots.html b/ScoutSuite/output/data/html/partials/aws/services.ec2.regions.id.snapshots.html index eb5244ad4..6e7806719 100755 --- a/ScoutSuite/output/data/html/partials/aws/services.ec2.regions.id.snapshots.html +++ b/ScoutSuite/output/data/html/partials/aws/services.ec2.regions.id.snapshots.html @@ -15,7 +15,7 @@

    Information

    Start Time: {{value_or_none start_time}}
    Volume: {{> resource_link resource_path = (concat 'services.ec2.regions' region 'volumes' volume_id )}}
    Owner ID: {{value_or_none owner_id}}
    -
    Encryption: {{convert_bool_to_enabled encrypted}}
    +
    Encryption: {{convert_bool_to_enabled encrypted}}
    KMS Key ID: {{value_or_none kms_key_id}}
    diff --git a/ScoutSuite/output/data/html/partials/aws/services.route53.regions.id.domains.html b/ScoutSuite/output/data/html/partials/aws/services.route53.regions.id.domains.html index 4aec7945a..0819bb0c5 100755 --- a/ScoutSuite/output/data/html/partials/aws/services.route53.regions.id.domains.html +++ b/ScoutSuite/output/data/html/partials/aws/services.route53.regions.id.domains.html @@ -7,10 +7,10 @@

    {{name}}

    Information

    ARN: {{value_or_none arn}}
    Auto Renew: {{convert_bool_to_enabled auto_renew}} + id="route53.regions.id.domains.{{@key}}.auto_renew">{{convert_bool_to_enabled auto_renew}}
    Transfer Lock: - {{convert_bool_to_enabled transfer_lock}} + {{convert_bool_to_enabled transfer_lock}} This domain's top-level domain (TLD) does not support domain locking. diff --git a/ScoutSuite/output/data/html/partials/azure/services.aad.groups.html b/ScoutSuite/output/data/html/partials/azure/services.aad.groups.html index 713e4e579..df73bab39 100755 --- a/ScoutSuite/output/data/html/partials/azure/services.aad.groups.html +++ b/ScoutSuite/output/data/html/partials/azure/services.aad.groups.html @@ -9,9 +9,9 @@

    Information

    Name: {{value_or_none name}}
    Type: {{value_or_none object_type}}
    Mail Nickname: {{value_or_none mail_nickname}}
    -
    Mail Status: {{convert_bool_to_enabled mail_enabled}}
    +
    Mail Status: {{convert_bool_to_enabled mail_enabled}}
    Mail: {{value_or_none mail}}
    -
    Security Status: {{convert_bool_to_enabled security_enabled}}
    +
    Security Status: {{convert_bool_to_enabled security_enabled}}
    Deletion Timestamp: {{value_or_none deletion_timestamp}}
    diff --git a/ScoutSuite/output/data/html/partials/azure/services.aad.service_principals.html b/ScoutSuite/output/data/html/partials/azure/services.aad.service_principals.html index d737dafb3..5548e21dd 100755 --- a/ScoutSuite/output/data/html/partials/azure/services.aad.service_principals.html +++ b/ScoutSuite/output/data/html/partials/azure/services.aad.service_principals.html @@ -17,7 +17,7 @@

    Information

    None
    {{/each}}
    -
    Status: {{convert_bool_to_enabled account_enabled}}
    +
    Status: {{convert_bool_to_enabled account_enabled}}
    {{#if app_name}} {{else}} diff --git a/ScoutSuite/output/data/html/partials/azure/services.aad.users.html b/ScoutSuite/output/data/html/partials/azure/services.aad.users.html index 30cf1a4d5..971c765a4 100755 --- a/ScoutSuite/output/data/html/partials/azure/services.aad.users.html +++ b/ScoutSuite/output/data/html/partials/azure/services.aad.users.html @@ -14,7 +14,7 @@

    Information

    Mail: {{value_or_none mail}}
    Sign-In Names: {{value_or_none sign_in_names}}
    Type: {{value_or_none user_type}}
    -
    Status: {{convert_bool_to_enabled account_enabled}}
    +
    Status: {{convert_bool_to_enabled account_enabled}}
    Usage Location: {{value_or_none usage_location}}
    Deletion Timestamp: {{value_or_none deletion_timestamp}}
    diff --git a/ScoutSuite/output/data/html/partials/azure/services.appservice.subscriptions.id.web_apps.html b/ScoutSuite/output/data/html/partials/azure/services.appservice.subscriptions.id.web_apps.html index a28e17413..0e0d7fa06 100755 --- a/ScoutSuite/output/data/html/partials/azure/services.appservice.subscriptions.id.web_apps.html +++ b/ScoutSuite/output/data/html/partials/azure/services.appservice.subscriptions.id.web_apps.html @@ -29,12 +29,12 @@

    Information

    Configuration

    -
    Authentication: {{convert_bool_to_enabled authentication_enabled}}
    +
    Authentication: {{convert_bool_to_enabled authentication_enabled}}
    HTTPS-Only Traffic: {{convert_bool_to_enabled https_only}}
    HTTPS 2.0 Support: {{convert_bool_to_enabled http_2_enabled}}
    HTTP Logging: {{convert_bool_to_enabled http_logging_enabled}}
    Minimum TLS Version Supported: {{value_or_none minimum_tls_version_supported}}
    -
    Client Certificates: {{convert_bool_to_enabled client_cert_enabled}}
    +
    Client Certificates: {{convert_bool_to_enabled client_cert_enabled}}
    {{#if identity}}
    From cd8e1ed8f998f3f2eadefea72ad377f414854658 Mon Sep 17 00:00:00 2001 From: xga Date: Tue, 17 Nov 2020 13:04:56 +0100 Subject: [PATCH 022/193] Improve fetching and parsing --- ...ces.secretsmanager.regions.id.secrets.html | 12 ++++++ .../providers/aws/facade/secretsmanager.py | 43 ++++++++++++++++++- .../aws/resources/secretsmanager/secrets.py | 11 ++++- 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/ScoutSuite/output/data/html/partials/aws/services.secretsmanager.regions.id.secrets.html b/ScoutSuite/output/data/html/partials/aws/services.secretsmanager.regions.id.secrets.html index caab24cb8..c1864179c 100755 --- a/ScoutSuite/output/data/html/partials/aws/services.secretsmanager.regions.id.secrets.html +++ b/ScoutSuite/output/data/html/partials/aws/services.secretsmanager.regions.id.secrets.html @@ -9,7 +9,19 @@

    Information

    ARN: {{value_or_none arn}}
    Description: {{value_or_none description}}
    Last Changed Date: {{format_date last_changed_date}}
    +
    Last Accessed Date: {{format_date last_accessed_date}}
    +
    KMS Key: {{value_or_none kms}}
    +
    Rotation: {{convert_bool_to_enabled rotation}}
    + {{#if rotation}} +
    Rotation Lambda ARN: {{value_or_none rotation_lambda_arn}}
    +
    Rotation Interval: {{value_or_none rotation_interval}}
    + {{/if}}
    + {{#if policy}} +
    + {{> accordion_policy name = 'Resource Permissions ' policy_path = (concat 'secretsmanager.regions' region 'secrets' @key 'policy') document = policy}} +
    + {{/if}} diff --git a/ScoutSuite/providers/aws/rules/findings/cloudfront-distribution-cleartext-origin.json b/ScoutSuite/providers/aws/rules/findings/cloudfront-distribution-cleartext-origin.json new file mode 100644 index 000000000..b48cbf1d5 --- /dev/null +++ b/ScoutSuite/providers/aws/rules/findings/cloudfront-distribution-cleartext-origin.json @@ -0,0 +1,18 @@ +{ + "description": "Content Distribution with Clear-Text Origin TLS Policy", + "rationale": "Distributing content between AWS CloudFront distributions and their custom origins over clear-text HTTP, without using AWS encryption solutions, can potentially expose sensitive data.", + "references": [ + "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-customoriginconfig.html" + ], + "dashboard_name": "Distributions", + "path": "cloudfront.distributions.id", + "conditions": [ + "and", + [ + "cloudfront.distributions.id.origins", + "containString", + "http-only" + ] + ], + "class_suffix": "config_policy" +} diff --git a/ScoutSuite/providers/aws/rules/findings/cloudfront-distribution-insecure-origin.json b/ScoutSuite/providers/aws/rules/findings/cloudfront-distribution-insecure-origin.json index 271dbfcbc..088158113 100644 --- a/ScoutSuite/providers/aws/rules/findings/cloudfront-distribution-insecure-origin.json +++ b/ScoutSuite/providers/aws/rules/findings/cloudfront-distribution-insecure-origin.json @@ -1,17 +1,28 @@ { - "description": "CloudFront Insecure Content Distribution - Insecure Custom Origin Policy ", - "rationale": "Distributing insecure content between AWS CloudFront distributions and their custom origins, without using AWS encryption solutions. (Depends on the content data classification, this could be false-positive finding.)", + "description": "Content Distribution with Insecure Origin TLS Policy", + "rationale": "Distributing content between AWS CloudFront distributions and their custom origins over HTTPS using older SSL/TLS protocols can potentially expose sensitive data.", "references": [ "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-customoriginconfig.html" ], "dashboard_name": "Distributions", "path": "cloudfront.distributions.id", "conditions": [ - "and", + "or", [ "cloudfront.distributions.id.origins", "containString", "http-only" + ], + [ + "cloudfront.distributions.id.view_certificate.MinimumProtocolVersion.", + "containNoneOf", + [ + "TLSv1.1", + "TLSv1.1_2016", + "TLSv1.2_2018", + "TLSv1.2_2019" + ] ] - ] + ], + "class_suffix": "config_protocols" } diff --git a/ScoutSuite/providers/aws/rules/findings/cloudfront-distribution-insufficient-viewer-security.json b/ScoutSuite/providers/aws/rules/findings/cloudfront-distribution-insufficient-viewer-security.json index c00ea990f..df84d29fb 100644 --- a/ScoutSuite/providers/aws/rules/findings/cloudfront-distribution-insufficient-viewer-security.json +++ b/ScoutSuite/providers/aws/rules/findings/cloudfront-distribution-insufficient-viewer-security.json @@ -1,6 +1,6 @@ { - "description": "CloudFront Insecure Content Distribution - Insufficient Viewer Security Policy ", - "rationale": "Distributing insecure content to the Internet viewers (browsers), without using AWS encryption solutions; or using an encyption standard prior to TLSv1.1. So that the content data may be easily sniffed when in transit. (Depends on the content data classification, this could be false-positive finding.)", + "description": "Content Distribution with Insufficient Viewer Security Policy", + "rationale": "Distributing content between AWS CloudFront distributions and their custom origins without the use of a valid certificate, can potentially expose sensitive data.", "references": [ "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-viewercertificate.html" ], @@ -11,16 +11,8 @@ [ "this", "withoutKey", - "view_certificate" ], - [ - "cloudfront.distributions.id.view_certificate.MinimumProtocolVersion.", - "containNoneOf", - [ - "TLSv1.1", - "TLSv1.1_2016", - "TLSv1.2_2018", - "TLSv1.2_2019" - ] + "view_certificate" ] - ] + ], + "id_suffix": "certificate" } diff --git a/ScoutSuite/providers/aws/rules/rulesets/default.json b/ScoutSuite/providers/aws/rules/rulesets/default.json index 52c899e43..8142ee25b 100755 --- a/ScoutSuite/providers/aws/rules/rulesets/default.json +++ b/ScoutSuite/providers/aws/rules/rulesets/default.json @@ -1,6 +1,25 @@ { "about": "This ruleset consists of numerous rules that are considered standard by NCC Group. The rules enabled range from violations of well-known security best practices to gaps resulting from less-known security implications of provider-specific mechanisms. Additional rules exist, some of them requiring extra-parameters to be configured, and some of them being applicable to a limited number of users.", "rules": { + "cloudfront-distribution-cleartext-origin.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "cloudfront-distribution-insecure-origin.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "cloudfront-distribution-insufficient-viewer-security.json": [ + { + "enabled": true, + "level": "warning" + } + + ], "acm-certificate-with-close-expiration-date.json": [ { "args": [ From c2c38459d2309321eea69592d2a8ab6ca538a912 Mon Sep 17 00:00:00 2001 From: xga Date: Tue, 29 Dec 2020 15:33:44 +0100 Subject: [PATCH 039/193] Add findings and sort rulesets --- .../providers/aws/rules/rulesets/default.json | 23 +++++++++---------- .../aws/rules/rulesets/detailed.json | 18 +++++++++++++++ 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/ScoutSuite/providers/aws/rules/rulesets/default.json b/ScoutSuite/providers/aws/rules/rulesets/default.json index 8142ee25b..af10954c6 100755 --- a/ScoutSuite/providers/aws/rules/rulesets/default.json +++ b/ScoutSuite/providers/aws/rules/rulesets/default.json @@ -1,44 +1,43 @@ { "about": "This ruleset consists of numerous rules that are considered standard by NCC Group. The rules enabled range from violations of well-known security best practices to gaps resulting from less-known security implications of provider-specific mechanisms. Additional rules exist, some of them requiring extra-parameters to be configured, and some of them being applicable to a limited number of users.", "rules": { - "cloudfront-distribution-cleartext-origin.json": [ + "acm-certificate-with-close-expiration-date.json": [ { + "args": [ + "7" + ], "enabled": true, "level": "warning" } ], - "cloudfront-distribution-insecure-origin.json": [ + "acm-certificate-with-transparency-logging-disabled.json": [ { "enabled": true, "level": "warning" } ], - "cloudfront-distribution-insufficient-viewer-security.json": [ + "cloudformation-stack-with-role.json": [ { "enabled": true, - "level": "warning" + "level": "danger" } - ], - "acm-certificate-with-close-expiration-date.json": [ + "cloudfront-distribution-cleartext-origin.json": [ { - "args": [ - "7" - ], "enabled": true, "level": "warning" } ], - "acm-certificate-with-transparency-logging-disabled.json": [ + "cloudfront-distribution-insecure-origin.json": [ { "enabled": true, "level": "warning" } ], - "cloudformation-stack-with-role.json": [ + "cloudfront-distribution-insufficient-viewer-security.json": [ { "enabled": true, - "level": "danger" + "level": "warning" } ], "cloudtrail-duplicated-global-services-logging.json": [ diff --git a/ScoutSuite/providers/aws/rules/rulesets/detailed.json b/ScoutSuite/providers/aws/rules/rulesets/detailed.json index 14f857177..d396afed5 100755 --- a/ScoutSuite/providers/aws/rules/rulesets/detailed.json +++ b/ScoutSuite/providers/aws/rules/rulesets/detailed.json @@ -22,6 +22,24 @@ "level": "danger" } ], + "cloudfront-distribution-cleartext-origin.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "cloudfront-distribution-insecure-origin.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "cloudfront-distribution-insufficient-viewer-security.json": [ + { + "enabled": true, + "level": "warning" + } + ], "cloudtrail-duplicated-global-services-logging.json": [ { "enabled": true, From 088b30386a4d1b689618b120419603c874947dc2 Mon Sep 17 00:00:00 2001 From: Jason Ross Date: Thu, 21 Jan 2021 07:06:03 -0500 Subject: [PATCH 040/193] updated tooling to current revs. pulling in current scout image also --- docker/Dockerfile | 42 +++++++++++-------- docker/bin/container-install-aws2.sh | 32 +++++++------- docker/bin/container-install-azure.sh | 5 +-- docker/bin/container-install-gcp.sh | 13 +++--- ...tional.sh => container-install-prereqs.sh} | 18 +++----- docker/bin/container-install-scoutsuite.sh | 4 -- docker/bin/container-set-init.sh | 7 ++++ docker/bin/container-set-motd.sh | 6 --- docker/build.sh | 29 ++++++------- docker/config/build.env | 8 +++- docker/docker-compose.yaml | 19 +++++---- 11 files changed, 92 insertions(+), 91 deletions(-) rename docker/bin/{container-install-additional.sh => container-install-prereqs.sh} (64%) create mode 100755 docker/bin/container-set-init.sh delete mode 100755 docker/bin/container-set-motd.sh diff --git a/docker/Dockerfile b/docker/Dockerfile index 53f5e86b3..9c49bcc40 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -2,33 +2,36 @@ FROM python:3.8 LABEL maintainer="Jason Ross " -ARG VCS_REF -ARG VCS_URL -ARG VERSION ARG BUILD_DATE -ARG VENDOR ARG NAME ARG DESCRIPTION +ARG VCS_REF +ARG VCS_URL +ARG VENDOR +ARG VERSION +ARG IMAGE_NAME + +ENV DEBIAN_FRONTEND=${DEBIAN_FRONTEND} +ENV TERM=${TERM} +ENV IBMCLOUD_COLOR=${IBMCLOUD_COLOR} # Build-time metadata as defined at http://label-schema.org LABEL \ org.label-schema.schema-version="1.0" \ - org.label-schema.build-date=$BUILD_DATE \ - org.label-schema.name=$NAME \ - org.label-schema.description=$DESCRIPTION \ - org.label-schema.vcs-ref=$VCS_REF \ - org.label-schema.vcs-url=$VCS_URL \ - org.label-schema.vendor=$VENDOR \ - org.label-schema.version=$VERSION + org.label-schema.build-date="${BUILD_DATE}" \ + org.label-schema.name="${NAME}" \ + org.label-schema.description="${DESCRIPTION}" \ + org.label-schema.vcs-ref="${VCS_REF}" \ + org.label-schema.vcs-url="${VCS_URL}" \ + org.label-schema.vendor="${VENDOR}" \ + org.label-schema.version="${VERSION}" \ + org.label.image-name="${IMAGE_NAME}" # Copy helper scripts to container -COPY bin /root/bin - -# Install any additional software -RUN ["/bin/bash", "-c", "/root/bin/container-install-additional.sh"] +ADD bin /root/bin -# Set a nice message -RUN ["/bin/bash", "-c", "/root/bin/container-set-motd.sh"] +# Install required software +RUN ["/bin/bash", "-c", "/root/bin/container-install-prereqs.sh"] # Install AWS CLI RUN ["/bin/bash", "-c", "/root/bin/container-install-aws2.sh"] @@ -42,8 +45,11 @@ RUN ["/bin/bash", "-c", "/root/bin/container-install-gcp.sh"] # Install ScoutSuite RUN ["/bin/bash", "-c", "/root/bin/container-install-scoutsuite.sh"] +# Set a nice message +RUN ["/bin/bash", "-c", "/root/bin/container-set-init.sh"] + # Remove scripts RUN ["rm", "-rf", "/root/bin"] # Command -CMD ["/bin/bash"] \ No newline at end of file +CMD ["/bin/bash"] diff --git a/docker/bin/container-install-aws2.sh b/docker/bin/container-install-aws2.sh index 253a84c2b..d8f9e521b 100755 --- a/docker/bin/container-install-aws2.sh +++ b/docker/bin/container-install-aws2.sh @@ -1,12 +1,9 @@ #!/bin/bash +export DEBIAN_FRONTEND=noninteractive # ===================================== -# container-scoutsuite-install.sh -# ===================================== -# AUTHOR: jason.ross@nccgroup.com -# VERSION: 0.1.0 +# install the AWS CLI Tools # ===================================== -export DEBIAN_FRONTEND=noninteractive WORKDIR=/root TMPDIR=/tmp @@ -35,16 +32,23 @@ rm -rf ${TMPDIR}/aws # if the aws config directory already exists # then we do nothing and leave it alone if [ ! -d ${AWSDIR} ]; then - mkdir ${AWSDIR} - - # create the config template - cat <<'EOF' >${AWSDIR}/config - [default] - region = us-east-1 - output = json - aws_access_key_id = - aws_secret_access_key = +mkdir ${AWSDIR} + +# create the config template +cat <<'EOF' >${AWSDIR}/config +[default] +region = us-east-1 +output = json EOF + +# create the credentials template +cat <<'EOF' >${AWSDIR}/credentials +[default] +aws_access_key_id = +aws_secret_access_key = +EOF + fi + echo -e "\n\nAWS2 CLI Installation Complete!\n\n" diff --git a/docker/bin/container-install-azure.sh b/docker/bin/container-install-azure.sh index 81057ae09..540308835 100755 --- a/docker/bin/container-install-azure.sh +++ b/docker/bin/container-install-azure.sh @@ -1,12 +1,9 @@ #!/bin/bash +export DEBIAN_FRONTEND=noninteractive # ===================================== # install the Azure CLI Tools # ===================================== -# AUTHOR: jason.ross@nccgroup.com -# VERSION: 0.1.0 -# ===================================== -export DEBIAN_FRONTEND=noninteractive WORKDIR=/root TMPDIR=/tmp diff --git a/docker/bin/container-install-gcp.sh b/docker/bin/container-install-gcp.sh index fd602e1fb..2d03a2418 100755 --- a/docker/bin/container-install-gcp.sh +++ b/docker/bin/container-install-gcp.sh @@ -1,12 +1,9 @@ #!/bin/bash +export DEBIAN_FRONTEND=noninteractive # ===================================== # install gCloud SDK CLI Tools # ===================================== -# AUTHOR: jason.ross@nccgroup.com -# VERSION: 0.1.0 -# ===================================== -export DEBIAN_FRONTEND=noninteractive WORKDIR=/root TMPDIR=/tmp @@ -15,13 +12,13 @@ cd ${TMPDIR} echo -e "\n\ngCloud SDK Installation Starting...\n\n" # add the gcp repo to apt -echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" > /etc/apt/sources.list.d/google-cloud-sdk.list +echo "deb [signed-by=/etc/apt/trusted.gpg.d/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" > /etc/apt/sources.list.d/google-cloud-sdk.list # add the gcp pubkey to apt -curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key --keyring /usr/share/keyrings/cloud.google.gpg add - +curl https://packages.cloud.google.com./apt/doc/apt-key.gpg > /etc/apt/trusted.gpg.d/cloud.google.gpg -# install the sdk + some extra python-related bits -apt-get update && apt-get install -y google-cloud-sdk google-cloud-sdk-app-engine-python google-cloud-sdk-app-engine-python-extras +# install the sdk + kubectl + some extra python-related bits +apt-get update && apt-get install -y google-cloud-sdk google-cloud-sdk-app-engine-python google-cloud-sdk-app-engine-python-extras kubectl # let folks know the install is done echo -e "\n\ngCloud SDK Installation Complete!\n\n" diff --git a/docker/bin/container-install-additional.sh b/docker/bin/container-install-prereqs.sh similarity index 64% rename from docker/bin/container-install-additional.sh rename to docker/bin/container-install-prereqs.sh index e603e0a15..af4173210 100755 --- a/docker/bin/container-install-additional.sh +++ b/docker/bin/container-install-prereqs.sh @@ -1,13 +1,10 @@ #!/bin/bash +export DEBIAN_FRONTEND=noninteractive # ===================================== # install software packages needed for # all the other components to run # ===================================== -# AUTHOR: jason.ross@nccgroup.com -# VERSION: 0.1.0 -# ===================================== -export DEBIAN_FRONTEND=noninteractive WORKDIR=/root TMPDIR=/tmp @@ -15,11 +12,6 @@ cd ${TMPDIR} echo -e "\n\nSoftware Pre-reqs Installation Starting...\n\n" -# ===================================== -# make sure the timezone gets set to UTC -# ===================================== -ln -fs /usr/share/zoneinfo/Etc/UTC /etc/localtime - # ===================================== # set up the pre-reqs # ===================================== @@ -33,17 +25,17 @@ apt-get install -qy \ dialog \ gnupg \ groff \ + jq \ less \ lsb-release \ nano \ python3 \ python3-pip \ + tzdata \ unzip \ vim \ virtualenv \ - virtualenvwrapper - -# reconfigure the tzdata package to make sure it picks up the UTC bit -dpkg-reconfigure --frontend noninteractive tzdata + virtualenvwrapper \ + wget echo -e "\n\nSoftware Pre-reqs Installation Complete!\n\n" diff --git a/docker/bin/container-install-scoutsuite.sh b/docker/bin/container-install-scoutsuite.sh index 2ebe85785..cfe5311f1 100755 --- a/docker/bin/container-install-scoutsuite.sh +++ b/docker/bin/container-install-scoutsuite.sh @@ -3,10 +3,6 @@ # ===================================== # install ScoutSuite into a virtual env # ===================================== -# AUTHOR: jason.ross@nccgroup.com -# VERSION: 0.1.0 -# ===================================== -export DEBIAN_FRONTEND=noninteractive WORKDIR=/root TMPDIR=/tmp diff --git a/docker/bin/container-set-init.sh b/docker/bin/container-set-init.sh new file mode 100755 index 000000000..7f89131c7 --- /dev/null +++ b/docker/bin/container-set-init.sh @@ -0,0 +1,7 @@ +#!/bin/bash +cat <<'EOF' >> /root/.bashrc +export TERM=linux +cd ${HOME} +source ${HOME}/scoutsuite/bin/activate +echo -e "Welcome to Sscoutsuite!\nYou are already in the Scoutsuite virtual environment, so just type \`scout\` to run it!\n (for example: \`scout -h\` to see the help documentation).\n\nHave fun!\n\n" +EOF diff --git a/docker/bin/container-set-motd.sh b/docker/bin/container-set-motd.sh deleted file mode 100755 index 5ed02d14a..000000000 --- a/docker/bin/container-set-motd.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -cat <<'EOF' >> /root/.bashrc -cd ${HOME} -source ${HOME}/scoutsuite/bin/activate -echo -e "Welcome to ScoutSuite!\nTo run ScoutSuite, just type \`scout -h\` to see the help documentation.\nHave fun!\n\n" -EOF \ No newline at end of file diff --git a/docker/build.sh b/docker/build.sh index fd7a488ba..1ef82ac22 100755 --- a/docker/build.sh +++ b/docker/build.sh @@ -1,19 +1,20 @@ #!/bin/bash echo -e "\n\nbuild running...\n" -source ../config/build.env +source ./config/build.env BUILD_CMD="docker build \ - -t ${IMAGE_NAME} \ - -t ${VENDOR}/${NAME}:${VERSION} \ - --build-arg VCS_REF=${VCS_REF} \ - --build-arg VCS_URL=${VCS_URL} \ - --build-arg VERSION=${VERSION} \ - --build-arg BUILD_DATE=${BUILD_DATE} \ - --build-arg NAME=${NAME} \ - --build-arg VENDOR=${VENDOR} \ - --build-arg IMAGE_NAME=${IMAGE_NAME} \ - ." - # --build-arg DESCRIPTION=${DESCRIPTION} \ - +-t ${IMAGE_NAME} \ +-t ${IMAGE_NAME} \ +--build-arg BUILD_DATE=${BUILD_DATE} \ +--build-arg NAME=${NAME} \ +--build-arg VCS_REF=${VCS_REF} \ +--build-arg VCS_URL=${VCS_URL} \ +--build-arg VENDOR=${VENDOR} \ +--build-arg VERSION=${VERSION} \ +--build-arg IMAGE_NAME=${IMAGE_NAME} \ +." +# wtf. idk why this doesn't work +# --build-arg DESCRIPTION=\"${DESCRIPTION}\" \ + echo -e "\n\nbuilding image using:\n${BUILD_CMD}" -exec ${BUILD_CMD} \ No newline at end of file +exec ${BUILD_CMD} diff --git a/docker/config/build.env b/docker/config/build.env index e2175e243..cd22fe07a 100644 --- a/docker/config/build.env +++ b/docker/config/build.env @@ -1,9 +1,13 @@ VCS_REF=$(git rev-parse --short HEAD) VCS_URL='https://github.com/nccgroup/ScoutSuite' -VERSION='0.2.2' +VERSION='0.3.0' BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") VENDOR='nccgroup' NAME='scoutsuite' DESCRIPTION='A ready-to-go NCC Group ScoutSuite container based on Ubuntu.' IMAGE_NAME="${VENDOR}/${NAME}:${VERSION}" -MICROSCANNER_TOKEN="" + +# These are passed in as env vars to the container at runtime +IBMCLOUD_COLOR=true +DEBIAN_FRONTEND=noninteractive +TERM=linux \ No newline at end of file diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index cf79659e8..bf68f4814 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -1,4 +1,4 @@ -version: "3.7" +version: "3.8" services: ncc-scoutsuite: image: scoutsuite:latest @@ -8,10 +8,13 @@ services: context: . dockerfile: Dockerfile args: - - VCS_REF - - VCS_URL - - VERSION - - BUILD_DATE - - VENDOR - - NAME - - DESCRIPTION + - VCS_REF=${VCS_REF} + - VCS_URL=${VCS_URL} + - VERSION=${VERSION} + - BUILD_DATE=${BUILD_DATE} + - VENDOR=${VENDOR} + - NAME=${NAME} + - IMAGE_NAME=${IMAGE_NAME} + - DESCRIPTION=${DESCRIPTION} + env_file: + - config/build.env \ No newline at end of file From 56d071b7e44786d8f70f653c78f8bb6eefc4879e Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Mon, 25 Jan 2021 12:25:36 +0100 Subject: [PATCH 041/193] Fixed bug where returned path was incorrect after sanitizing --- ScoutSuite/output/data/inc-scoutsuite/scoutsuite.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ScoutSuite/output/data/inc-scoutsuite/scoutsuite.js b/ScoutSuite/output/data/inc-scoutsuite/scoutsuite.js index cdcc4c386..7d6a8258a 100755 --- a/ScoutSuite/output/data/inc-scoutsuite/scoutsuite.js +++ b/ScoutSuite/output/data/inc-scoutsuite/scoutsuite.js @@ -1004,7 +1004,7 @@ function updateTitle(title) { * Updates the Document Object Model */ function showPageFromHash() { - myhash = location.hash.replace(/[^a-z|0-9|.]/gi,'') + myhash = location.hash.replace(/[^a-z|0-9|.#-]/gi,'') if (myhash) { updateDOM(myhash) } else { From c99e8637383f651e7125332b4cb54b69dd79a09d Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Tue, 26 Jan 2021 11:29:34 +0100 Subject: [PATCH 042/193] Update azure-mgmt-compute to 12.0.0 for accurate disk encryption reporting --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c761c03e6..659723227 100755 --- a/requirements.txt +++ b/requirements.txt @@ -45,7 +45,7 @@ azure-mgmt-keyvault==1.1.0 azure-mgmt-network==2.5.1 azure-mgmt-redis==6.0.0 azure-mgmt-web==0.47.0 -azure-mgmt-compute==5.0.0 +azure-mgmt-compute==12.0.0 azure-mgmt-authorization==0.60.0 # Aliyun / Alibaba Cloud Provider From 0c3d993605a1b0610f0545a9c2edcab9569d9296 Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Tue, 26 Jan 2021 18:29:42 +0100 Subject: [PATCH 043/193] Fixed processing for: - Additional Capabilities - Plan - Zones - Hardware Profile - Diagnostics Profile - OS Profile - Storage Profile Added display information for: - Hardware Profile - Diagnostics Profile - OS Profile - Storage Profile --- ...almachines.subscriptions.id.instances.html | 45 +++++++++-- .../resources/virtualmachines/instances.py | 74 ++++++++++++++++--- 2 files changed, 103 insertions(+), 16 deletions(-) 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 ce32ba05a..014d704da 100755 --- 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 @@ -8,15 +8,50 @@

    Information

    Name: {{value_or_none name}}
    VM ID: {{value_or_none vm_id}}
    Location: {{value_or_none location}}
    +
    Zones: {{value_or_none zones}}
    +
    Proximity Placement Group: {{value_or_none proximity_placement_group}}
    +
    Availability Set: {{value_or_none availability_set}}
    Provisioning State: {{value_or_none provisioning_state}}
    Identity Principal ID: {{value_or_none identity.principal_id}}
    License Type: {{value_or_none license_type}}
    Plan: {{value_or_none plan}}
    -
    Zones: {{value_or_none zones}}
    -
    Instance View: {{value_or_none instance_view}}
    -
    Proximity Placement Group: {{value_or_none proximity_placement_group}}
    -
    Availability Set: {{value_or_none availability_set}}
    -
    Additional Capabilities: {{value_or_none additional_capabilities}}
    +
    Hardware Profile: {{value_or_none hardware_profile}}
    +
    Diagnostics Profile: + {{#each diagnostics_profile}} +
      +
    • {{@key}}: {{value_or_none this}}
    • +
    + {{else}} +
    None
    + {{/each}} +
    +
    OS Profile: + {{#each os_profile}} +
      +
    • {{@key}}: {{value_or_none this}}
    • +
    + {{else}} +
    None
    + {{/each}} +
    +
    Storage Profile: + {{#each storage_profile}} +
      +
    • {{@key}}: {{value_or_none this}}
    • +
    + {{else}} +
    None
    + {{/each}} +
    +
    Additional Capabilities: + {{#each additional_capabilities}} +
      +
    • {{this}}
    • +
    + {{else}} +
    None
    + {{/each}} +
    Tags: {{#each tags}}
    Date: Wed, 27 Jan 2021 11:09:03 +0100 Subject: [PATCH 044/193] Removed unused import that was added for testing --- .../providers/azure/resources/virtualmachines/instances.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/ScoutSuite/providers/azure/resources/virtualmachines/instances.py b/ScoutSuite/providers/azure/resources/virtualmachines/instances.py index 42085967f..34c5a9e93 100755 --- a/ScoutSuite/providers/azure/resources/virtualmachines/instances.py +++ b/ScoutSuite/providers/azure/resources/virtualmachines/instances.py @@ -5,8 +5,6 @@ from ScoutSuite.providers.azure.utils import get_resource_group_name -from pydoc import locate - class Instances(AzureResources): def __init__(self, facade: AzureFacade, subscription_id: str): From 3320c0c711db058a4636d063bb12c30ba811466f Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Wed, 27 Jan 2021 13:34:10 +0100 Subject: [PATCH 045/193] Added best practices references to IAM findings --- .../rules/findings/iam-password-policy-minimum-length.json | 4 +++- .../findings/iam-password-policy-no-lowercase-required.json | 4 +++- .../findings/iam-password-policy-no-number-required.json | 4 +++- .../findings/iam-password-policy-no-symbol-required.json | 4 +++- .../findings/iam-password-policy-no-uppercase-required.json | 4 +++- .../rules/findings/iam-root-account-no-hardware-mfa.json | 3 ++- .../aws/rules/findings/iam-root-account-no-mfa.json | 3 ++- .../aws/rules/findings/iam-root-account-used-recently.json | 6 +++++- .../rules/findings/iam-root-account-with-active-certs.json | 3 ++- .../rules/findings/iam-root-account-with-active-keys.json | 6 +++++- .../providers/aws/rules/findings/iam-user-without-mfa.json | 3 ++- 11 files changed, 33 insertions(+), 11 deletions(-) diff --git a/ScoutSuite/providers/aws/rules/findings/iam-password-policy-minimum-length.json b/ScoutSuite/providers/aws/rules/findings/iam-password-policy-minimum-length.json index df12565c0..06213d207 100755 --- a/ScoutSuite/providers/aws/rules/findings/iam-password-policy-minimum-length.json +++ b/ScoutSuite/providers/aws/rules/findings/iam-password-policy-minimum-length.json @@ -20,7 +20,9 @@ } ], "references": [ - "https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cis-controls.html#securityhub-cis-controls-1.9" + "https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cis-controls.html#securityhub-cis-controls-1.9", + "https://docs.aws.amazon.com/organizations/latest/userguide/orgs_best-practices_mgmt-acct.html#best-practices_mgmt-acct_complex-password", + "https://docs.aws.amazon.com/organizations/latest/userguide/best-practices_member-acct.html#best-practices_mbr-acct_complex-password" ], "dashboard_name": "Password policy", "path": "iam.password_policy.MinimumPasswordLength", diff --git a/ScoutSuite/providers/aws/rules/findings/iam-password-policy-no-lowercase-required.json b/ScoutSuite/providers/aws/rules/findings/iam-password-policy-no-lowercase-required.json index 6a7507a67..6c18e3f93 100755 --- a/ScoutSuite/providers/aws/rules/findings/iam-password-policy-no-lowercase-required.json +++ b/ScoutSuite/providers/aws/rules/findings/iam-password-policy-no-lowercase-required.json @@ -20,7 +20,9 @@ } ], "references": [ - "https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cis-controls.html#securityhub-cis-controls-1.6" + "https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cis-controls.html#securityhub-cis-controls-1.6", + "https://docs.aws.amazon.com/organizations/latest/userguide/orgs_best-practices_mgmt-acct.html#best-practices_mgmt-acct_complex-password", + "https://docs.aws.amazon.com/organizations/latest/userguide/best-practices_member-acct.html#best-practices_mbr-acct_complex-password" ], "dashboard_name": "Password policy", "path": "iam.password_policy.RequireLowercaseCharacters", diff --git a/ScoutSuite/providers/aws/rules/findings/iam-password-policy-no-number-required.json b/ScoutSuite/providers/aws/rules/findings/iam-password-policy-no-number-required.json index 048d63bbe..8d4619299 100755 --- a/ScoutSuite/providers/aws/rules/findings/iam-password-policy-no-number-required.json +++ b/ScoutSuite/providers/aws/rules/findings/iam-password-policy-no-number-required.json @@ -20,7 +20,9 @@ } ], "references": [ - "https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cis-controls.html#securityhub-cis-controls-1.8" + "https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cis-controls.html#securityhub-cis-controls-1.8", + "https://docs.aws.amazon.com/organizations/latest/userguide/orgs_best-practices_mgmt-acct.html#best-practices_mgmt-acct_complex-password", + "https://docs.aws.amazon.com/organizations/latest/userguide/best-practices_member-acct.html#best-practices_mbr-acct_complex-password" ], "dashboard_name": "Password policy", "path": "iam.password_policy.RequireNumbers", diff --git a/ScoutSuite/providers/aws/rules/findings/iam-password-policy-no-symbol-required.json b/ScoutSuite/providers/aws/rules/findings/iam-password-policy-no-symbol-required.json index bcfd2abe5..721131414 100755 --- a/ScoutSuite/providers/aws/rules/findings/iam-password-policy-no-symbol-required.json +++ b/ScoutSuite/providers/aws/rules/findings/iam-password-policy-no-symbol-required.json @@ -20,7 +20,9 @@ } ], "references": [ - "https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cis-controls.html#securityhub-cis-controls-1.7" + "https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cis-controls.html#securityhub-cis-controls-1.7", + "https://docs.aws.amazon.com/organizations/latest/userguide/orgs_best-practices_mgmt-acct.html#best-practices_mgmt-acct_complex-password", + "https://docs.aws.amazon.com/organizations/latest/userguide/best-practices_member-acct.html#best-practices_mbr-acct_complex-password" ], "dashboard_name": "Password policy", "path": "iam.password_policy.RequireSymbols", diff --git a/ScoutSuite/providers/aws/rules/findings/iam-password-policy-no-uppercase-required.json b/ScoutSuite/providers/aws/rules/findings/iam-password-policy-no-uppercase-required.json index 9ca4e33f5..41c399ffb 100755 --- a/ScoutSuite/providers/aws/rules/findings/iam-password-policy-no-uppercase-required.json +++ b/ScoutSuite/providers/aws/rules/findings/iam-password-policy-no-uppercase-required.json @@ -20,7 +20,9 @@ } ], "references": [ - "https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cis-controls.html#securityhub-cis-controls-1.5" + "https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cis-controls.html#securityhub-cis-controls-1.5", + "https://docs.aws.amazon.com/organizations/latest/userguide/orgs_best-practices_mgmt-acct.html#best-practices_mgmt-acct_complex-password", + "https://docs.aws.amazon.com/organizations/latest/userguide/best-practices_member-acct.html#best-practices_mbr-acct_complex-password" ], "dashboard_name": "Password policy", "path": "iam.password_policy.RequireUppercaseCharacters", diff --git a/ScoutSuite/providers/aws/rules/findings/iam-root-account-no-hardware-mfa.json b/ScoutSuite/providers/aws/rules/findings/iam-root-account-no-hardware-mfa.json index a25ef5da6..c1a8fe3ce 100644 --- a/ScoutSuite/providers/aws/rules/findings/iam-root-account-no-hardware-mfa.json +++ b/ScoutSuite/providers/aws/rules/findings/iam-root-account-no-hardware-mfa.json @@ -20,7 +20,8 @@ } ], "references": [ - "https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cis-controls.html#securityhub-cis-controls-1.13" + "https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cis-controls.html#securityhub-cis-controls-1.13", + "https://docs.aws.amazon.com/organizations/latest/userguide/orgs_best-practices_mgmt-acct.html#best-practices_mgmt-acct_mfa" ], "dashboard_name": "Accounts", "path": "iam.credential_reports.id", diff --git a/ScoutSuite/providers/aws/rules/findings/iam-root-account-no-mfa.json b/ScoutSuite/providers/aws/rules/findings/iam-root-account-no-mfa.json index 6e0a4da8e..9d6da2c60 100755 --- a/ScoutSuite/providers/aws/rules/findings/iam-root-account-no-mfa.json +++ b/ScoutSuite/providers/aws/rules/findings/iam-root-account-no-mfa.json @@ -15,7 +15,8 @@ } ], "references": [ - "https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cis-controls.html#securityhub-cis-controls-1.13" + "https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cis-controls.html#securityhub-cis-controls-1.13", + "https://docs.aws.amazon.com/organizations/latest/userguide/orgs_best-practices_mgmt-acct.html#best-practices_mgmt-acct_mfa" ], "dashboard_name": "Accounts", "path": "iam.credential_reports.id", diff --git a/ScoutSuite/providers/aws/rules/findings/iam-root-account-used-recently.json b/ScoutSuite/providers/aws/rules/findings/iam-root-account-used-recently.json index d3400043f..8115f53d8 100755 --- a/ScoutSuite/providers/aws/rules/findings/iam-root-account-used-recently.json +++ b/ScoutSuite/providers/aws/rules/findings/iam-root-account-used-recently.json @@ -21,7 +21,11 @@ ], "references": [ "https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cis-controls.html#securityhub-standards-cis-controls-1.1", - "https://docs.aws.amazon.com/general/latest/gr/aws_tasks-that-require-root.html" + "https://docs.aws.amazon.com/general/latest/gr/aws_tasks-that-require-root.html", + "https://docs.aws.amazon.com/organizations/latest/userguide/orgs_best-practices_mgmt-acct.html#best-practices_mgmt-use", + "https://docs.aws.amazon.com/organizations/latest/userguide/orgs_best-practices_mgmt-acct.html#best-practices_mgmt-acct_review-access", + "https://docs.aws.amazon.com/organizations/latest/userguide/orgs_best-practices_mgmt-acct.html#best-practices_mgmt-acct_document-processes", + "https://docs.aws.amazon.com/organizations/latest/userguide/orgs_best-practices_mgmt-acct.html#best-practices_mgmt-acct_monitor-access" ], "dashboard_name": "Root account", "path": "iam.credential_reports.id", diff --git a/ScoutSuite/providers/aws/rules/findings/iam-root-account-with-active-certs.json b/ScoutSuite/providers/aws/rules/findings/iam-root-account-with-active-certs.json index df7a8ded5..1f637bbc0 100755 --- a/ScoutSuite/providers/aws/rules/findings/iam-root-account-with-active-certs.json +++ b/ScoutSuite/providers/aws/rules/findings/iam-root-account-with-active-certs.json @@ -2,7 +2,8 @@ "description": "Root Account Has Active X.509 Certs", "rationale": "Root account X.509 certificates should be deleted as they may be used to make SOAP-protocol requests in the context of the root account.", "references": [ - "https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cis-controls.html#securityhub-standards-cis-controls-1.1" + "https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cis-controls.html#securityhub-standards-cis-controls-1.1", + "https://docs.aws.amazon.com/organizations/latest/userguide/orgs_best-practices_mgmt-acct.html#best-practices_mgmt-use" ], "dashboard_name": "Root account", "path": "iam.credential_reports.id", diff --git a/ScoutSuite/providers/aws/rules/findings/iam-root-account-with-active-keys.json b/ScoutSuite/providers/aws/rules/findings/iam-root-account-with-active-keys.json index baf4df44e..763cd05d0 100755 --- a/ScoutSuite/providers/aws/rules/findings/iam-root-account-with-active-keys.json +++ b/ScoutSuite/providers/aws/rules/findings/iam-root-account-with-active-keys.json @@ -20,7 +20,11 @@ } ], "references": [ - "https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cis-controls.html#securityhub-standards-cis-controls-1.1" + "https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cis-controls.html#securityhub-standards-cis-controls-1.1", + "https://docs.aws.amazon.com/organizations/latest/userguide/orgs_best-practices_mgmt-acct.html#best-practices_mgmt-use", + "https://docs.aws.amazon.com/organizations/latest/userguide/orgs_best-practices_mgmt-acct.html#best-practices_mgmt-acct_review-access", + "https://docs.aws.amazon.com/organizations/latest/userguide/orgs_best-practices_mgmt-acct.html#best-practices_mgmt-acct_document-processes" + ], "dashboard_name": "Root account", "path": "iam.credential_reports.id", diff --git a/ScoutSuite/providers/aws/rules/findings/iam-user-without-mfa.json b/ScoutSuite/providers/aws/rules/findings/iam-user-without-mfa.json index 64e2c3bd6..e7333ce2f 100755 --- a/ScoutSuite/providers/aws/rules/findings/iam-user-without-mfa.json +++ b/ScoutSuite/providers/aws/rules/findings/iam-user-without-mfa.json @@ -20,7 +20,8 @@ } ], "references": [ - "https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#enable-mfa-for-privileged-users" + "https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#enable-mfa-for-privileged-users", + "https://docs.aws.amazon.com/organizations/latest/userguide/best-practices_member-acct.html#best-practices_mbr-acct_mfa" ], "dashboard_name": "Users", "path": "iam.users.id", From e5f0e8ccc02d249067f02f1e05e1c4433c5613d7 Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Thu, 28 Jan 2021 14:25:08 +0100 Subject: [PATCH 046/193] Unified imports in AWS provider --- ScoutSuite/providers/aws/provider.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ScoutSuite/providers/aws/provider.py b/ScoutSuite/providers/aws/provider.py index 5ce6ffe12..0018eebb1 100755 --- a/ScoutSuite/providers/aws/provider.py +++ b/ScoutSuite/providers/aws/provider.py @@ -4,11 +4,10 @@ from ScoutSuite.core.console import print_error, print_exception, print_debug from ScoutSuite.providers.aws.services import AWSServicesConfig from ScoutSuite.providers.aws.resources.vpc.base import put_cidr_name -from ScoutSuite.providers.aws.utils import ec2_classic, get_aws_account_id +from ScoutSuite.providers.aws.utils import ec2_classic, get_aws_account_id, get_partition_name from ScoutSuite.providers.base.configs.browser import combine_paths, get_object_at, get_value_at from ScoutSuite.providers.base.provider import BaseProvider from ScoutSuite.utils import manage_dictionary -from ScoutSuite.providers.aws.utils import get_partition_name class AWSProvider(BaseProvider): From 92343fb2a8417ca6f6abb92d1ac1068692add795 Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Thu, 28 Jan 2021 14:25:46 +0100 Subject: [PATCH 047/193] Added a helper function to format resource ARNs --- ScoutSuite/providers/aws/utils.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/ScoutSuite/providers/aws/utils.py b/ScoutSuite/providers/aws/utils.py index c4e762d30..9c426f2ba 100755 --- a/ScoutSuite/providers/aws/utils.py +++ b/ScoutSuite/providers/aws/utils.py @@ -111,3 +111,27 @@ def snake_keys(d): else: new_table[new_key] = d[k] return new_table + +def format_arn(partition, service, region, account_id, resource_id, resource_type=None): + """ + Formats a resource ARN based on the parameters + + :param partition: The partition where the resource is located + :param service: The service namespace that identified the AWS product + :param region: The corresponding region + :param account_id: The ID of the AWS account that owns the resource + :param resource_id: The resource identified + :param resource_type: (Optional) The resource type + :return: Resource ARN + """ + + try: + # If a resource type is specified + if resource_type is not None: + arn = 'arn:{}:{}:{}:{}:{}/{}'.format(partition, service, region, account_id, resource_type, resource_id) + else: + arn = 'arn:{}:{}:{}:{}:{}'.format(partition, service, region, account_id, resource_id) + except Exception as e: + print_exception(f'Failed to parse a resource ARN: {e}') + return None + return arn From c30323dcec26764a19b5917f1d0e5a5343480d14 Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Fri, 29 Jan 2021 12:58:29 +0100 Subject: [PATCH 048/193] Changed ARN processing for resources implemented in #511 --- ScoutSuite/providers/aws/resources/ec2/ami.py | 8 +++++--- ScoutSuite/providers/aws/resources/ec2/instances.py | 11 ++++++----- .../providers/aws/resources/ec2/networkinterfaces.py | 8 +++++--- .../providers/aws/resources/ec2/securitygroups.py | 8 +++++--- ScoutSuite/providers/aws/resources/ec2/snapshots.py | 11 +++++------ ScoutSuite/providers/aws/resources/ec2/volumes.py | 9 +++++---- .../providers/aws/resources/elasticache/cluster.py | 8 +++++--- .../providers/aws/resources/elb/load_balancers.py | 10 ++++++---- .../resources/redshift/cluster_parameter_groups.py | 8 +++++--- .../providers/aws/resources/redshift/clusters.py | 9 +++++---- .../providers/aws/resources/route53/domains.py | 8 +++++--- ScoutSuite/providers/aws/resources/s3/buckets.py | 9 ++++++--- ScoutSuite/providers/aws/resources/ses/identities.py | 9 +++++---- ScoutSuite/providers/aws/resources/vpcs.py | 12 +++++++----- 14 files changed, 75 insertions(+), 53 deletions(-) diff --git a/ScoutSuite/providers/aws/resources/ec2/ami.py b/ScoutSuite/providers/aws/resources/ec2/ami.py index 266e18285..f65a897a5 100755 --- a/ScoutSuite/providers/aws/resources/ec2/ami.py +++ b/ScoutSuite/providers/aws/resources/ec2/ami.py @@ -1,11 +1,15 @@ from ScoutSuite.providers.aws.resources.base import AWSResources from ScoutSuite.providers.aws.facade.base import AWSFacade +from ScoutSuite.providers.aws.utils import get_partition_name, format_arn class AmazonMachineImages(AWSResources): def __init__(self, facade: AWSFacade, region: str): super().__init__(facade) self.region = region + self.partition = get_partition_name(facade.session) + self.service = 'ec2' + self.resource_type = 'amazon-machine-image' async def fetch_all(self): raw_images = await self.facade.ec2.get_images(self.region) @@ -16,7 +20,5 @@ async def fetch_all(self): def _parse_image(self, raw_image): raw_image['id'] = raw_image.get('ImageId') raw_image['name'] = raw_image.get('Name') - raw_image['arn'] = 'arn:aws:ec2:{}:{}:ami/{}'.format(self.region, - raw_image.get('OwnerId'), - raw_image.get('ImageId')) + raw_image['arn'] = format_arn(self.partition, self.service, self.region, raw_image.get('OwnerId'), raw_image.get('ImageId'), self.resource_type) return raw_image['id'], raw_image diff --git a/ScoutSuite/providers/aws/resources/ec2/instances.py b/ScoutSuite/providers/aws/resources/ec2/instances.py index 0e6070007..b8e0b9781 100755 --- a/ScoutSuite/providers/aws/resources/ec2/instances.py +++ b/ScoutSuite/providers/aws/resources/ec2/instances.py @@ -1,7 +1,7 @@ from ScoutSuite.providers.aws.resources.base import AWSResources from ScoutSuite.providers.aws.facade.base import AWSFacade -from ScoutSuite.providers.aws.utils import get_name -from ScoutSuite.providers.aws.utils import get_keys +from ScoutSuite.providers.aws.utils import get_name, get_keys, get_partition_name, format_arn + import re @@ -10,6 +10,9 @@ def __init__(self, facade: AWSFacade, region: str, vpc: str): super().__init__(facade) self.region = region self.vpc = vpc + self.partition = get_partition_name(facade.session) + self.service = 'ec2' + self.resource_type = 'instance' async def fetch_all(self): raw_instances = await self.facade.ec2.get_instances(self.region, self.vpc) @@ -21,9 +24,7 @@ async def _parse_instance(self, raw_instance): instance = {} id = raw_instance['InstanceId'] instance['id'] = id - instance['arn'] = 'arn:aws:ec2:{}:{}:instance/{}'.format(self.region, - raw_instance['OwnerId'], - raw_instance['InstanceId']) + instance['arn'] = format_arn(self.partition, self.service, self.region, raw_instance['OwnerId'], raw_instance['InstanceId'], self.resource_type) instance['reservation_id'] = raw_instance['ReservationId'] instance['availability_zone'] = raw_instance.get('Placement', {}).get('AvailabilityZone') instance['monitoring_enabled'] = raw_instance['Monitoring']['State'] == 'enabled' diff --git a/ScoutSuite/providers/aws/resources/ec2/networkinterfaces.py b/ScoutSuite/providers/aws/resources/ec2/networkinterfaces.py index 181d2e95c..1315ea96c 100755 --- a/ScoutSuite/providers/aws/resources/ec2/networkinterfaces.py +++ b/ScoutSuite/providers/aws/resources/ec2/networkinterfaces.py @@ -1,5 +1,6 @@ from ScoutSuite.providers.aws.resources.base import AWSResources from ScoutSuite.providers.aws.facade.base import AWSFacade +from ScoutSuite.providers.aws.utils import get_partition_name, format_arn class NetworkInterfaces(AWSResources): @@ -7,6 +8,9 @@ def __init__(self, facade: AWSFacade, region: str, vpc: str): super().__init__(facade) self.region = region self.vpc = vpc + self.partition = get_partition_name(facade.session) + self.service = 'ec2' + self.resource_type = 'network-interface' async def fetch_all(self): raw_security_groups = await self.facade.ec2.get_network_interfaces(self.region, self.vpc) @@ -16,7 +20,5 @@ async def fetch_all(self): def _parse_network_interface(self, raw_network_interface): raw_network_interface['name'] = raw_network_interface['NetworkInterfaceId'] - raw_network_interface['arn'] = 'arn:aws:ec2:{}:{}:network-interface/{}'.format(self.region, - raw_network_interface.get('OwnerId'), - raw_network_interface.get('NetworkInterfaceId')) + raw_network_interface['arn'] = format_arn(self.partition, self.service, self.region, raw_network_interface.get('OwnerId'), raw_network_interface.get('NetworkInterfaceId'), self.resource_type) return raw_network_interface['NetworkInterfaceId'], raw_network_interface diff --git a/ScoutSuite/providers/aws/resources/ec2/securitygroups.py b/ScoutSuite/providers/aws/resources/ec2/securitygroups.py index ce688a781..2890ed730 100755 --- a/ScoutSuite/providers/aws/resources/ec2/securitygroups.py +++ b/ScoutSuite/providers/aws/resources/ec2/securitygroups.py @@ -1,5 +1,6 @@ from ScoutSuite.providers.aws.resources.base import AWSResources from ScoutSuite.providers.aws.facade.base import AWSFacade +from ScoutSuite.providers.aws.utils import get_partition_name, format_arn from ScoutSuite.utils import manage_dictionary from ScoutSuite.core.fs import load_data @@ -11,6 +12,9 @@ def __init__(self, facade: AWSFacade, region: str, vpc: str): super().__init__(facade) self.region = region self.vpc = vpc + self.partition = get_partition_name(facade.session) + self.service = 'ec2' + self.resource_type = 'security-group' async def fetch_all(self): raw_security_groups = await self.facade.ec2.get_security_groups(self.region, self.vpc) @@ -22,9 +26,7 @@ def _parse_security_group(self, raw_security_group): security_group = {} security_group['name'] = raw_security_group['GroupName'] security_group['id'] = raw_security_group['GroupId'] - security_group['arn'] = 'arn:aws:ec2:{}:{}:security-group/{}'.format(self.region, - raw_security_group.get('OwnerId'), - raw_security_group.get('GroupId')) + security_group['arn'] = format_arn(self.partition, self.service, self.region, raw_security_group.get('OwnerId'), raw_security_group.get('GroupId'), self.resource_type) security_group['description'] = raw_security_group['Description'] security_group['owner_id'] = raw_security_group['OwnerId'] diff --git a/ScoutSuite/providers/aws/resources/ec2/snapshots.py b/ScoutSuite/providers/aws/resources/ec2/snapshots.py index 7e768f4e2..3f9111753 100755 --- a/ScoutSuite/providers/aws/resources/ec2/snapshots.py +++ b/ScoutSuite/providers/aws/resources/ec2/snapshots.py @@ -1,12 +1,15 @@ from ScoutSuite.providers.aws.resources.base import AWSResources from ScoutSuite.providers.aws.facade.base import AWSFacade -from ScoutSuite.providers.aws.utils import get_name +from ScoutSuite.providers.aws.utils import get_name, get_partition_name, format_arn class Snapshots(AWSResources): def __init__(self, facade: AWSFacade, region: str): super().__init__(facade) self.region = region + self.partition = get_partition_name(facade.session) + self.service = 'ec2' + self.resource_type = 'snapshot' async def fetch_all(self): raw_snapshots = await self.facade.ec2.get_snapshots(self.region) @@ -29,11 +32,7 @@ def _parse_snapshot(self, raw_snapshot): snapshot_dict['volume_id'] = raw_snapshot.get('VolumeId') snapshot_dict['volume_size'] = raw_snapshot.get('VolumeSize') snapshot_dict['create_volume_permissions'] = raw_snapshot.get('CreateVolumePermissions') - - snapshot_dict['arn'] = 'arn:aws:ec2:{}:{}:snapshot/{}'.format(self.region, - raw_snapshot.get('OwnerId'), - raw_snapshot.get('SnapshotId')) - + snapshot_dict['arn'] = format_arn(self.partition, self.service, self.region, raw_snapshot.get('OwnerId'), raw_snapshot.get('SnapshotId'), self.resource_type) return snapshot_dict['id'], snapshot_dict @staticmethod diff --git a/ScoutSuite/providers/aws/resources/ec2/volumes.py b/ScoutSuite/providers/aws/resources/ec2/volumes.py index d7499bb9a..5f1ca0701 100755 --- a/ScoutSuite/providers/aws/resources/ec2/volumes.py +++ b/ScoutSuite/providers/aws/resources/ec2/volumes.py @@ -1,12 +1,15 @@ from ScoutSuite.providers.aws.resources.base import AWSResources from ScoutSuite.providers.aws.facade.base import AWSFacade -from ScoutSuite.providers.aws.utils import get_name +from ScoutSuite.providers.aws.utils import get_name, get_partition_name, format_arn class Volumes(AWSResources): def __init__(self, facade: AWSFacade, region: str): super().__init__(facade) self.region = region + self.partition = get_partition_name(facade.session) + self.service = 'ec2' + self.resource_type = 'volume' async def fetch_all(self): raw_volumes = await self.facade.ec2.get_volumes(self.region) @@ -17,7 +20,5 @@ async def fetch_all(self): def _parse_volume(self, raw_volume): raw_volume['id'] = raw_volume.pop('VolumeId') raw_volume['name'] = get_name(raw_volume, raw_volume, 'id') - raw_volume['arn'] = 'arn:aws:ec2:{}:{}:volume/{}'.format(self.region, - self.facade.owner_id, - raw_volume.get('id')) + raw_volume['arn'] = format_arn(self.partition, self.service, self.region, self.facade.owner_id, raw_volume.get('id'), self.resource_type) return raw_volume['id'], raw_volume diff --git a/ScoutSuite/providers/aws/resources/elasticache/cluster.py b/ScoutSuite/providers/aws/resources/elasticache/cluster.py index 70af3ec43..adcc9c4f5 100755 --- a/ScoutSuite/providers/aws/resources/elasticache/cluster.py +++ b/ScoutSuite/providers/aws/resources/elasticache/cluster.py @@ -1,5 +1,6 @@ from ScoutSuite.providers.aws.facade.base import AWSFacade from ScoutSuite.providers.aws.resources.base import AWSResources +from ScoutSuite.providers.aws.utils import get_partition_name, format_arn class Clusters(AWSResources): @@ -7,6 +8,9 @@ def __init__(self, facade: AWSFacade, region: str, vpc: str): super().__init__(facade) self.region = region self.vpc = vpc + self.partition = get_partition_name(facade.session) + self.service = 'elasticache' + self.resource_type = 'cluster' async def fetch_all(self): raw_clusters = await self.facade.elasticache.get_clusters(self.region, self.vpc) @@ -16,7 +20,5 @@ async def fetch_all(self): def _parse_cluster(self, raw_cluster): raw_cluster['name'] = raw_cluster.pop('CacheClusterId') - raw_cluster['arn'] = 'arn:aws:elasticache:{}:{}:cluster/{}'.format(self.region, - self.facade.owner_id, - raw_cluster.get('name')) + raw_cluster['arn'] = format_arn(self.partition, self.service, self.region, self.facade.owner_id, raw_cluster.get('name'), self.resource_type) return raw_cluster['name'], raw_cluster diff --git a/ScoutSuite/providers/aws/resources/elb/load_balancers.py b/ScoutSuite/providers/aws/resources/elb/load_balancers.py index 28f6b3069..49bf16a20 100755 --- a/ScoutSuite/providers/aws/resources/elb/load_balancers.py +++ b/ScoutSuite/providers/aws/resources/elb/load_balancers.py @@ -1,6 +1,6 @@ from ScoutSuite.providers.aws.facade.base import AWSFacade from ScoutSuite.providers.aws.resources.base import AWSResources -from ScoutSuite.providers.aws.utils import get_keys +from ScoutSuite.providers.aws.utils import get_keys, get_partition_name, format_arn from ScoutSuite.providers.utils import get_non_provider_id @@ -9,6 +9,9 @@ def __init__(self, facade: AWSFacade, region: str, vpc: str): super().__init__(facade) self.region = region self.vpc = vpc + self.partition = get_partition_name(facade.session) + self.service = 'elb' + self.resource_type = 'load-balancer' async def fetch_all(self): raw_load_balancers = await self.facade.elb.get_load_balancers(self.region, self.vpc) @@ -22,9 +25,8 @@ def _parse_load_balancer(self, raw_load_balancer): ['DNSName', 'CreatedTime', 'AvailabilityZones', 'Subnets', 'Scheme', 'attributes']) load_balancer['security_groups'] = [] - load_balancer['arn'] = 'arn:aws:elb:{}:{}:load-balancer/{}'.format(self.region, - self.facade.owner_id, - raw_load_balancer.get('LoadBalancerName')) + load_balancer['arn'] = format_arn(self.partition, self.service, self.region, self.facade.owner_id, raw_load_balancer.get('LoadBalancerName'), self.resource_type) + for sg in raw_load_balancer['SecurityGroups']: load_balancer['security_groups'].append({'GroupId': sg}) diff --git a/ScoutSuite/providers/aws/resources/redshift/cluster_parameter_groups.py b/ScoutSuite/providers/aws/resources/redshift/cluster_parameter_groups.py index 9a24dda22..407e72880 100755 --- a/ScoutSuite/providers/aws/resources/redshift/cluster_parameter_groups.py +++ b/ScoutSuite/providers/aws/resources/redshift/cluster_parameter_groups.py @@ -1,6 +1,7 @@ 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 ScoutSuite.providers.aws.utils import get_partition_name, format_arn from .cluster_parameters import ClusterParameters @@ -13,6 +14,9 @@ class ClusterParameterGroups(AWSCompositeResources): def __init__(self, facade: AWSFacade, region: str): super().__init__(facade) self.region = region + self.partition = get_partition_name(facade.session) + self.service = 'redshift' + self.resource_type = 'parametergroup' async def fetch_all(self): raw_parameter_groups = await self.facade.redshift.get_cluster_parameter_groups(self.region) @@ -31,9 +35,7 @@ def _parse_parameter_group(self, raw_parameter_group): parameter_group = {} parameter_group['name'] = raw_parameter_group.get('ParameterGroupName') parameter_group['id'] = get_non_provider_id(parameter_group['name']) - parameter_group['arn'] = 'arn:aws:redshift:{}:{}:parametergroup:{}'.format(self.region, - self.facade.owner_id, - raw_parameter_group.get('ParameterGroupName')) + parameter_group['arn'] = format_arn(self.partition, self.service, self.region, self.facade.owner_id, raw_parameter_group.get('ParameterGroupName'), self.resource_type) parameter_group['family'] = raw_parameter_group.get('ParameterGroupFamily') parameter_group['description'] = raw_parameter_group.get('Description') parameter_group['is_default'] = self._is_default(raw_parameter_group) diff --git a/ScoutSuite/providers/aws/resources/redshift/clusters.py b/ScoutSuite/providers/aws/resources/redshift/clusters.py index 4084c237a..c02425ffc 100755 --- a/ScoutSuite/providers/aws/resources/redshift/clusters.py +++ b/ScoutSuite/providers/aws/resources/redshift/clusters.py @@ -1,5 +1,6 @@ from ScoutSuite.providers.aws.facade.base import AWSFacade from ScoutSuite.providers.aws.resources.base import AWSResources +from ScoutSuite.providers.aws.utils import get_partition_name, format_arn class Clusters(AWSResources): @@ -7,6 +8,9 @@ def __init__(self, facade: AWSFacade, region: str, vpc: str): super().__init__(facade) self.region = region self.vpc = vpc + self.partition = get_partition_name(facade.session) + self.service = 'redshift' + self.resource_type = 'cluster' async def fetch_all(self): raw_clusters = await self.facade.redshift.get_clusters(self.region, self.vpc) @@ -17,8 +21,5 @@ async def fetch_all(self): def _parse_cluster(self, raw_cluster): name = raw_cluster.pop('ClusterIdentifier') raw_cluster['name'] = name - raw_cluster['arn'] = 'arn:aws:redshift:{}:{}:cluster/{}'.format(self.region, - self.facade.owner_id, - name) - + raw_cluster['arn'] = format_arn(self.partition, self.service, self.region, self.facade.owner_id, name, self.resource_type) return name, raw_cluster diff --git a/ScoutSuite/providers/aws/resources/route53/domains.py b/ScoutSuite/providers/aws/resources/route53/domains.py index 4cc342b84..833091dc4 100755 --- a/ScoutSuite/providers/aws/resources/route53/domains.py +++ b/ScoutSuite/providers/aws/resources/route53/domains.py @@ -1,12 +1,16 @@ 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 +from ScoutSuite.providers.aws.utils import get_partition_name, format_arn class Domains(AWSResources): def __init__(self, facade: AWSFacade, region: str): super().__init__(facade) self.region = region + self.partition = get_partition_name(facade.session) + self.service = 'route53' + self.resource_type = 'domain' async def fetch_all(self): raw_domains = await self.facade.route53.get_domains(self.region) @@ -21,7 +25,5 @@ def _parse_domain(self, raw_domain): domain_dict['auto_renew'] = raw_domain.get('AutoRenew') domain_dict['transfer_lock'] = raw_domain.get('TransferLock') domain_dict['expiry'] = raw_domain.get('Expiry') - domain_dict['arn'] = 'arn:aws:route53:{}:{}:domain/{}'.format(self.region, - self.facade.owner_id, - domain_dict.get('id')) + domain_dict['arn'] = format_arn(self.partition, self.service, self.region, self.facade.owner_id, domain_dict.get('id'), self.resource_type) return domain_dict['id'], domain_dict diff --git a/ScoutSuite/providers/aws/resources/s3/buckets.py b/ScoutSuite/providers/aws/resources/s3/buckets.py index 779acb6fd..53bf9000c 100755 --- a/ScoutSuite/providers/aws/resources/s3/buckets.py +++ b/ScoutSuite/providers/aws/resources/s3/buckets.py @@ -1,10 +1,13 @@ from ScoutSuite.providers.aws.resources.base import AWSResources - +from ScoutSuite.providers.aws.utils import get_partition_name, format_arn from ScoutSuite.providers.utils import get_non_provider_id class Buckets(AWSResources): async def fetch_all(self): + self.partition = get_partition_name(self.facade.session) + self.service = 's3' + raw_buckets = await self.facade.s3.get_buckets() for raw_bucket in raw_buckets: name, resource = self._parse_bucket(raw_bucket) @@ -27,7 +30,7 @@ def _parse_bucket(self, raw_bucket): raw_bucket['name'] = raw_bucket.pop('Name') raw_bucket['CreationDate'] = str(raw_bucket['CreationDate']) - # If requested, get key properties raw_bucket['id'] = get_non_provider_id(raw_bucket['name']) - raw_bucket['arn'] = 'arn:aws:s3:::{}/*'.format(raw_bucket['name']) + # Passing empty strings for 'region' and 'account-id' since S3 bucket ARNs omit them + raw_bucket['arns'] = format_arn(self.partition, self.service, '', '', '*', raw_bucket['name']) return raw_bucket['id'], raw_bucket diff --git a/ScoutSuite/providers/aws/resources/ses/identities.py b/ScoutSuite/providers/aws/resources/ses/identities.py index 7e735ddf9..be092195d 100755 --- a/ScoutSuite/providers/aws/resources/ses/identities.py +++ b/ScoutSuite/providers/aws/resources/ses/identities.py @@ -1,6 +1,7 @@ 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 ScoutSuite.providers.aws.utils import get_partition_name, format_arn from .identity_policies import IdentityPolicies @@ -13,6 +14,9 @@ class Identities(AWSCompositeResources): def __init__(self, facade: AWSFacade, region: str): super().__init__(facade) self.region = region + self.partition = get_partition_name(facade.session) + self.service = 'ses' + self.resource_type = 'identity' async def fetch_all(self): raw_identities = await self.facade.ses.get_identities(self.region) @@ -32,8 +36,5 @@ def _parse_identity(self, raw_identity): identity['name'] = identity_name identity['DkimEnabled'] = dkim_attributes['DkimEnabled'] identity['DkimVerificationStatus'] = dkim_attributes['DkimVerificationStatus'] - identity['arn'] = 'arn:aws:ses:{}:{}:identity/{}'.format(self.region, - self.facade.owner_id, - identity_name) - + identity['arn'] = format_arn(self.partition, self.service, self.region, self.facade.owner_id, identity_name, self.resource_type) return get_non_provider_id(identity_name), identity diff --git a/ScoutSuite/providers/aws/resources/vpcs.py b/ScoutSuite/providers/aws/resources/vpcs.py index 2f872dc7c..157a5b6c7 100755 --- a/ScoutSuite/providers/aws/resources/vpcs.py +++ b/ScoutSuite/providers/aws/resources/vpcs.py @@ -1,5 +1,5 @@ from ScoutSuite.providers.aws.resources.base import AWSCompositeResources - +from ScoutSuite.providers.aws.utils import get_partition_name, format_arn class Vpcs(AWSCompositeResources): """ @@ -11,6 +11,9 @@ def __init__(self, facade, region: str, add_ec2_classic=False): super().__init__(facade) self.region = region self.add_ec2_classic = add_ec2_classic + self.partition = get_partition_name(facade.session) + self.service = 'vpc' + self.resource_type = 'virtual-private-cloud' async def fetch_all(self): raw_vpcs = await self.facade.ec2.get_vpcs(self.region) @@ -31,10 +34,9 @@ def _parse_vpc(self, raw_vpc): vpc['cidr_block'] = raw_vpc['CidrBlock'] vpc['default'] = raw_vpc['IsDefault'] vpc['state'] = raw_vpc['State'] - vpc['arn'] = 'arn:aws:vpc:{}:{}:virtual-private-cloud/{}'.format(self.region, - raw_vpc.get('OwnerId'), - raw_vpc.get('VpcId')) - # pull the name from tags + vpc['arn'] = format_arn(self.partition, self.service, self.region, raw_vpc.get('OwnerId'), raw_vpc.get('VpcId'), self.resource_type) + + # Pull the name from tags name_tag = next((d for i, d in enumerate(raw_vpc.get('Tags', [])) if d.get('Key') == 'Name'), None) if name_tag: vpc['name'] = name_tag.get('Value') From cef85538f2958fc5568ef98598028abd4729ddf5 Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Fri, 29 Jan 2021 16:06:15 +0100 Subject: [PATCH 049/193] Fixed typo --- ScoutSuite/providers/aws/resources/s3/buckets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ScoutSuite/providers/aws/resources/s3/buckets.py b/ScoutSuite/providers/aws/resources/s3/buckets.py index 53bf9000c..19760f51c 100755 --- a/ScoutSuite/providers/aws/resources/s3/buckets.py +++ b/ScoutSuite/providers/aws/resources/s3/buckets.py @@ -32,5 +32,5 @@ def _parse_bucket(self, raw_bucket): raw_bucket['id'] = get_non_provider_id(raw_bucket['name']) # Passing empty strings for 'region' and 'account-id' since S3 bucket ARNs omit them - raw_bucket['arns'] = format_arn(self.partition, self.service, '', '', '*', raw_bucket['name']) + raw_bucket['arn'] = format_arn(self.partition, self.service, '', '', '*', raw_bucket['name']) return raw_bucket['id'], raw_bucket From b36018edbecae783ffed2896a9d6777f8344dfc7 Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Fri, 29 Jan 2021 16:30:11 +0100 Subject: [PATCH 050/193] Fixed bug when sanitizing paths --- ScoutSuite/output/data/inc-scoutsuite/scoutsuite.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ScoutSuite/output/data/inc-scoutsuite/scoutsuite.js b/ScoutSuite/output/data/inc-scoutsuite/scoutsuite.js index 7d6a8258a..2ee13fe34 100755 --- a/ScoutSuite/output/data/inc-scoutsuite/scoutsuite.js +++ b/ScoutSuite/output/data/inc-scoutsuite/scoutsuite.js @@ -1004,7 +1004,7 @@ function updateTitle(title) { * Updates the Document Object Model */ function showPageFromHash() { - myhash = location.hash.replace(/[^a-z|0-9|.#-]/gi,'') + myhash = location.hash.replace(/[^a-z|0-9|.#-_]/gi,'') if (myhash) { updateDOM(myhash) } else { From 7839c645bc93293d07ebeae6abfd473f27f60c10 Mon Sep 17 00:00:00 2001 From: Andy Gu Date: Wed, 3 Feb 2021 10:02:49 -0500 Subject: [PATCH 051/193] Cloud Memorystore Support --- ...morystore.projects.id.redis_instances.html | 26 ++++++++++ ScoutSuite/providers/gcp/facade/base.py | 2 + .../gcp/facade/cloudmemorystoreredis.py | 19 ++++++++ .../resources/cloudmemorystore/__init__.py | 0 .../gcp/resources/cloudmemorystore/base.py | 7 +++ .../cloudmemorystore/redis_instances.py | 47 +++++++++++++++++++ ...store-redis-instance-auth-not-enabled.json | 19 ++++++++ ...store-redis-instance-ssl-not-required.json | 19 ++++++++ .../providers/gcp/rules/rulesets/default.json | 12 +++++ ScoutSuite/providers/gcp/services.py | 2 + ScoutSuite/utils.py | 1 + 11 files changed, 154 insertions(+) create mode 100755 ScoutSuite/output/data/html/partials/gcp/services.cloudmemorystore.projects.id.redis_instances.html create mode 100755 ScoutSuite/providers/gcp/facade/cloudmemorystoreredis.py create mode 100755 ScoutSuite/providers/gcp/resources/cloudmemorystore/__init__.py create mode 100755 ScoutSuite/providers/gcp/resources/cloudmemorystore/base.py create mode 100755 ScoutSuite/providers/gcp/resources/cloudmemorystore/redis_instances.py create mode 100755 ScoutSuite/providers/gcp/rules/findings/memorystore-redis-instance-auth-not-enabled.json create mode 100755 ScoutSuite/providers/gcp/rules/findings/memorystore-redis-instance-ssl-not-required.json diff --git a/ScoutSuite/output/data/html/partials/gcp/services.cloudmemorystore.projects.id.redis_instances.html b/ScoutSuite/output/data/html/partials/gcp/services.cloudmemorystore.projects.id.redis_instances.html new file mode 100755 index 000000000..dc75a343c --- /dev/null +++ b/ScoutSuite/output/data/html/partials/gcp/services.cloudmemorystore.projects.id.redis_instances.html @@ -0,0 +1,26 @@ + + + + + + + + diff --git a/ScoutSuite/providers/gcp/facade/base.py b/ScoutSuite/providers/gcp/facade/base.py index 366a2a54a..94b9f61af 100755 --- a/ScoutSuite/providers/gcp/facade/base.py +++ b/ScoutSuite/providers/gcp/facade/base.py @@ -4,6 +4,7 @@ from ScoutSuite.providers.gcp.facade.basefacade import GCPBaseFacade from ScoutSuite.providers.gcp.facade.cloudresourcemanager import CloudResourceManagerFacade from ScoutSuite.providers.gcp.facade.cloudsql import CloudSQLFacade +from ScoutSuite.providers.gcp.facade.cloudmemorystoreredis import CloudMemorystoreRedisFacade from ScoutSuite.providers.gcp.facade.cloudstorage import CloudStorageFacade from ScoutSuite.providers.gcp.facade.gce import GCEFacade from ScoutSuite.providers.gcp.facade.iam import IAMFacade @@ -29,6 +30,7 @@ def __init__(self, self.cloudresourcemanager = CloudResourceManagerFacade() self.cloudsql = CloudSQLFacade() self.cloudstorage = CloudStorageFacade() + self.cloudmemorystoreredis = CloudMemorystoreRedisFacade() self.gce = GCEFacade() self.iam = IAMFacade() self.kms = KMSFacade() diff --git a/ScoutSuite/providers/gcp/facade/cloudmemorystoreredis.py b/ScoutSuite/providers/gcp/facade/cloudmemorystoreredis.py new file mode 100755 index 000000000..407bfcb09 --- /dev/null +++ b/ScoutSuite/providers/gcp/facade/cloudmemorystoreredis.py @@ -0,0 +1,19 @@ +from ScoutSuite.core.console import print_exception +from ScoutSuite.providers.gcp.facade.basefacade import GCPBaseFacade +from ScoutSuite.providers.gcp.facade.utils import GCPFacadeUtils +from ScoutSuite.providers.utils import run_concurrently + +class CloudMemorystoreRedisFacade(GCPBaseFacade): + def __init__(self): + super().__init__('redis', 'v1beta1') + + async def get_redis_instances(self, project_id: str): + try: + formatted_parent = f'projects/{project_id}/locations/-' + cloudmem_client = self._get_client() + instances_group = cloudmem_client.projects().locations().instances() + request = instances_group.list(parent=formatted_parent) + return await GCPFacadeUtils.get_all('instances', request, instances_group) + except Exception as e: + print_exception(f'Failed to retrieve redis instances: {e}') + return [] diff --git a/ScoutSuite/providers/gcp/resources/cloudmemorystore/__init__.py b/ScoutSuite/providers/gcp/resources/cloudmemorystore/__init__.py new file mode 100755 index 000000000..e69de29bb diff --git a/ScoutSuite/providers/gcp/resources/cloudmemorystore/base.py b/ScoutSuite/providers/gcp/resources/cloudmemorystore/base.py new file mode 100755 index 000000000..f7ce438ac --- /dev/null +++ b/ScoutSuite/providers/gcp/resources/cloudmemorystore/base.py @@ -0,0 +1,7 @@ +from ScoutSuite.providers.gcp.resources.projects import Projects +from ScoutSuite.providers.gcp.resources.cloudmemorystore.redis_instances import RedisInstances + +class CloudMemorystore(Projects): + _children = [ + (RedisInstances, 'redis_instances') + ] diff --git a/ScoutSuite/providers/gcp/resources/cloudmemorystore/redis_instances.py b/ScoutSuite/providers/gcp/resources/cloudmemorystore/redis_instances.py new file mode 100755 index 000000000..534974356 --- /dev/null +++ b/ScoutSuite/providers/gcp/resources/cloudmemorystore/redis_instances.py @@ -0,0 +1,47 @@ +from ScoutSuite.core.console import print_exception +from ScoutSuite.providers.gcp.facade.base import GCPFacade +from ScoutSuite.providers.gcp.resources.base import GCPCompositeResources +from ScoutSuite.providers.utils import get_non_provider_id + + +class RedisInstances(GCPCompositeResources): + + def __init__(self, facade: GCPFacade, project_id: str): + super().__init__(facade) + self.project_id = project_id + + async def fetch_all(self): + raw_instances = await self.facade.cloudmemorystoreredis.get_redis_instances(self.project_id) + for raw_instance in raw_instances: + instance_id, instance = self._parse_instance(raw_instance) + self[instance_id] = instance + + def _parse_instance(self, raw_instance): + instance_dict = {} + + instance_dict['id'] = get_non_provider_id(raw_instance['name']) + instance_dict['project_id'] = self.project_id + instance_dict['name'] = raw_instance['name'] + instance_dict['display_name'] = raw_instance['displayName'] + instance_dict['location'] = raw_instance['locationId'] + instance_dict['redis_version'] = raw_instance['redisVersion'] + instance_dict['port'] = raw_instance['port'] + instance_dict['tier'] = raw_instance['tier'] + instance_dict['memory_size_gb'] = raw_instance['memorySizeGb'] + instance_dict['authorized_network'] = raw_instance['authorizedNetwork'] + instance_dict['connect_mode'] = raw_instance['connectMode'] + instance_dict['transit_encryption_mode'] = raw_instance['transitEncryptionMode'] + instance_dict['ssl_required'] = self._is_ssl_required(raw_instance) + instance_dict['auth_enabled'] = self._is_auth_required(raw_instance) + + return instance_dict['id'], instance_dict + + def _is_ssl_required(self, raw_instance): + is_ssl_required = raw_instance.get('transitEncryptionMode', False) + if is_ssl_required == 'SERVER_AUTHENTICATION': + return True + return False + + def _is_auth_required(self, raw_instance): + is_auth_enabled = raw_instance.get('authEnabled', False) + return is_auth_enabled diff --git a/ScoutSuite/providers/gcp/rules/findings/memorystore-redis-instance-auth-not-enabled.json b/ScoutSuite/providers/gcp/rules/findings/memorystore-redis-instance-auth-not-enabled.json new file mode 100755 index 000000000..028c787dd --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/memorystore-redis-instance-auth-not-enabled.json @@ -0,0 +1,19 @@ +{ + "description": "Memory Instance Allows Unauthenticated Connections", + "rationale": "All incoming connections to Cloud Memorystore databases should require the use of authentication and SSL.", + "compliance": [ + ], + "references": [ + "https://cloud.google.com/memorystore/docs/redis/managing-auth" + ], + "dashboard_name": "Instances", + "path": "cloudmemorystore.projects.id.redis_instances.id", + "conditions": [ + "and", + [ + "cloudmemorystore.projects.id.redis_instances.id.auth_enabled", + "false", + "" + ] + ] +} diff --git a/ScoutSuite/providers/gcp/rules/findings/memorystore-redis-instance-ssl-not-required.json b/ScoutSuite/providers/gcp/rules/findings/memorystore-redis-instance-ssl-not-required.json new file mode 100755 index 000000000..1889b63e2 --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/memorystore-redis-instance-ssl-not-required.json @@ -0,0 +1,19 @@ +{ + "description": "Memory Instance Not Requiring SSL for Incoming Connections", + "rationale": "All incoming connections to Cloud Memorystore databases should require the use of SSL.", + "compliance": [ + ], + "references": [ + "https://cloud.google.com/memorystore/docs/redis/securing-tls-connections" + ], + "dashboard_name": "Instances", + "path": "cloudmemorystore.projects.id.redis_instances.id", + "conditions": [ + "and", + [ + "cloudmemorystore.projects.id.redis_instances.id.ssl_required", + "false", + "" + ] + ] +} diff --git a/ScoutSuite/providers/gcp/rules/rulesets/default.json b/ScoutSuite/providers/gcp/rules/rulesets/default.json index d6b4532fa..8721c403e 100755 --- a/ScoutSuite/providers/gcp/rules/rulesets/default.json +++ b/ScoutSuite/providers/gcp/rules/rulesets/default.json @@ -308,6 +308,18 @@ "level": "warning" } ], + "memorystore-redis-instance-ssl-not-required.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "memorystore-redis-instance-auth-not-enabled.json": [ + { + "enabled": true, + "level": "warning" + } + ], "stackdriverlogging-no-export-sinks.json": [ { "enabled": true, diff --git a/ScoutSuite/providers/gcp/services.py b/ScoutSuite/providers/gcp/services.py index f90fbdc80..b6ad17559 100755 --- a/ScoutSuite/providers/gcp/services.py +++ b/ScoutSuite/providers/gcp/services.py @@ -1,6 +1,7 @@ from ScoutSuite.providers.base.services import BaseServicesConfig from ScoutSuite.providers.gcp.facade.base import GCPFacade from ScoutSuite.providers.gcp.resources.cloudsql.base import CloudSQL +from ScoutSuite.providers.gcp.resources.cloudmemorystore.base import CloudMemorystore from ScoutSuite.providers.gcp.resources.cloudstorage.base import CloudStorage from ScoutSuite.providers.gcp.resources.gce.base import ComputeEngine from ScoutSuite.providers.gcp.resources.iam.base import IAM @@ -21,6 +22,7 @@ def __init__(self, credentials=None, default_project_id=None, facade = GCPFacade(default_project_id, project_id, folder_id, organization_id, all_projects) self.cloudsql = CloudSQL(facade) + self.cloudmemorystore = CloudMemorystore(facade) self.cloudstorage = CloudStorage(facade) self.computeengine = ComputeEngine(facade) self.iam = IAM(facade) diff --git a/ScoutSuite/utils.py b/ScoutSuite/utils.py index 0fd1c41e6..8534f514f 100755 --- a/ScoutSuite/utils.py +++ b/ScoutSuite/utils.py @@ -47,6 +47,7 @@ 'virtualmachines': 'Virtual Machines', # GCP 'cloudstorage': 'Cloud Storage', + 'cloudmemorystore': 'Cloud Memorystore', 'cloudsql': 'Cloud SQL', 'stackdriverlogging': 'Stackdriver Logging', 'stackdrivermonitoring': 'Stackdriver Monitoring', From 62aa120911b5b78fae78272cd7719717888831cd Mon Sep 17 00:00:00 2001 From: Andy Gu Date: Wed, 3 Feb 2021 10:08:33 -0500 Subject: [PATCH 052/193] add newline --- .../providers/gcp/resources/cloudmemorystore/redis_instances.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ScoutSuite/providers/gcp/resources/cloudmemorystore/redis_instances.py b/ScoutSuite/providers/gcp/resources/cloudmemorystore/redis_instances.py index 534974356..f31e1a6db 100755 --- a/ScoutSuite/providers/gcp/resources/cloudmemorystore/redis_instances.py +++ b/ScoutSuite/providers/gcp/resources/cloudmemorystore/redis_instances.py @@ -45,3 +45,4 @@ def _is_ssl_required(self, raw_instance): def _is_auth_required(self, raw_instance): is_auth_enabled = raw_instance.get('authEnabled', False) return is_auth_enabled + From 5c824870fc9167e517702c7f2d2c30a369092ef6 Mon Sep 17 00:00:00 2001 From: Andy Gu Date: Wed, 3 Feb 2021 10:11:15 -0500 Subject: [PATCH 053/193] two quick comments --- ScoutSuite/providers/gcp/facade/cloudmemorystoreredis.py | 1 + .../providers/gcp/resources/cloudmemorystore/redis_instances.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/ScoutSuite/providers/gcp/facade/cloudmemorystoreredis.py b/ScoutSuite/providers/gcp/facade/cloudmemorystoreredis.py index 407bfcb09..85ca97d0f 100755 --- a/ScoutSuite/providers/gcp/facade/cloudmemorystoreredis.py +++ b/ScoutSuite/providers/gcp/facade/cloudmemorystoreredis.py @@ -8,6 +8,7 @@ def __init__(self): super().__init__('redis', 'v1beta1') async def get_redis_instances(self, project_id: str): + # Retrieves Redis Instances using the Cloud Memorystore API try: formatted_parent = f'projects/{project_id}/locations/-' cloudmem_client = self._get_client() diff --git a/ScoutSuite/providers/gcp/resources/cloudmemorystore/redis_instances.py b/ScoutSuite/providers/gcp/resources/cloudmemorystore/redis_instances.py index f31e1a6db..396a853fe 100755 --- a/ScoutSuite/providers/gcp/resources/cloudmemorystore/redis_instances.py +++ b/ScoutSuite/providers/gcp/resources/cloudmemorystore/redis_instances.py @@ -37,6 +37,8 @@ def _parse_instance(self, raw_instance): return instance_dict['id'], instance_dict def _is_ssl_required(self, raw_instance): + # Checks if transit encryption mode is SERVER_AUTHENTICATION. Otherwise, SSL + # is not enabled. is_ssl_required = raw_instance.get('transitEncryptionMode', False) if is_ssl_required == 'SERVER_AUTHENTICATION': return True From 27e399403f6b1aef30a7181e9bb7315ca06c2c80 Mon Sep 17 00:00:00 2001 From: Andy Gu Date: Wed, 3 Feb 2021 10:12:29 -0500 Subject: [PATCH 054/193] rename variable in template --- .../services.cloudmemorystore.projects.id.redis_instances.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ScoutSuite/output/data/html/partials/gcp/services.cloudmemorystore.projects.id.redis_instances.html b/ScoutSuite/output/data/html/partials/gcp/services.cloudmemorystore.projects.id.redis_instances.html index dc75a343c..9953bdcf8 100755 --- a/ScoutSuite/output/data/html/partials/gcp/services.cloudmemorystore.projects.id.redis_instances.html +++ b/ScoutSuite/output/data/html/partials/gcp/services.cloudmemorystore.projects.id.redis_instances.html @@ -22,5 +22,5 @@

    Information

    {{> modal-template template='services.cloudmemorystore.projects.id.redis_instances'}} From 0d3e85853a184e48bb36df7c4e3ab816c2f92342 Mon Sep 17 00:00:00 2001 From: Andy Gu Date: Wed, 3 Feb 2021 12:31:07 -0500 Subject: [PATCH 055/193] Memorystore Rendering --- ...ices.cloudmemorystore.projects.id.redis_instances.html | 3 +-- ScoutSuite/providers/gcp/metadata.json | 8 ++++++++ .../memorystore-redis-instance-auth-not-enabled.json | 2 +- .../memorystore-redis-instance-ssl-not-required.json | 2 +- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/ScoutSuite/output/data/html/partials/gcp/services.cloudmemorystore.projects.id.redis_instances.html b/ScoutSuite/output/data/html/partials/gcp/services.cloudmemorystore.projects.id.redis_instances.html index 9953bdcf8..8ba77fcfc 100755 --- a/ScoutSuite/output/data/html/partials/gcp/services.cloudmemorystore.projects.id.redis_instances.html +++ b/ScoutSuite/output/data/html/partials/gcp/services.cloudmemorystore.projects.id.redis_instances.html @@ -1,7 +1,6 @@ From 57828e7182da84db7df75a9a3d2b631636f7cb74 Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Tue, 9 Feb 2021 17:23:36 +0100 Subject: [PATCH 073/193] Route53 hosted zones ARN processing --- ScoutSuite/providers/aws/resources/route53/hosted_zones.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ScoutSuite/providers/aws/resources/route53/hosted_zones.py b/ScoutSuite/providers/aws/resources/route53/hosted_zones.py index be1049849..d2a44d692 100755 --- a/ScoutSuite/providers/aws/resources/route53/hosted_zones.py +++ b/ScoutSuite/providers/aws/resources/route53/hosted_zones.py @@ -1,11 +1,15 @@ from ScoutSuite.providers.aws.resources.base import AWSResources from ScoutSuite.providers.aws.facade.base import AWSFacade +from ScoutSuite.providers.aws.utils import get_partition_name, format_arn class HostedZones(AWSResources): def __init__(self, facade: AWSFacade, region: str): super().__init__(facade) self.region = region + self.partition = get_partition_name(facade.session) + self.service = 'route53' + self.resource_type = 'hosted-zone' async def fetch_all(self): raw_hosted_zones = await self.facade.route53.get_hosted_zones() @@ -21,4 +25,5 @@ async def _parse_hosted_zone(self, raw_hosted_zone): hosted_zone_dict['config'] = raw_hosted_zone.get('Config') hosted_zone_dict['resource_record_sets'] = await self.facade.route53.get_resource_records(hosted_zone_dict['id']) hosted_zone_dict['resource_record_set_count'] = raw_hosted_zone.get('ResourceRecordSetCount') + hosted_zone_dict['arn'] = format_arn(self.partition, self.service, self.region, '', raw_hosted_zone.get('Id'), self.resource_type) return hosted_zone_dict['id'], hosted_zone_dict From fb690c7d8896e28a737f40106c073238096475bc Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Tue, 9 Feb 2021 17:24:18 +0100 Subject: [PATCH 074/193] Route53 hosted zones partial ARN display --- .../partials/aws/services.route53.regions.id.hosted_zones.html | 1 + 1 file changed, 1 insertion(+) diff --git a/ScoutSuite/output/data/html/partials/aws/services.route53.regions.id.hosted_zones.html b/ScoutSuite/output/data/html/partials/aws/services.route53.regions.id.hosted_zones.html index 52cd2d86e..194452745 100755 --- a/ScoutSuite/output/data/html/partials/aws/services.route53.regions.id.hosted_zones.html +++ b/ScoutSuite/output/data/html/partials/aws/services.route53.regions.id.hosted_zones.html @@ -6,6 +6,7 @@

    {{name}}

    Information

    ID: {{value_or_none id}}
    +
    ARN: {{value_or_none arn}}
    Caller Reference: {{value_or_none caller_reference}}
    Resource Record Set Count: {{value_or_none resource_record_set_count}}
    From f3f691cfa0963c53f886e11fe53693c3191b8652 Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Tue, 9 Feb 2021 17:25:14 +0100 Subject: [PATCH 075/193] Redshift cluster parameters ARN processing --- .../providers/aws/resources/redshift/cluster_parameters.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ScoutSuite/providers/aws/resources/redshift/cluster_parameters.py b/ScoutSuite/providers/aws/resources/redshift/cluster_parameters.py index c5ed6ece3..93d8bae34 100755 --- a/ScoutSuite/providers/aws/resources/redshift/cluster_parameters.py +++ b/ScoutSuite/providers/aws/resources/redshift/cluster_parameters.py @@ -1,5 +1,6 @@ from ScoutSuite.providers.aws.facade.base import AWSFacade from ScoutSuite.providers.aws.resources.base import AWSResources +from ScoutSuite.providers.aws.utils import get_partition_name, format_arn class ClusterParameters(AWSResources): @@ -7,6 +8,9 @@ def __init__(self, facade: AWSFacade, region: str, parameter_group_name: str): super().__init__(facade) self.region = region self.parameter_group_name = parameter_group_name + self.partition = get_partition_name(facade.session) + self.service = 'redshift' + self.resource_type = 'cluster-parameter' async def fetch_all(self): raw_parameters = await self.facade.redshift.get_cluster_parameters( @@ -18,4 +22,5 @@ async def fetch_all(self): def _parse_parameter(self, raw_parameter): parameter = {'value': raw_parameter['ParameterValue'], 'source': raw_parameter['Source']} + raw_parameter['arn'] = format_arn(self.partition, self.service, self.region, '', raw_parameter.get('ParameterName'), self.resource_type) return raw_parameter['ParameterName'], parameter From 06c84e6455ff0c9458cf300b0221f4bb1e8318ff Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Tue, 9 Feb 2021 17:25:55 +0100 Subject: [PATCH 076/193] Redshift cluster parameter group partial ARN display --- .../aws/services.redshift.regions.id.parameter_groups.html | 1 + 1 file changed, 1 insertion(+) 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 7877e2455..e3eac4528 100755 --- 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 @@ -15,6 +15,7 @@

    Information

    Parameters

      {{#each parameters}} +
      ARN: {{arn}}
    • {{@key}}: {{value}}
    • From d5de4aaece1d2c48a64455d04fb0d3bb8e473c13 Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Tue, 9 Feb 2021 17:26:37 +0100 Subject: [PATCH 077/193] RDS subnet groups ARN processing --- ScoutSuite/providers/aws/resources/rds/subnetgroups.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ScoutSuite/providers/aws/resources/rds/subnetgroups.py b/ScoutSuite/providers/aws/resources/rds/subnetgroups.py index 9719429c0..c8b87642f 100755 --- a/ScoutSuite/providers/aws/resources/rds/subnetgroups.py +++ b/ScoutSuite/providers/aws/resources/rds/subnetgroups.py @@ -16,4 +16,5 @@ async def fetch_all(self): def _parse_subnet_group(self, raw_subnet_group): raw_subnet_group['name'] = raw_subnet_group['DBSubnetGroupName'] + raw_subnet_group['ARN'] = raw_subnet_group.pop('DBSubnetGroupArn') return raw_subnet_group['name'], raw_subnet_group From b8b676da399b0ad4b1abf21bc84579f27f147aa9 Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Tue, 9 Feb 2021 17:27:47 +0100 Subject: [PATCH 078/193] Cloudwatch metric filters ARN processing --- .../providers/aws/resources/cloudwatch/metric_filters.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ScoutSuite/providers/aws/resources/cloudwatch/metric_filters.py b/ScoutSuite/providers/aws/resources/cloudwatch/metric_filters.py index e10b5396d..339b271fc 100644 --- a/ScoutSuite/providers/aws/resources/cloudwatch/metric_filters.py +++ b/ScoutSuite/providers/aws/resources/cloudwatch/metric_filters.py @@ -1,12 +1,16 @@ 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 +from ScoutSuite.providers.aws.utils import get_partition_name, format_arn class MetricFilters(AWSResources): def __init__(self, facade: AWSFacade, region: str): super(MetricFilters, self).__init__(facade) self.region = region + self.partition = get_partition_name(facade.session) + self.service = 'cloudwatch' + self.resource_type = 'metric-filter' async def fetch_all(self): for raw_metric_filter in await self.facade.cloudwatch.get_metric_filters(self.region): @@ -22,6 +26,7 @@ def _parse_metric_filter(self, raw_metric_filter): metric_filter_dict['pattern'] = raw_metric_filter.get('filterPattern') metric_filter_dict['metric_transformations'] = raw_metric_filter.get('metricTransformations') metric_filter_dict['log_group_name'] = raw_metric_filter.get('logGroupName') + metric_filter_dict['arn'] = format_arn(self.partition, self.service, self.region, '', raw_metric_filter.get('filterName'), self.resource_type) return metric_filter_dict['id'], metric_filter_dict From 6d57b66ac43f95da0663efa66ba1784eb343e872 Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Tue, 9 Feb 2021 17:28:19 +0100 Subject: [PATCH 079/193] Cloudwatch metric filters partial ARN display --- .../aws/services.cloudwatch.regions.id.metric_filters.html | 1 + 1 file changed, 1 insertion(+) diff --git a/ScoutSuite/output/data/html/partials/aws/services.cloudwatch.regions.id.metric_filters.html b/ScoutSuite/output/data/html/partials/aws/services.cloudwatch.regions.id.metric_filters.html index 0ed5443e8..ea8c469f3 100644 --- a/ScoutSuite/output/data/html/partials/aws/services.cloudwatch.regions.id.metric_filters.html +++ b/ScoutSuite/output/data/html/partials/aws/services.cloudwatch.regions.id.metric_filters.html @@ -6,6 +6,7 @@

      {{name}}

      Information

      Name: {{value_or_none name}}
      +
      ARN: {{value_or_none arn}}
      Creation Time: {{format_date creation_time}}
      Log Group Name: {{value_or_none log_group_name}}
      Pattern: {{value_or_none pattern}}
      From 6fb1159eb72f6f4250129de724d1d3182d1b7a72 Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Tue, 9 Feb 2021 17:29:12 +0100 Subject: [PATCH 080/193] EMR clusters ARN processing --- ScoutSuite/providers/aws/resources/emr/clusters.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ScoutSuite/providers/aws/resources/emr/clusters.py b/ScoutSuite/providers/aws/resources/emr/clusters.py index 9708bc415..8de18f03d 100755 --- a/ScoutSuite/providers/aws/resources/emr/clusters.py +++ b/ScoutSuite/providers/aws/resources/emr/clusters.py @@ -16,4 +16,5 @@ async def fetch_all(self): def _parse_cluster(self, raw_cluster): raw_cluster['id'] = raw_cluster.pop('Id') raw_cluster['name'] = raw_cluster.pop('Name') + raw_cluster['arn'] = raw_cluster.pop('ClusterArn') return raw_cluster['id'], raw_cluster From 246f716eda7f4a2467376a9f237230963355c730 Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Tue, 9 Feb 2021 17:29:47 +0100 Subject: [PATCH 081/193] EMR clusters partial ARN display --- .../services.emr.regions.id.vpcs.id.clusters.html | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/ScoutSuite/output/data/html/partials/aws/services.emr.regions.id.vpcs.id.clusters.html b/ScoutSuite/output/data/html/partials/aws/services.emr.regions.id.vpcs.id.clusters.html index 40fd32370..e62398df5 100755 --- a/ScoutSuite/output/data/html/partials/aws/services.emr.regions.id.vpcs.id.clusters.html +++ b/ScoutSuite/output/data/html/partials/aws/services.emr.regions.id.vpcs.id.clusters.html @@ -7,13 +7,14 @@

      {{name}}

      Information

        -
      • Region: {{region}}
      • -
      • VPC: {{getValueAt 'services.ec2.regions' region 'vpcs' vpc 'name'}} ({{vpc}})
      • -
      • Id: {{id}}
      • -
      • Availability zone: {{Ec2InstanceAttributes.Ec2AvailabilityZone}}
      • -
      • Status: {{Status.State}}
      • -
      • Instance profile: {{Ec2InstanceAttributes.IamInstanceProfile}}
      • -
      • Visible to all users: {{VisibleToAllUsers}}
      • +
      • Region: {{region}}
      • +
      • ARN: {{arn}}
      • +
      • VPC: {{getValueAt 'services.ec2.regions' region 'vpcs' vpc 'name'}} ({{getValueAt 'services.ec2.regions' region 'vpcs' vpc 'arn'}})
      • +
      • Id: {{id}}
      • +
      • Availability zone: {{Ec2InstanceAttributes.Ec2AvailabilityZone}}
      • +
      • Status: {{Status.State}}
      • +
      • Instance profile: {{Ec2InstanceAttributes.IamInstanceProfile}}
      • +
      • Visible to all users: {{VisibleToAllUsers}}
      From 0c58edd56bc0507bfe332829594b0cea9610a3d7 Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Tue, 9 Feb 2021 17:30:37 +0100 Subject: [PATCH 082/193] KMS grants ARN processing --- ScoutSuite/providers/aws/resources/kms/grants.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ScoutSuite/providers/aws/resources/kms/grants.py b/ScoutSuite/providers/aws/resources/kms/grants.py index 4684979f1..54cc20c56 100755 --- a/ScoutSuite/providers/aws/resources/kms/grants.py +++ b/ScoutSuite/providers/aws/resources/kms/grants.py @@ -1,5 +1,6 @@ from ScoutSuite.providers.aws.facade.base import AWSFacade from ScoutSuite.providers.aws.resources.base import AWSResources +from ScoutSuite.providers.aws.utils import get_partition_name, format_arn class Grants(AWSResources): @@ -7,6 +8,9 @@ def __init__(self, facade: AWSFacade, region: str, key_id: str): super().__init__(facade) self.region = region self.key_id = key_id + self.partition = get_partition_name(facade.session) + self.service = 'kms' + self.resource_type = 'grant' async def fetch_all(self): raw_grants = await self.facade.kms.get_grants(self.region, self.key_id) @@ -24,6 +28,7 @@ def _parse_grant(self, raw_grant): 'retiring_principal': raw_grant.get('ReitirngPrincipal'), 'issuing_account': raw_grant.get('IssuingAccount'), 'operations': raw_grant.get('Operations'), - 'constraints': raw_grant.get('Constraints') + 'constraints': raw_grant.get('Constraints'), + 'arn': format_arn(self.partition, self.service, self.region, raw_grant.get('IssuingAccount').split(':')[4], raw_grant.get('GrantId'), self.resource_type) if ':' in raw_grant.get('IssuingAccount') else format_arn(self.partition, self.service, self.region, raw_grant.get('IssuingAccount'), raw_grant.get('GrantId'), self.resource_type) } return grant_dict['grant_id'], grant_dict From b4a4611357bf22915ef69cf51a30bdc4d395c110 Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Tue, 9 Feb 2021 17:31:20 +0100 Subject: [PATCH 083/193] KMS keys with grant ARNs display --- .../html/partials/aws/services.kms.regions.id.keys.html | 9 +++++++++ 1 file changed, 9 insertions(+) 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 a6d1cd8e4..579b8bb07 100755 --- 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 @@ -30,6 +30,15 @@

      Aliases

      Aliases

      {{/if}}
      +
      +

      Grants

      +
        + {{#each grants}} +
      • Name: {{value_or_none name}}
      • + {{> generic_object this}} + {{/each}} +
      +
      {{#if policy}} {{> accordion_policy name = 'Key Policy' document = policy policy_path = (concat 'kms.regions' region 'keys' @key 'policy')}} From b4028d83abdc3023c69138f46994bff9a203c867 Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Tue, 9 Feb 2021 17:31:50 +0100 Subject: [PATCH 084/193] ELB policies ARN processing --- ScoutSuite/providers/aws/resources/elb/policies.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ScoutSuite/providers/aws/resources/elb/policies.py b/ScoutSuite/providers/aws/resources/elb/policies.py index 3f808166b..d461e5ad2 100755 --- a/ScoutSuite/providers/aws/resources/elb/policies.py +++ b/ScoutSuite/providers/aws/resources/elb/policies.py @@ -1,5 +1,6 @@ from ScoutSuite.providers.aws.resources.base import AWSResources from ScoutSuite.providers.aws.facade.base import AWSFacade +from ScoutSuite.providers.aws.utils import get_partition_name, format_arn from ScoutSuite.providers.utils import get_non_provider_id @@ -7,6 +8,9 @@ class Policies(AWSResources): def __init__(self, facade: AWSFacade, region: str): super().__init__(facade) self.region = region + self.partition = get_partition_name(facade.session) + self.service = 'elb' + self.resource_type = 'policy' async def fetch_all(self): raw_policies = await self.facade.elb.get_policies(self.region) @@ -17,4 +21,5 @@ async def fetch_all(self): def _parse_policy(self, raw_policy): raw_policy['name'] = raw_policy.pop('PolicyName') policy_id = get_non_provider_id(raw_policy['name']) + raw_policy['arn'] = format_arn(self.partition, self.service, self.region, '', raw_policy['name'], self.resource_type) return policy_id, raw_policy From e5434f96ae7c5c8490c149c9d6d7da7c5ce8eec1 Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Tue, 9 Feb 2021 17:32:16 +0100 Subject: [PATCH 085/193] ELB policies partial ARN display --- .../partials/aws/services.elb.regions.id.elb_policies.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ScoutSuite/output/data/html/partials/aws/services.elb.regions.id.elb_policies.html b/ScoutSuite/output/data/html/partials/aws/services.elb.regions.id.elb_policies.html index 0cac00d38..c1b4eb4ae 100755 --- a/ScoutSuite/output/data/html/partials/aws/services.elb.regions.id.elb_policies.html +++ b/ScoutSuite/output/data/html/partials/aws/services.elb.regions.id.elb_policies.html @@ -3,6 +3,10 @@

      {{name}}

      +
      +

      Information

      +
      ARN: {{value_or_none arn}}
      +
      {{#ifEqual PolicyTypeName 'SSLNegotiationPolicyType'}}

      Protocols

      From 2f4cbce39cdb9f0c97ffd352a150fc8c251f4aa2 Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Tue, 9 Feb 2021 17:33:03 +0100 Subject: [PATCH 086/193] EFS filesystems ARN processing --- ScoutSuite/providers/aws/resources/efs/filesystems.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ScoutSuite/providers/aws/resources/efs/filesystems.py b/ScoutSuite/providers/aws/resources/efs/filesystems.py index 8c1b157b8..f416be827 100755 --- a/ScoutSuite/providers/aws/resources/efs/filesystems.py +++ b/ScoutSuite/providers/aws/resources/efs/filesystems.py @@ -1,11 +1,15 @@ from ScoutSuite.providers.aws.facade.base import AWSFacade from ScoutSuite.providers.aws.resources.base import AWSResources +from ScoutSuite.providers.aws.utils import get_partition_name, format_arn class FileSystems(AWSResources): def __init__(self, facade: AWSFacade, region: str): super().__init__(facade) self.region = region + self.partition = get_partition_name(facade.session) + self.service = 'elasticfilesystem' + self.resource_type = 'file-system' async def fetch_all(self): raw_file_systems = await self.facade.efs.get_file_systems(self.region) @@ -17,5 +21,5 @@ def _parse_file_system(self, raw_file_system): fs_id = raw_file_system.pop('FileSystemId') raw_file_system['name'] = raw_file_system.pop('Name') if 'Name' in raw_file_system else None raw_file_system['tags'] = raw_file_system.pop('Tags') - + raw_file_system['arn'] = format_arn(self.partition, self.service, self.region, raw_file_system.get('OwnerId'), fs_id, self.resource_type) return fs_id, raw_file_system From f5bbf7e85baa1fcfe7a0842af9c5654020e9971e Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Tue, 9 Feb 2021 17:33:33 +0100 Subject: [PATCH 087/193] Directconnect connections ARN processing --- .../providers/aws/resources/directconnect/connections.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ScoutSuite/providers/aws/resources/directconnect/connections.py b/ScoutSuite/providers/aws/resources/directconnect/connections.py index 82de5af90..6884b296d 100755 --- a/ScoutSuite/providers/aws/resources/directconnect/connections.py +++ b/ScoutSuite/providers/aws/resources/directconnect/connections.py @@ -1,11 +1,15 @@ from ScoutSuite.providers.aws.facade.base import AWSFacade from ScoutSuite.providers.aws.resources.base import AWSResources +from ScoutSuite.providers.aws.utils import get_partition_name, format_arn class Connections(AWSResources): def __init__(self, facade: AWSFacade, region: str): super().__init__(facade) self.region = region + self.partition = get_partition_name(facade.session) + self.service = 'directconnect' + self.resource_type = 'connection' async def fetch_all(self): raw_connections = await self.facade.directconnect.get_connections(self.region) @@ -16,4 +20,5 @@ async def fetch_all(self): def _parse_connection(self, raw_connection): raw_connection['id'] = raw_connection.pop('connectionId') raw_connection['name'] = raw_connection.pop('connectionName') + raw_connection['arn'] = format_arn(self.partition, self.service, self.region, raw_connection.get('ownerAccount'), raw_connection.get('id'), self.resource_type) return raw_connection['id'], raw_connection From 5d782b48d5f37e75673d43c8876451e98e184355 Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Tue, 9 Feb 2021 17:34:38 +0100 Subject: [PATCH 088/193] Changed Handlebars accordion_policy partial headers from h4 to h5 --- ScoutSuite/output/data/html/partials/accordion_policy.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ScoutSuite/output/data/html/partials/accordion_policy.html b/ScoutSuite/output/data/html/partials/accordion_policy.html index ce571485a..e146da4ab 100755 --- a/ScoutSuite/output/data/html/partials/accordion_policy.html +++ b/ScoutSuite/output/data/html/partials/accordion_policy.html @@ -1,11 +1,11 @@ + + + + + + diff --git a/ScoutSuite/providers/gcp/metadata.json b/ScoutSuite/providers/gcp/metadata.json index 4a03ca23b..e3382af73 100755 --- a/ScoutSuite/providers/gcp/metadata.json +++ b/ScoutSuite/providers/gcp/metadata.json @@ -17,6 +17,10 @@ "bindings": { "cols": 2, "path": "services.iam.projects.id.bindings" + }, + "bindings_separation_duties": { + "cols": 2, + "path": "services.iam.projects.id.bindings_separation_duties" } } }, diff --git a/ScoutSuite/providers/gcp/resources/iam/base.py b/ScoutSuite/providers/gcp/resources/iam/base.py index 8ede240e5..6544cdf95 100755 --- a/ScoutSuite/providers/gcp/resources/iam/base.py +++ b/ScoutSuite/providers/gcp/resources/iam/base.py @@ -3,6 +3,7 @@ from ScoutSuite.providers.gcp.resources.iam.users import Users from ScoutSuite.providers.gcp.resources.iam.groups import Groups from ScoutSuite.providers.gcp.resources.iam.service_accounts import ServiceAccounts +from ScoutSuite.providers.gcp.resources.iam.bindings_separation_duties import BindingsSeparationDuties class IAM(Projects): @@ -10,5 +11,6 @@ class IAM(Projects): (Bindings, 'bindings'), (Users, 'users'), (Groups, 'groups'), - (ServiceAccounts, 'service_accounts') + (ServiceAccounts, 'service_accounts'), + (BindingsSeparationDuties, 'bindings_separation_duties') ] diff --git a/ScoutSuite/providers/gcp/resources/iam/bindings_separation_duties.py b/ScoutSuite/providers/gcp/resources/iam/bindings_separation_duties.py new file mode 100644 index 000000000..cd2be9bc4 --- /dev/null +++ b/ScoutSuite/providers/gcp/resources/iam/bindings_separation_duties.py @@ -0,0 +1,68 @@ +from ScoutSuite.providers.base.resources.base import Resources +from ScoutSuite.providers.gcp.facade.base import GCPFacade + + +class BindingsSeparationDuties(Resources): + def __init__(self, facade: GCPFacade, project_id: str): + super().__init__(facade) + self.project_id = project_id + + async def fetch_all(self): + raw_bindings = await self.facade.cloudresourcemanager.get_member_bindings(self.project_id) + binding_id, binding = await self._parse_binding_separation(raw_bindings) + self[binding_id] = binding + x=1 + + async def _parse_binding_separation(self, raw_bindings): + binding_dict = {} + binding_dict['id'] = self.project_id + binding_dict["account_separation_duties"] = self.ensure_seperation_duties(raw_bindings) + binding_dict["kms_separation_duties"] = self.ensure_KMS_seperation_duties(raw_bindings) + + return binding_dict['id'], binding_dict + + def ensure_seperation_duties(self, raw_bindings): + # This function checks if a member has both the iam.serviceAccountAdmin role and iam.serviceAccountUser role. + # If the roles do have a common member the function returns False + list_members_role_admin = [] + list_members_role_other = [] + for binding in raw_bindings: + role = binding['role'].split('/')[-1] + if role == 'iam.serviceAccountAdmin': + list_members_role_admin = binding['members'] + if role == 'iam.serviceAccountUser': + list_members_role_other = binding['members'] + + common_members = list(set(list_members_role_admin).intersection(list_members_role_other)) + if common_members: + return False + return True + + def ensure_KMS_seperation_duties(self, raw_bindings): + # This function checks if a member has both the cloudkms.admin role and either + # cloudkms.cryptoKeyEncrypterDecrypter, cloudkms.cryptoKeyEncrypter, cloudkms.cryptoKeyDecrypter role. + # If the roles do have a common member the function returns False + list_members_role_admin = [] + list_members_role_others = {"cloudkms.cryptoKeyEncrypterDecrypter": [], + "cloudkms.cryptoKeyEncrypter": [], + "cloudkms.cryptoKeyDecrypter": []} + for binding in raw_bindings: + role = binding['role'].split('/')[-1] + if role == 'cloudkms.admin': + list_members_role_admin = binding['members'] + if role == 'cloudkms.cryptoKeyEncrypterDecrypter': + list_members_role_others['cloudkms.cryptoKeyEncrypterDecrypter'] = binding['members'] + if role == 'cloudkms.cryptoKeyEncrypter': + list_members_role_others['cloudkms.cryptoKeyEncrypter'] = binding['members'] + if role == 'cloudkms.cryptoKeyDecrypter': + list_members_role_others['cloudkms.cryptoKeyDecrypter'] = binding['members'] + + common_members1 = list( + set(list_members_role_admin).intersection(list_members_role_others['cloudkms.cryptoKeyEncrypterDecrypter'])) + common_members2 = list( + set(list_members_role_admin).intersection(list_members_role_others['cloudkms.cryptoKeyEncrypter'])) + common_members3 = list( + set(list_members_role_admin).intersection(list_members_role_others['cloudkms.cryptoKeyDecrypter'])) + if common_members1 or common_members2 or common_members3: + return False + return True diff --git a/ScoutSuite/providers/gcp/rules/findings/iam-role-account-separation-duties-is-false.json b/ScoutSuite/providers/gcp/rules/findings/iam-role-account-separation-duties-is-false.json new file mode 100644 index 000000000..011460b5e --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/iam-role-account-separation-duties-is-false.json @@ -0,0 +1,28 @@ +{ + "description": "Separation Of Duties Not Enforced For Service Account", + "rationale": "Separation of duties is the concept of ensuring that one individual does not have all necessary permissions to be able to complete a malicious action. In Cloud IAM-service accounts, this could be an action such as using a service account to access resources that user should not normally have access to. No user should have Service Account Admin and Service Account User roles assigned at the same time.", + "remediation": "From console:
      1. Go to IAM & Admin/IAM using https://console.cloud.google.com/iam-admin/iam.
      2. For any member having both Service Account Admin and Service account User roles granted/assigned, click the Delete Bin icon to remove either role from the member.
        Removal of a role should be done based on the business requirements.
      ", + "compliance": [ + { + "name": "CIS Google Cloud Platform Foundations", + "version": "1.1.0", + "reference": "1.8" + } + ], + "references": [ + "https://cloud.google.com/iam/docs/service-accounts", + "https://cloud.google.com/iam/docs/understanding-roles", + "https://cloud.google.com/iam/docs/granting-changing-revoking-access" + ], + "dashboard_name": "Project", + "path": "iam.projects.id.bindings_separation_duties.id", + "conditions": [ + "and", + [ + "iam.projects.id.bindings_separation_duties.id.account_separation_duties", + "false", + "" + ] + ], + "id_suffix": "account_separation_duties" +} diff --git a/ScoutSuite/providers/gcp/rules/findings/iam-role-kms-separation-duties-is-false.json b/ScoutSuite/providers/gcp/rules/findings/iam-role-kms-separation-duties-is-false.json new file mode 100644 index 000000000..f5b4df3ab --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/iam-role-kms-separation-duties-is-false.json @@ -0,0 +1,26 @@ +{ + "description": "Separation Of Duties Not Enforced For KMS", + "rationale": "Separation of duties is the concept of ensuring that one individual does not have all necessary permissions to be able to complete a malicious action. In Cloud KMS, this could be an action such as using a key to access and decrypt data a user should not normally have access to. Separation of duties is a business control typically used in larger organizations, meant to help avoid security or privacy incidents and errors. It is considered best practice.No user(s) should have Cloud KMS Admin and any of the Cloud KMS CryptoKey Encrypter/Decrypter, Cloud KMS CryptoKey Encrypter, Cloud KMS CryptoKey Decrypter roles assigned at the same time.", + "remediation": "From console:
      1. Go to IAM & Admin/IAM using https://console.cloud.google.com/iam-admin/iam.
      2. For any member having Cloud KMS Admin and any of the Cloud KMS CryptoKey Encrypter/Decrypter, Cloud KMS CryptoKey Encrypter, Cloud KMS CryptoKey Decrypter roles granted/assigned, click the Delete Bin icon to remove either role from the member.
        Removal of a role should be done based on the business requirements.
      ", + "compliance": [ + { + "name": "CIS Google Cloud Platform Foundations", + "version": "1.1.0", + "reference": "1.11" + } + ], + "references": [ + "https://cloud.google.com/kms/docs/separation-of-duties" + ], + "dashboard_name": "Project", + "path": "iam.projects.id.bindings_separation_duties.id", + "conditions": [ + "and", + [ + "iam.projects.id.bindings_separation_duties.id.kms_separation_duties", + "false", + "" + ] + ], + "id_suffix": "kms_separation_duties" +} diff --git a/ScoutSuite/providers/gcp/rules/rulesets/cis-1.1.0.json b/ScoutSuite/providers/gcp/rules/rulesets/cis-1.1.0.json index 6bc3215f3..2be8b9c7c 100644 --- a/ScoutSuite/providers/gcp/rules/rulesets/cis-1.1.0.json +++ b/ScoutSuite/providers/gcp/rules/rulesets/cis-1.1.0.json @@ -6,6 +6,18 @@ "enabled": true, "level": "warning" } + ], + "iam-role-account-separation-duties-is-false.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "iam-role-kms-separation-duties-is-false.json": [ + { + "enabled": true, + "level": "warning" + } ] } diff --git a/ScoutSuite/providers/gcp/rules/rulesets/default.json b/ScoutSuite/providers/gcp/rules/rulesets/default.json index 954074b01..f96622667 100755 --- a/ScoutSuite/providers/gcp/rules/rulesets/default.json +++ b/ScoutSuite/providers/gcp/rules/rulesets/default.json @@ -200,6 +200,18 @@ "level": "warning" } ], + "iam-role-account-separation-duties-is-false.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "iam-role-kms-separation-duties-is-false.json": [ + { + "enabled": true, + "level": "warning" + } + ], "kubernetesengine-basic-authentication-enabled.json": [ { "enabled": true, From e1b9e0ff368c1c071ae7f885c510a2afc1a6a071 Mon Sep 17 00:00:00 2001 From: Kevin Nguyen Date: Wed, 31 Mar 2021 09:30:54 -0400 Subject: [PATCH 112/193] GCP Network Benchmarks - 3.1 & 3.2 (#1214) * Finish GCP 3.1 * Benchmark 3.2 * Add legacy mode in partial --- ...es.computeengine.projects.id.networks.html | 1 + .../providers/gcp/resources/gce/networks.py | 5 +++ .../computeengine-network-default-in-use.json | 32 +++++++++++++++++++ .../computeengine-network-legacy-in-use.json | 27 ++++++++++++++++ .../gcp/rules/rulesets/cis-1.1.0.json | 12 +++++++ .../providers/gcp/rules/rulesets/default.json | 12 +++++++ 6 files changed, 89 insertions(+) create mode 100755 ScoutSuite/providers/gcp/rules/findings/computeengine-network-default-in-use.json create mode 100755 ScoutSuite/providers/gcp/rules/findings/computeengine-network-legacy-in-use.json diff --git a/ScoutSuite/output/data/html/partials/gcp/services.computeengine.projects.id.networks.html b/ScoutSuite/output/data/html/partials/gcp/services.computeengine.projects.id.networks.html index ccb8c486c..f1fdb6baa 100755 --- a/ScoutSuite/output/data/html/partials/gcp/services.computeengine.projects.id.networks.html +++ b/ScoutSuite/output/data/html/partials/gcp/services.computeengine.projects.id.networks.html @@ -11,6 +11,7 @@

      Information

      Project ID: {{project_id}}
      Description: {{description}}
      Creation Date: {{format_date creation_timestamp}}
      +
      Legacy Mode: {{ legacy_mode}}

      Firewall Rules diff --git a/ScoutSuite/providers/gcp/resources/gce/networks.py b/ScoutSuite/providers/gcp/resources/gce/networks.py index fbf120323..84a97afa4 100755 --- a/ScoutSuite/providers/gcp/resources/gce/networks.py +++ b/ScoutSuite/providers/gcp/resources/gce/networks.py @@ -18,6 +18,7 @@ def _parse_network(self, raw_network): network_dict['id'] = raw_network['id'] network_dict['project_id'] = raw_network['selfLink'].split('/')[-4] network_dict['name'] = raw_network['name'] + network_dict['description'] = self._get_description(raw_network) network_dict['creation_timestamp'] = raw_network['creationTimestamp'] network_dict['auto_subnet'] = raw_network.get('autoCreateSubnetworks', None) @@ -25,6 +26,10 @@ def _parse_network(self, raw_network): network_dict['network_url'] = raw_network['selfLink'] network_dict['subnetwork_urls'] = raw_network.get('subnetworks', None) + # Network is legacy if there is no subnets + network_dict['legacy_mode'] = True \ + if raw_network.get('subnetworks', None) is None or not raw_network.get('subnetworks', None) \ + else False return network_dict['id'], network_dict diff --git a/ScoutSuite/providers/gcp/rules/findings/computeengine-network-default-in-use.json b/ScoutSuite/providers/gcp/rules/findings/computeengine-network-default-in-use.json new file mode 100755 index 000000000..5f5aa85ea --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/computeengine-network-default-in-use.json @@ -0,0 +1,32 @@ +{ + "description": "Default Network should should be removed", + "rationale": "The default network has a preconfigured network configuration and automatically generates insecure firewall rules. These automatically created firewall rules do not get audit logged and cannot be configured to enable firewall rule logging.", + "remediation": "From Console:\n
        \n
      1. Go to VPC networks page by visiting:\n https://console.cloud.google.com/networking/networks/list\n
      2. \n
      3. Click the network named default
      4. \n
      5. On the network detail page, click EDIT
      6. \n
      7. Click DELETE VPC NETWORK
      8. \n
      9. If needed, create a new network to replace the default network
      10. \n
      ", + "compliance": [ + { + "name": "CIS Google Cloud Platform Foundations", + "version": "1.1.0", + "reference": "3.1" + } + ], + "dashboard_name": "Networks", + "path": "computeengine.projects.id.networks.id", + "references": [ + "https://cloud.google.com/compute/docs/networking#firewall_rules", + "https://cloud.google.com/compute/docs/reference/latest/networks/insert", + "https://cloud.google.com/compute/docs/reference/latest/networks/delete", + "https://cloud.google.com/vpc/docs/firewall-rules-logging", + "https://cloud.google.com/vpc/docs/vpc#default-network", + "https://cloud.google.com/sdk/gcloud/reference/compute/networks/delete" + ], + "conditions": [ + "and", + [ + "computeengine.projects.id.networks.id.name", + "equal", + "default" + ] + ], + "id_suffix": "name" + +} diff --git a/ScoutSuite/providers/gcp/rules/findings/computeengine-network-legacy-in-use.json b/ScoutSuite/providers/gcp/rules/findings/computeengine-network-legacy-in-use.json new file mode 100755 index 000000000..52b6cff89 --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/computeengine-network-legacy-in-use.json @@ -0,0 +1,27 @@ +{ + "description": "Legacy Network should be removed", + "rationale": "Legacy networks have a single network IPv4 prefix range and a single gateway IP address for the whole network. The network is global in scope and spans all cloud regions. Subnetworks cannot be created in a legacy network and are unable to switch from legacy to auto or custom subnet networks. Legacy networks can have an impact for high network traffic projects and are subject to a single point of contention or failure.", + "remediation": "For each Google Cloud Platform project,\n
        \n
      1. \n 1. Follow the documentation and create a non-legacy network suitable for the organization's requirements.\n
      2. \n
      3. Follow the documentation and delete the networks in the legacy mode.
      4. \n\n
      ", + "compliance": [ + { + "name": "CIS Google Cloud Platform Foundations", + "version": "1.1.0", + "reference": "3.2" + } + ], + "dashboard_name": "Networks", + "path": "computeengine.projects.id.networks.id", + "references": [ + "https://cloud.google.com/vpc/docs/using-legacy#creating_a_legacy_network", + "https://cloud.google.com/vpc/docs/using-legacy#deleting_a_legacy_network" + ], + "conditions": [ + "and", + [ + "computeengine.projects.id.networks.id.legacy_mode", + "true", + "" + ] + ], + "id_suffix": "legacy_mode" +} diff --git a/ScoutSuite/providers/gcp/rules/rulesets/cis-1.1.0.json b/ScoutSuite/providers/gcp/rules/rulesets/cis-1.1.0.json index 2be8b9c7c..aa1d19f54 100644 --- a/ScoutSuite/providers/gcp/rules/rulesets/cis-1.1.0.json +++ b/ScoutSuite/providers/gcp/rules/rulesets/cis-1.1.0.json @@ -7,6 +7,18 @@ "level": "warning" } ], + "computeengine-network-default-in-use.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "computeengine-network-legacy-in-use.json": [ + { + "enabled": true, + "level": "warning" + } + ], "iam-role-account-separation-duties-is-false.json": [ { "enabled": true, diff --git a/ScoutSuite/providers/gcp/rules/rulesets/default.json b/ScoutSuite/providers/gcp/rules/rulesets/default.json index f96622667..aa80a6a21 100755 --- a/ScoutSuite/providers/gcp/rules/rulesets/default.json +++ b/ScoutSuite/providers/gcp/rules/rulesets/default.json @@ -134,6 +134,18 @@ "level": "warning" } ], + "computeengine-network-default-in-use.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "computeengine-network-legacy-in-use.json": [ + { + "enabled": true, + "level": "warning" + } + ], "computeengine-old-disk-snapshot.json": [ { "enabled": true, From 29801cf7d6aadbc8c9926cfc54210db5ca4ec84f Mon Sep 17 00:00:00 2001 From: Kevin Nguyen Date: Wed, 31 Mar 2021 09:31:32 -0400 Subject: [PATCH 113/193] GCP Network -DNS zones CISv3.3 to 3.5 (#1223) * Implement dns facade for gcp * Create new partial for managed zones * Add json file to verify key strength * Correct error on partial compute * Check if api is enabled --- ...ervices.dns.projects.id.managed_zones.html | 44 ++++++++++++++ .../output/data/inc-scoutsuite/scoutsuite.js | 2 +- ScoutSuite/providers/gcp/facade/base.py | 4 ++ ScoutSuite/providers/gcp/facade/dns.py | 19 ++++++ ScoutSuite/providers/gcp/metadata.json | 10 ++++ .../providers/gcp/resources/dns/__init__.py | 0 .../providers/gcp/resources/dns/base.py | 8 +++ .../gcp/resources/dns/managed_zones.py | 59 +++++++++++++++++++ .../dns-zones-dnssec-not-enabled.json | 21 +++++++ ...s-zones-key-signing-key-using-rsasha1.json | 16 +++++ ...-zones-zone-signing-key-using-rsasha1.json | 16 +++++ .../gcp/rules/rulesets/cis-1.1.0.json | 20 ++++++- .../providers/gcp/rules/rulesets/default.json | 18 ++++++ ScoutSuite/providers/gcp/services.py | 3 + ScoutSuite/utils.py | 1 + 15 files changed, 239 insertions(+), 2 deletions(-) create mode 100755 ScoutSuite/output/data/html/partials/gcp/services.dns.projects.id.managed_zones.html create mode 100755 ScoutSuite/providers/gcp/facade/dns.py create mode 100644 ScoutSuite/providers/gcp/resources/dns/__init__.py create mode 100755 ScoutSuite/providers/gcp/resources/dns/base.py create mode 100755 ScoutSuite/providers/gcp/resources/dns/managed_zones.py create mode 100755 ScoutSuite/providers/gcp/rules/findings/dns-zones-dnssec-not-enabled.json create mode 100755 ScoutSuite/providers/gcp/rules/findings/dns-zones-key-signing-key-using-rsasha1.json create mode 100755 ScoutSuite/providers/gcp/rules/findings/dns-zones-zone-signing-key-using-rsasha1.json diff --git a/ScoutSuite/output/data/html/partials/gcp/services.dns.projects.id.managed_zones.html b/ScoutSuite/output/data/html/partials/gcp/services.dns.projects.id.managed_zones.html new file mode 100755 index 000000000..bf7ead99e --- /dev/null +++ b/ScoutSuite/output/data/html/partials/gcp/services.dns.projects.id.managed_zones.html @@ -0,0 +1,44 @@ + + + + + + + + + diff --git a/ScoutSuite/output/data/inc-scoutsuite/scoutsuite.js b/ScoutSuite/output/data/inc-scoutsuite/scoutsuite.js index b114fba6a..f373fcb23 100755 --- a/ScoutSuite/output/data/inc-scoutsuite/scoutsuite.js +++ b/ScoutSuite/output/data/inc-scoutsuite/scoutsuite.js @@ -1211,7 +1211,7 @@ function makeTitle(title) { return title.toString() } title = title.toLowerCase() - if (['acm', 'ec2', 'ecr', 'ecs', 'efs', 'eks', 'iam', 'kms', 'rds', 'sns', 'ses', 'sqs', 'vpc', 'elb', 'elbv2', 'emr'].indexOf(title) !== -1) { + if (['acm', 'ec2', 'ecr', 'ecs', 'efs', 'eks', 'iam', 'kms', 'rds', 'sns', 'ses', 'sqs', 'vpc', 'elb', 'elbv2', 'emr','dns'].indexOf(title) !== -1) { return title.toUpperCase() } else if (title === 'cloudtrail') { return 'CloudTrail' diff --git a/ScoutSuite/providers/gcp/facade/base.py b/ScoutSuite/providers/gcp/facade/base.py index 90b3d2bd9..3b984d822 100755 --- a/ScoutSuite/providers/gcp/facade/base.py +++ b/ScoutSuite/providers/gcp/facade/base.py @@ -7,6 +7,7 @@ from ScoutSuite.providers.gcp.facade.memorystoreredis import MemoryStoreRedisFacade from ScoutSuite.providers.gcp.facade.cloudstorage import CloudStorageFacade from ScoutSuite.providers.gcp.facade.gce import GCEFacade +from ScoutSuite.providers.gcp.facade.dns import DNSFacade from ScoutSuite.providers.gcp.facade.iam import IAMFacade from ScoutSuite.providers.gcp.facade.kms import KMSFacade from ScoutSuite.providers.gcp.facade.stackdriverlogging import StackdriverLoggingFacade @@ -34,6 +35,7 @@ def __init__(self, self.gce = GCEFacade() self.iam = IAMFacade() self.kms = KMSFacade() + self.dns = DNSFacade() self.stackdriverlogging = StackdriverLoggingFacade() self.stackdrivermonitoring = StackdriverMonitoringFacade() @@ -170,6 +172,8 @@ async def is_api_enabled(self, project_id, service): endpoint = 'monitoring' elif service == 'MemoryStore': endpoint = 'redis' + elif service =='DNS': + endpoint='dns' else: print_debug('Could not validate the state of the {} API for project \"{}\", ' 'including it in the execution'.format(format_service_name(service.lower()), project_id)) diff --git a/ScoutSuite/providers/gcp/facade/dns.py b/ScoutSuite/providers/gcp/facade/dns.py new file mode 100755 index 000000000..88cdfeb13 --- /dev/null +++ b/ScoutSuite/providers/gcp/facade/dns.py @@ -0,0 +1,19 @@ +from ScoutSuite.core.console import print_exception +from ScoutSuite.providers.gcp.facade.basefacade import GCPBaseFacade +from ScoutSuite.providers.gcp.facade.utils import GCPFacadeUtils +from ScoutSuite.providers.utils import run_concurrently + + +class DNSFacade(GCPBaseFacade): + def __init__(self): + super().__init__('dns', 'v1') + + async def get_zones(self, project_id): + try: + dns_client = self._get_client() + return await run_concurrently( + lambda: dns_client.managedZones().list(project=project_id).execute() + ) + except Exception as e: + print_exception(f'Failed to retrieve zones: {e}') + return [] diff --git a/ScoutSuite/providers/gcp/metadata.json b/ScoutSuite/providers/gcp/metadata.json index e3382af73..26e47387e 100755 --- a/ScoutSuite/providers/gcp/metadata.json +++ b/ScoutSuite/providers/gcp/metadata.json @@ -67,6 +67,15 @@ } } }, + "network": { + "dns": { + "resources": { + "managed_zones": { + "cols": 2, + "path": "services.dns.projects.id.managed_zones" + } + } + }}, "storage": { "cloudstorage": { "resources": { @@ -77,6 +86,7 @@ } } }, + "database": { "cloudsql": { "resources": { diff --git a/ScoutSuite/providers/gcp/resources/dns/__init__.py b/ScoutSuite/providers/gcp/resources/dns/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/ScoutSuite/providers/gcp/resources/dns/base.py b/ScoutSuite/providers/gcp/resources/dns/base.py new file mode 100755 index 000000000..779dd131d --- /dev/null +++ b/ScoutSuite/providers/gcp/resources/dns/base.py @@ -0,0 +1,8 @@ +from ScoutSuite.providers.gcp.resources.projects import Projects +from ScoutSuite.providers.gcp.resources.dns.managed_zones import ManagedZones + + +class DNS(Projects): + _children = [ + (ManagedZones, 'managed_zones') + ] diff --git a/ScoutSuite/providers/gcp/resources/dns/managed_zones.py b/ScoutSuite/providers/gcp/resources/dns/managed_zones.py new file mode 100755 index 000000000..bbfdfc1ed --- /dev/null +++ b/ScoutSuite/providers/gcp/resources/dns/managed_zones.py @@ -0,0 +1,59 @@ +from ScoutSuite.providers.base.resources.base import Resources +from ScoutSuite.providers.gcp.facade.base import GCPFacade + + +class ManagedZones(Resources): + def __init__(self, facade: GCPFacade, project_id: str): + super().__init__(facade) + self.project_id = project_id + + async def fetch_all(self): + raw_zones = await self.facade.dns.get_zones(self.project_id) + for raw_zone in raw_zones['managedZones']: + zone_id, zone = self._parse_zone(raw_zone) + self[zone_id] = zone + + def _parse_zone(self, raw_zone): + zone_dict = {} + zone_dict['id'] = raw_zone['id'] + zone_dict['name'] = raw_zone['name'] + zone_dict['description'] = self._get_description(raw_zone) + zone_dict['dns_name'] = raw_zone['dnsName'] + zone_dict['name_servers'] = raw_zone.get('nameServers', None) + zone_dict['visibility'] = raw_zone['visibility'] + zone_dict['creation_timestamp'] = raw_zone['creationTime'] + + dnssec_config = raw_zone.get('dnssecConfig',None) + if dnssec_config: + zone_dict['dnssec_enabled'] = True if dnssec_config['state'] == 'on' else False + zone_dict['dnssec_keys'] = self._get_keys(dnssec_config,zone_dict) + else: + zone_dict['dnssec_enabled'] = False + zone_dict['dnssec_keys'] = None + zone_dict['key_signing_algorithm'] = None + zone_dict['zone_signing_algorithm']=None + return zone_dict['id'], zone_dict + + def _get_description(self, raw_zone): + description = raw_zone.get('description') + return description if description else 'N/A' + + def _get_keys(self, dnssec_config,zone_dict): + raw_keys = dnssec_config.get('defaultKeySpecs', None) + if not raw_keys: + return None + key_dict = {} + for raw_key in raw_keys: + key_dict[raw_key['keyType']]={ + 'key_type': raw_key['keyType'], + 'key_algorithm': raw_key['algorithm'], + 'length': raw_key['keyLength'], + } + if raw_key['keyType'] == 'keySigning': + zone_dict['key_signing_algorithm'] = raw_key['algorithm'] + elif raw_key['keyType'] == 'zoneSigning': + zone_dict['zone_signing_algorithm'] = raw_key['algorithm'] + + + + return key_dict diff --git a/ScoutSuite/providers/gcp/rules/findings/dns-zones-dnssec-not-enabled.json b/ScoutSuite/providers/gcp/rules/findings/dns-zones-dnssec-not-enabled.json new file mode 100755 index 000000000..623d16f20 --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/dns-zones-dnssec-not-enabled.json @@ -0,0 +1,21 @@ +{ + "description": "DNSSEC is not enabled for Cloud DNS", + "rationale": "Domain Name System Security Extensions (DNSSEC) adds security to the DNS protocol by enabling DNS responses to be validated. Having a trustworthy DNS that translates a domain name like www.example.com into its associated IP address is an increasingly important building block of today’s web-based applications. Attackers can hijack this process of domain/IP lookup and redirect users to a malicious site through DNS hijacking and man-in-the-middle attacks. DNSSEC helps mitigate the risk of such attacks by cryptographically signing DNS records. As a result, it prevents attackers from issuing fake DNS responses that may misdirect browsers to nefarious websites.", + "remediation": "From Console:\n
        \n
      1. \n 1. Go to Cloud DNS by visiting https://console.cloud.google.com/net-services/dns/zones.\n
      2. \n
      3. \n 2. For each zone of Type Public, set DNSSEC to ON.\n
      4. \n\n
      ", + "dashboard_name": "Cloud DNS", + "path": "dns.projects.id.managed_zones.id", + "references": [ + "https://cloudplatform.googleblog.com/2017/11/DNSSEC-now-available-in-Cloud-DNS.html", + "https://cloud.google.com/dns/dnssec-config#enabling", + "https://cloud.google.com/dns/dnssec" + ], + "conditions": [ + "and", + [ + "dns.projects.id.managed_zones.id.dnssec_enabled", + "false", + "" + ] + ], + "id_suffix": "dnssec_enabled" +} diff --git a/ScoutSuite/providers/gcp/rules/findings/dns-zones-key-signing-key-using-rsasha1.json b/ScoutSuite/providers/gcp/rules/findings/dns-zones-key-signing-key-using-rsasha1.json new file mode 100755 index 000000000..36a1349d8 --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/dns-zones-key-signing-key-using-rsasha1.json @@ -0,0 +1,16 @@ +{ + "description": "DNSSEC key-signing key uses RSASHA1", + "rationale": "The algorithm used for key signing should be a recommended one and it should be strong. When enabling DNSSEC for a managed zone, or creating a managed zone with DNSSEC, the user can select the DNSSEC signing algorithms and the denial-of-existence type. Changing the DNSSEC settings is only effective for a managed zone if DNSSEC is not already enabled. If there is a need to change the settings for a managed zone where it has been enabled, turn DNSSEC off and then re-enable it with different settings.", + "remediation": "From Console:\n
        \n
      1. \n\n 1. If it is necessary to change the settings for a managed zone where it has been enabled, NSSEC must be turned\n off and re-enabled with different settings. To turn off DNSSEC, run the following command:\n
        \n \n gcloud dns managed-zones update ZONE_NAME --dnssec-state off\n \n
        \n
      2. \n
      3. \n 2. To update key-signing for a reported managed DNS Zone, run the following command:\n
        \n gcloud dns managed-zones update ZONE_NAME --dnssec-state on --ksk-algorithm KSK_ALGORITHM\n --ksk-key-length KSK_KEY_LENGTH --zsk-algorithm ZSK_ALGORITHM --zsk-key-length ZSK_KEY_LENGTH\n --denial-of-existence DENIAL_OF_EXISTENCE\n \n
        \n
      4. \n\n
      ", + "dashboard_name": "Cloud DNS", + "path": "dns.projects.id.managed_zones.id", + "references": ["https://cloud.google.com/dns/dnssec-advanced#advanced_signing_options"], + "conditions": [ + "and", + [ + "dns.projects.id.managed_zones.id.key_signing_algorithm", + "equal", + "rsasha1" + ] + ] +} diff --git a/ScoutSuite/providers/gcp/rules/findings/dns-zones-zone-signing-key-using-rsasha1.json b/ScoutSuite/providers/gcp/rules/findings/dns-zones-zone-signing-key-using-rsasha1.json new file mode 100755 index 000000000..bea647d68 --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/dns-zones-zone-signing-key-using-rsasha1.json @@ -0,0 +1,16 @@ +{ + "description": "DNSSEC zone-signing key uses RSASHA1", + "rationale": "The algorithm used for key signing should be a recommended one and it should be strong. When enabling DNSSEC for a managed zone, or creating a managed zone with DNSSEC, the user can select the DNSSEC signing algorithms and the denial-of-existence type. Changing the DNSSEC settings is only effective for a managed zone if DNSSEC is not already enabled. If there is a need to change the settings for a managed zone where it has been enabled, turn DNSSEC off and then re-enable it with different settings.", + "remediation": "From Console:\n
        \n
      1. \n\n 1. If it is necessary to change the settings for a managed zone where it has been enabled, NSSEC must be turned\n off and re-enabled with different settings. To turn off DNSSEC, run the following command:\n
        \n \n gcloud dns managed-zones update ZONE_NAME --dnssec-state off\n \n
        \n
      2. \n
      3. \n 2. To update key-signing for a reported managed DNS Zone, run the following command:\n
        \n gcloud dns managed-zones update ZONE_NAME --dnssec-state on --ksk-algorithm KSK_ALGORITHM\n --ksk-key-length KSK_KEY_LENGTH --zsk-algorithm ZSK_ALGORITHM --zsk-key-length ZSK_KEY_LENGTH\n --denial-of-existence DENIAL_OF_EXISTENCE\n \n
        \n
      4. \n\n
      ", + "dashboard_name": "Cloud DNS", + "path": "dns.projects.id.managed_zones.id", + "references": ["https://cloud.google.com/dns/dnssec-advanced#advanced_signing_options"], + "conditions": [ + "and", + [ + "dns.projects.id.managed_zones.id.zone_signing_algorithm", + "equal", + "rsasha1" + ] + ] +} diff --git a/ScoutSuite/providers/gcp/rules/rulesets/cis-1.1.0.json b/ScoutSuite/providers/gcp/rules/rulesets/cis-1.1.0.json index aa1d19f54..d9ef8ecc5 100644 --- a/ScoutSuite/providers/gcp/rules/rulesets/cis-1.1.0.json +++ b/ScoutSuite/providers/gcp/rules/rulesets/cis-1.1.0.json @@ -30,7 +30,25 @@ "enabled": true, "level": "warning" } - ] + ], + "dns-zones-dnssec-not-enabled.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "dns-zones-key-signing-key-using-rsasha1": [ + { + "enabled": true, + "level": "warning" + } + ], + "dns-zones-zone-signing-key-using-rsasha1": [ + { + "enabled": true, + "level": "warning" + } + ] } } \ No newline at end of file diff --git a/ScoutSuite/providers/gcp/rules/rulesets/default.json b/ScoutSuite/providers/gcp/rules/rulesets/default.json index aa80a6a21..0d7ce1c55 100755 --- a/ScoutSuite/providers/gcp/rules/rulesets/default.json +++ b/ScoutSuite/providers/gcp/rules/rulesets/default.json @@ -152,6 +152,24 @@ "level": "warning" } ], + "dns-zones-dnssec-not-enabled.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "dns-zones-key-signing-key-using-rsasha1.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "dns-zones-zone-signing-key-using-rsasha1.json": [ + { + "enabled": true, + "level": "warning" + } + ], "iam-gmail-accounts-used.json": [ { "enabled": true, diff --git a/ScoutSuite/providers/gcp/services.py b/ScoutSuite/providers/gcp/services.py index 470179681..9027b5abb 100755 --- a/ScoutSuite/providers/gcp/services.py +++ b/ScoutSuite/providers/gcp/services.py @@ -6,6 +6,8 @@ from ScoutSuite.providers.gcp.resources.gce.base import ComputeEngine from ScoutSuite.providers.gcp.resources.iam.base import IAM from ScoutSuite.providers.gcp.resources.kms.base import KMS +from ScoutSuite.providers.gcp.resources.dns.base import DNS + from ScoutSuite.providers.gcp.resources.stackdriverlogging.base import StackdriverLogging from ScoutSuite.providers.gcp.resources.stackdrivermonitoring.base import StackdriverMonitoring from ScoutSuite.providers.gcp.resources.gke.base import KubernetesEngine @@ -30,6 +32,7 @@ def __init__(self, credentials=None, default_project_id=None, self.stackdriverlogging = StackdriverLogging(facade) self.stackdrivermonitoring = StackdriverMonitoring(facade) self.kubernetesengine = KubernetesEngine(facade) + self.dns = DNS(facade) def _is_provider(self, provider_name): return provider_name == 'gcp' diff --git a/ScoutSuite/utils.py b/ScoutSuite/utils.py index ab48c82f6..64e49eba8 100755 --- a/ScoutSuite/utils.py +++ b/ScoutSuite/utils.py @@ -51,6 +51,7 @@ 'cloudstorage': 'Cloud Storage', 'cloudmemorystore': 'Cloud Memorystore', 'cloudsql': 'Cloud SQL', + 'dns': 'DNS', 'stackdriverlogging': 'Stackdriver Logging', 'stackdrivermonitoring': 'Stackdriver Monitoring', 'computeengine': 'Compute Engine', From e6385b9304b231ae26391032ba94b95e9c22d42b Mon Sep 17 00:00:00 2001 From: Andy Gu Date: Wed, 31 Mar 2021 16:15:07 -0400 Subject: [PATCH 114/193] vpc flow logs not enabled --- ...engine.projects.id.regions.id.subnetworks.html | 1 + .../providers/gcp/resources/gce/subnetworks.py | 8 ++++++++ .../computeengine-vpc-flow-logs-disabled.json | 15 +++++++++++++++ .../providers/gcp/rules/rulesets/default.json | 6 ++++++ 4 files changed, 30 insertions(+) create mode 100644 ScoutSuite/providers/gcp/rules/findings/computeengine-vpc-flow-logs-disabled.json diff --git a/ScoutSuite/output/data/html/partials/gcp/services.computeengine.projects.id.regions.id.subnetworks.html b/ScoutSuite/output/data/html/partials/gcp/services.computeengine.projects.id.regions.id.subnetworks.html index e9b231378..2f6cc509e 100755 --- a/ScoutSuite/output/data/html/partials/gcp/services.computeengine.projects.id.regions.id.subnetworks.html +++ b/ScoutSuite/output/data/html/partials/gcp/services.computeengine.projects.id.regions.id.subnetworks.html @@ -14,6 +14,7 @@

      Information

      IP Range: {{ip_range}}
      Gateway Address: {{gateway_address}}
      Private Google Access: {{convert_bool_to_enabled private_ip_google_access}}
      +
      VPC Flow Logs: {{flowlogs_enabled}}

      Compute Engine Instances diff --git a/ScoutSuite/providers/gcp/resources/gce/subnetworks.py b/ScoutSuite/providers/gcp/resources/gce/subnetworks.py index c25332585..175838be0 100755 --- a/ScoutSuite/providers/gcp/resources/gce/subnetworks.py +++ b/ScoutSuite/providers/gcp/resources/gce/subnetworks.py @@ -28,4 +28,12 @@ def _parse_subnetwork(self, raw_subnetwork): subnetwork_dict['subnetwork_url'] = raw_subnetwork['selfLink'] subnetwork_dict['network_url'] = raw_subnetwork['network'] + if 'logConfig' in raw_subnetwork: + subnetwork_dict['flowlogs_enabled'] = raw_subnetwork['logConfig']['enable'] + else: + # Set as UNKNOWN for now. For instance, some projects' + # default networks with flow logs enabled do not have a logConfig + # stanza in JSON output. + subnetwork_dict['flowlogs_enabled'] = "UNKNOWN" + return subnetwork_dict['id'], subnetwork_dict diff --git a/ScoutSuite/providers/gcp/rules/findings/computeengine-vpc-flow-logs-disabled.json b/ScoutSuite/providers/gcp/rules/findings/computeengine-vpc-flow-logs-disabled.json new file mode 100644 index 000000000..dbfc66d28 --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/computeengine-vpc-flow-logs-disabled.json @@ -0,0 +1,15 @@ +{ + "description": "VPC Flow Logs Not Enabled", + "rationale": "VPC Flow Logs were not enabled for this subnet. It is best practice to enable Flow Logs to some degree in order to have network visibility in the event of resource compromise, as well as source data for threat detections.", + "dashboard_name": "Subnetwork", + "path": "computeengine.projects.id.regions.id.subnetworks.id", + "conditions": [ + "and", + [ + "computeengine.projects.id.regions.id.subnetworks.id.flowlogs_enabled", + "false", + "" + ] + ], + "id_suffix": "name" +} diff --git a/ScoutSuite/providers/gcp/rules/rulesets/default.json b/ScoutSuite/providers/gcp/rules/rulesets/default.json index bd5117f0e..9883242cd 100755 --- a/ScoutSuite/providers/gcp/rules/rulesets/default.json +++ b/ScoutSuite/providers/gcp/rules/rulesets/default.json @@ -65,6 +65,12 @@ "level": "warning" } ], + "computeengine-vpc-flow-logs-disabled.json": [ + { + "enabled": true, + "level": "warning" + } + ], "computeengine-firewall-default-rule-in-use.json": [ { "enabled": true, From 1281906cdc4ba3068b28e4c2ea30497150dcffde Mon Sep 17 00:00:00 2001 From: Sophie Date: Mon, 5 Apr 2021 10:38:28 -0400 Subject: [PATCH 115/193] started rules 2.4to 2.11 in GCP --- .../gcp/resources/stackdriverlogging/base.py | 4 +- .../stackdriverlogging/logging_metrics.py | 63 +++++++++++++++++++ .../resources/stackdrivermonitoring/base.py | 4 +- .../monitoring_alert_policies.py | 24 +++++++ 4 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 ScoutSuite/providers/gcp/resources/stackdriverlogging/logging_metrics.py create mode 100644 ScoutSuite/providers/gcp/resources/stackdrivermonitoring/monitoring_alert_policies.py diff --git a/ScoutSuite/providers/gcp/resources/stackdriverlogging/base.py b/ScoutSuite/providers/gcp/resources/stackdriverlogging/base.py index 75e502c76..bd3aa0ca1 100755 --- a/ScoutSuite/providers/gcp/resources/stackdriverlogging/base.py +++ b/ScoutSuite/providers/gcp/resources/stackdriverlogging/base.py @@ -1,4 +1,5 @@ from ScoutSuite.providers.gcp.resources.projects import Projects +from ScoutSuite.providers.gcp.resources.stackdriverlogging.logging_metrics import LoggingMetrics from ScoutSuite.providers.gcp.resources.stackdriverlogging.sinks import Sinks from ScoutSuite.providers.gcp.resources.stackdriverlogging.metrics import Metrics @@ -6,5 +7,6 @@ class StackdriverLogging(Projects): _children = [ (Sinks, 'sinks'), - (Metrics, 'metrics') + (Metrics, 'metrics'), + (LoggingMetrics, 'logging_metrics') ] diff --git a/ScoutSuite/providers/gcp/resources/stackdriverlogging/logging_metrics.py b/ScoutSuite/providers/gcp/resources/stackdriverlogging/logging_metrics.py new file mode 100644 index 000000000..efd193ee9 --- /dev/null +++ b/ScoutSuite/providers/gcp/resources/stackdriverlogging/logging_metrics.py @@ -0,0 +1,63 @@ +from ScoutSuite.providers.base.resources.base import Resources +from ScoutSuite.providers.gcp.facade.base import GCPFacade + + +class LoggingMetrics(Resources): + def __init__(self, facade: GCPFacade, project_id: str): + super().__init__(facade) + self.project_id = project_id + + async def fetch_all(self): + raw_metrics = await self.facade.stackdriverlogging.get_metrics(self.project_id) + metric = self._parse_metric(raw_metrics) + self[self.project_id] = metric + + def _parse_metric(self, raw_metrics): + metric_dict = {} + metric_dict['project_ownership_assignments'] =\ + self._specific_filter_present(raw_metrics, '(protoPayload.serviceName="cloudresourcemanager.googleapis' + '.com") AND (ProjectOwnership OR projectOwnerInvitee) OR (' + 'protoPayload.serviceData.policyDelta.bindingDeltas.action' + '="REMOVE" AND ' + "protoPayload.serviceData.policyDelta.bindingDeltas.role" + '="roles/owner") OR (' + 'protoPayload.serviceData.policyDelta.bindingDeltas.action' + '="ADD" AND ' + 'protoPayload.serviceData.policyDelta.bindingDeltas.role' + '="roles/owner")') + metric_dict['audit_config_change'] = \ + self._specific_filter_present(raw_metrics, 'protoPayload.methodName="SetIamPolicy" AND ' + 'protoPayload.serviceData.policyDelta.auditConfigDeltas:*') + metric_dict['custom_role_change'] = \ + self._specific_filter_present(raw_metrics, 'resource.type="iam_role" AND protoPayload.methodName = ' + '"google.iam.admin.v1.CreateRole" OR ' + 'protoPayload.methodName="google.iam.admin.v1.DeleteRole" OR ' + 'protoPayload.methodName="google.iam.admin.v1.UpdateRole"') + metric_dict['vpc_network_firewall_rule_change'] = \ + self._specific_filter_present(raw_metrics, 'resource.type="gce_firewall_rule" AND ' + 'jsonPayload.event_subtype="compute.firewalls.patch" OR ' + 'jsonPayload.event_subtype="compute.firewalls.insert"') + metric_dict['vpc_network_route_change'] = \ + self._specific_filter_present(raw_metrics, 'resource.type="gce_route" AND ' + 'jsonPayload.event_subtype="compute.routes.delete" OR ' + 'jsonPayload.event_subtype="compute.routes.insert"') + metric_dict['vpc_network_change'] = \ + self._specific_filter_present(raw_metrics, 'resource.type=gce_network AND ' + 'jsonPayload.event_subtype="compute.networks.insert" OR ' + 'jsonPayload.event_subtype="compute.networks.patch" OR ' + 'jsonPayload.event_subtype="compute.networks.delete" OR ' + 'jsonPayload.event_subtype="compute.networks.removePeering" OR ' + 'jsonPayload.event_subtype="compute.networks.addPeering"') + metric_dict['cloud_storage_iam_permission_change'] = \ + self._specific_filter_present(raw_metrics, 'resource.type=gcs_bucket AND ' + 'protoPayload.methodName="storage.setIamPermissions"') + metric_dict['sql_instance_conf_change'] = \ + self._specific_filter_present(raw_metrics, 'protoPayload.methodName="cloudsql.instances.update"') + + return metric_dict + + def _specific_filter_present(self, raw_metrics, filter_value: str): + for metric in raw_metrics: + if metric.filter_ == filter_value: + return True + return False diff --git a/ScoutSuite/providers/gcp/resources/stackdrivermonitoring/base.py b/ScoutSuite/providers/gcp/resources/stackdrivermonitoring/base.py index b270238a6..3ba5c3207 100755 --- a/ScoutSuite/providers/gcp/resources/stackdrivermonitoring/base.py +++ b/ScoutSuite/providers/gcp/resources/stackdrivermonitoring/base.py @@ -1,4 +1,5 @@ from ScoutSuite.providers.gcp.resources.projects import Projects +from ScoutSuite.providers.gcp.resources.stackdrivermonitoring.monitoring_alert_policies import MonitoringAlertPolicies from ScoutSuite.providers.gcp.resources.stackdrivermonitoring.uptime_checks import UptimeChecks from ScoutSuite.providers.gcp.resources.stackdrivermonitoring.alert_policies import AlertPolicies @@ -6,5 +7,6 @@ class StackdriverMonitoring(Projects): _children = [ (UptimeChecks, 'uptime_checks'), - (AlertPolicies, 'alert_policies') + (AlertPolicies, 'alert_policies'), + (MonitoringAlertPolicies, 'monitoring_alert_policies') ] diff --git a/ScoutSuite/providers/gcp/resources/stackdrivermonitoring/monitoring_alert_policies.py b/ScoutSuite/providers/gcp/resources/stackdrivermonitoring/monitoring_alert_policies.py new file mode 100644 index 000000000..a4bca6f8c --- /dev/null +++ b/ScoutSuite/providers/gcp/resources/stackdrivermonitoring/monitoring_alert_policies.py @@ -0,0 +1,24 @@ +from ScoutSuite.providers.base.resources.base import Resources +from ScoutSuite.providers.gcp.facade.base import GCPFacade + + +class MonitoringAlertPolicies(Resources): + def __init__(self, facade: GCPFacade, project_id: str): + super().__init__(facade) + self.project_id = project_id + + async def fetch_all(self): + raw_alert_policies = await self.facade.stackdrivermonitoring.get_alert_policies(self.project_id) + alert_policy = self._parse_alert_policy(raw_alert_policies) + self[self.project_id] = alert_policy + + def _parse_alert_policy(self, raw_alert_policies): + alert_policy_dict = {} + return alert_policy_dict + + def _specific_alert_policy_present(self, alert_policies, alert_policy_value: str): + for alert_policy in alert_policies: + for condition in alert_policy.conditions._value: + if condition.condition_threshold.filter == alert_policy_value and alert_policy.enabled.value: + return True + return False From cd8e74d63bb264e6ea78ec094e1c4fbac429b921 Mon Sep 17 00:00:00 2001 From: Sophie Dorval <42855086+SophieDorval@users.noreply.github.com> Date: Tue, 6 Apr 2021 18:22:56 -0400 Subject: [PATCH 116/193] Enhancement/gcp sql 6.1.2 (#1225) * added rule 6.1.2 from gcp * fix issue with database flags * modified mysql to use only database instances Co-authored-by: Sophie --- ...rvices.cloudsql.projects.id.instances.html | 2 ++ .../resources/cloudsql/database_instances.py | 13 +++++++++ ...udsql-mysql-instances-local-infile-on.json | 28 +++++++++++++++++++ .../gcp/rules/rulesets/cis-1.1.0.json | 6 ++++ .../providers/gcp/rules/rulesets/default.json | 6 ++++ 5 files changed, 55 insertions(+) create mode 100644 ScoutSuite/providers/gcp/rules/findings/cloudsql-mysql-instances-local-infile-on.json diff --git a/ScoutSuite/output/data/html/partials/gcp/services.cloudsql.projects.id.instances.html b/ScoutSuite/output/data/html/partials/gcp/services.cloudsql.projects.id.instances.html index bf1d9095a..8137e5ce4 100755 --- a/ScoutSuite/output/data/html/partials/gcp/services.cloudsql.projects.id.instances.html +++ b/ScoutSuite/output/data/html/partials/gcp/services.cloudsql.projects.id.instances.html @@ -13,6 +13,8 @@

      Information

      SSL Required: {{convert_bool_to_enabled ssl_required}}
      Public IP Address: {{value_or_none public_ip}}
      Private IP Address: {{value_or_none private_ip}}
      +
      Local Infile Flag is Off: {{value_or_none local_infile_off}}
      + {{#if authorized_networks}}
      Authorized Networks:
        diff --git a/ScoutSuite/providers/gcp/resources/cloudsql/database_instances.py b/ScoutSuite/providers/gcp/resources/cloudsql/database_instances.py index ca9a5e4cc..035010771 100755 --- a/ScoutSuite/providers/gcp/resources/cloudsql/database_instances.py +++ b/ScoutSuite/providers/gcp/resources/cloudsql/database_instances.py @@ -39,6 +39,11 @@ def _parse_instance(self, raw_instance): instance_dict['ssl_required'] = self._is_ssl_required(raw_instance) instance_dict['authorized_networks'] = raw_instance['settings']['ipConfiguration']['authorizedNetworks'] + if raw_instance['settings'].get('databaseFlags', None): + instance_dict['local_infile_off'] = self._mysql_local_infile_flag_off(raw_instance) + else: + instance_dict['local_infile_off'] = True + # check if is or has a failover replica instance_dict['has_failover_replica'] = raw_instance.get('failoverReplica', []) != [] instance_dict['is_failover_replica'] = raw_instance.get('masterInstanceName', '') != '' @@ -73,3 +78,11 @@ def _get_last_backup_timestamp(self, backups): last_backup_id = max(backups.keys(), key=( lambda k: backups[k]['creation_timestamp'])) return backups[last_backup_id]['creation_timestamp'] + + + def _mysql_local_infile_flag_off(self, raw_instance): + if 'MYSQL' in raw_instance['databaseVersion']: + for flag in raw_instance['settings']['databaseFlags']: + if flag['name'] == 'local_infile' and flag['value'] == 'on': + return False + return True diff --git a/ScoutSuite/providers/gcp/rules/findings/cloudsql-mysql-instances-local-infile-on.json b/ScoutSuite/providers/gcp/rules/findings/cloudsql-mysql-instances-local-infile-on.json new file mode 100644 index 000000000..e59522fd5 --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/cloudsql-mysql-instances-local-infile-on.json @@ -0,0 +1,28 @@ +{ + "description": "Local Infile Database Flag For MySQL Instance Is On", + "rationale": "The local_infile flag controls the server-side LOCAL capability for LOAD DATA statements. Depending on the local_infile setting, the server refuses or permits local data loading by clients that have LOCAL enabled on the client side.To explicitly cause the server to refuse LOAD DATA LOCAL statements (regardless of how client programs and libraries are configured at build time or runtime), start mysqld with local_infile disabled. local_infile can also be set at runtime.", + "remediation": "From console:
        1. Go to the Cloud SQL Instances page in the Google Cloud Console by visiting https://console.cloud.google.com/sql/instances.
        2. Select the MySQL instance where the database flag needs to be enabled.
        3. Click Edit
        4. Scroll down to the Flags section.
        5. To set a flag that has not been set on the instance before, click Add item, choose the flag local_infile from the drop-down menu, and set its value to off.
        6. Click Save
        7. Confirm the changes under Flags on the Overview page.
        ", + "compliance": [ + { + "name": "CIS Google Cloud Platform Foundations", + "version": "1.1.0", + "reference": "6.1.2" + } + ], + "references": [ + "https://cloud.google.com/sql/docs/mysql/flags", + "https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_local_infile", + "https://dev.mysql.com/doc/refman/5.7/en/load-data-local.html" + ], + "dashboard_name": "Instances", + "path": "cloudsql.projects.id.instances.id", + "conditions": [ + "and", + [ + "cloudsql.projects.id.instances.id.local_infile_off", + "false", + "" + ] + ], + "id_suffix": "local_infile_off" +} diff --git a/ScoutSuite/providers/gcp/rules/rulesets/cis-1.1.0.json b/ScoutSuite/providers/gcp/rules/rulesets/cis-1.1.0.json index d9ef8ecc5..16d76c707 100644 --- a/ScoutSuite/providers/gcp/rules/rulesets/cis-1.1.0.json +++ b/ScoutSuite/providers/gcp/rules/rulesets/cis-1.1.0.json @@ -1,6 +1,12 @@ { "about": "This ruleset attempts to cover as many recommendations from the CIS Google Cloud Platform Foundation v1.1.0.", "rules": { + "cloudsql-mysql-instances-local-infile-on.json": [ + { + "enabled": true, + "level": "warning" + } + ], "cloudstorage-uniform-bucket-level-access-disabled.json": [ { "enabled": true, diff --git a/ScoutSuite/providers/gcp/rules/rulesets/default.json b/ScoutSuite/providers/gcp/rules/rulesets/default.json index 0d7ce1c55..35d118e41 100755 --- a/ScoutSuite/providers/gcp/rules/rulesets/default.json +++ b/ScoutSuite/providers/gcp/rules/rulesets/default.json @@ -37,6 +37,12 @@ "level": "warning" } ], + "cloudsql-mysql-instances-local-infile-on.json": [ + { + "enabled": true, + "level": "warning" + } + ], "cloudstorage-bucket-member.json": [ { "args": [ From 1944df3789ad5fb27e5f9f3876eaeb00ca8c304d Mon Sep 17 00:00:00 2001 From: Sophie Dorval <42855086+SophieDorval@users.noreply.github.com> Date: Wed, 7 Apr 2021 17:25:31 -0400 Subject: [PATCH 117/193] Enhancement/gcp iam 1.9 1.10 (#1224) * added rules 1.9 and 1.10 og gcp cis * added rules to cis-1.1.0.json * fix issues Co-authored-by: Sophie --- .../services.kms.projects.id.keyrings.html | 25 +++++++++- ScoutSuite/providers/gcp/facade/kms.py | 12 +++++ .../providers/gcp/resources/kms/keys.py | 25 +++++++++- .../providers/gcp/resources/kms/kms_policy.py | 41 ++++++++++++++++ ...okeys-anonymously-publicly-accessible.json | 30 ++++++++++++ .../kms-encryption-keys-not-rotated.json | 49 +++++++++++++++++++ .../gcp/rules/rulesets/cis-1.1.0.json | 12 +++++ .../providers/gcp/rules/rulesets/default.json | 12 +++++ 8 files changed, 203 insertions(+), 3 deletions(-) create mode 100644 ScoutSuite/providers/gcp/resources/kms/kms_policy.py create mode 100644 ScoutSuite/providers/gcp/rules/findings/kms-cryptokeys-anonymously-publicly-accessible.json create mode 100644 ScoutSuite/providers/gcp/rules/findings/kms-encryption-keys-not-rotated.json diff --git a/ScoutSuite/output/data/html/partials/gcp/services.kms.projects.id.keyrings.html b/ScoutSuite/output/data/html/partials/gcp/services.kms.projects.id.keyrings.html index 0451198eb..902811edd 100755 --- a/ScoutSuite/output/data/html/partials/gcp/services.kms.projects.id.keyrings.html +++ b/ScoutSuite/output/data/html/partials/gcp/services.kms.projects.id.keyrings.html @@ -17,20 +17,43 @@

        state: {{state}} +
      • State: {{state}}
      • Protection Level: {{protection_level}}
      • Algorithm: {{algorithm}}
      • Purpose: {{purpose}}
      • Creation Date: {{format_date creation_datetime}}
      • Rotation Period: {{value_or_none rotation_period}}
      • Next Rotation Date: {{value_or_none next_rotation_datetime}}
      • +
      • Days Until Next Rotation: {{value_or_none next_rotation_time_days}}
      • +
      • Bindings
      • +
          + {{#each kms_iam_policy}} +
        • {{name}}
        • +
            +
          • Title: {{title}}
          • +
          • Description: {{value_or_none description}}
          • +
          • Custom Role: {{custom_role}}
          • +
          • Not anonymously or publicly accessible: {{anonymous_public_accessible}}
          • +
          + + {{else}} +
        • None
        • + {{/each}} +

      + +
      + + + {{else}}
    • None
    • {{/each}}

    + + + + + + + + diff --git a/ScoutSuite/output/data/html/partials/gcp/services.stackdrivermonitoring.projects.id.monitoring_alert_policies.html b/ScoutSuite/output/data/html/partials/gcp/services.stackdrivermonitoring.projects.id.monitoring_alert_policies.html new file mode 100644 index 000000000..7acdcb9cc --- /dev/null +++ b/ScoutSuite/output/data/html/partials/gcp/services.stackdrivermonitoring.projects.id.monitoring_alert_policies.html @@ -0,0 +1,30 @@ + + + + + + + + diff --git a/ScoutSuite/providers/gcp/metadata.json b/ScoutSuite/providers/gcp/metadata.json index 26e47387e..d62bcfaef 100755 --- a/ScoutSuite/providers/gcp/metadata.json +++ b/ScoutSuite/providers/gcp/metadata.json @@ -115,6 +115,10 @@ "metrics": { "cols": 2, "path": "services.stackdriverlogging.projects.id.metrics" + }, + "logging_metrics": { + "cols": 2, + "path": "services.stackdriverlogging.projects.id.logging_metrics" } } }, @@ -127,6 +131,10 @@ "alert_policies": { "cols": 2, "path": "services.stackdrivermonitoring.projects.id.alert_policies" + }, + "monitoring_alert_policies": { + "cols": 2, + "path": "services.stackdrivermonitoring.projects.id.monitoring_alert_policies" } } } diff --git a/ScoutSuite/providers/gcp/resources/stackdrivermonitoring/monitoring_alert_policies.py b/ScoutSuite/providers/gcp/resources/stackdrivermonitoring/monitoring_alert_policies.py index a4bca6f8c..7c03ca07b 100644 --- a/ScoutSuite/providers/gcp/resources/stackdrivermonitoring/monitoring_alert_policies.py +++ b/ScoutSuite/providers/gcp/resources/stackdrivermonitoring/monitoring_alert_policies.py @@ -14,11 +14,22 @@ async def fetch_all(self): def _parse_alert_policy(self, raw_alert_policies): alert_policy_dict = {} + alert_policy_dict['project_ownership_assignments'] = \ + self._specific_alert_policy_present(raw_alert_policies) + alert_policy_dict['audit_config_change'] = self._specific_alert_policy_present(raw_alert_policies) + alert_policy_dict['custom_role_change'] = self._specific_alert_policy_present(raw_alert_policies) + alert_policy_dict['vpc_network_firewall_rule_change'] = self._specific_alert_policy_present(raw_alert_policies) + alert_policy_dict['vpc_network_route_change'] = self._specific_alert_policy_present(raw_alert_policies) + alert_policy_dict['vpc_network_change'] = self._specific_alert_policy_present(raw_alert_policies) + alert_policy_dict['cloud_storage_iam_permission_change'] = \ + self._specific_alert_policy_present(raw_alert_policies) + alert_policy_dict['sql_instance_conf_change'] = self._specific_alert_policy_present(raw_alert_policies) return alert_policy_dict - def _specific_alert_policy_present(self, alert_policies, alert_policy_value: str): + def _specific_alert_policy_present(self, alert_policies): for alert_policy in alert_policies: - for condition in alert_policy.conditions._value: - if condition.condition_threshold.filter == alert_policy_value and alert_policy.enabled.value: + for condition in alert_policy.conditions._values: + if condition.condition_threshold.filter == 'metric.type=\"logging.googleapis.com/user/\"' and alert_policy.enabled.value: return True return False diff --git a/ScoutSuite/providers/gcp/rules/findings/stackdriverlogging-metric-filter-does-not-exist-audit-config-changes.json b/ScoutSuite/providers/gcp/rules/findings/stackdriverlogging-metric-filter-does-not-exist-audit-config-changes.json new file mode 100644 index 000000000..cf8a2c41d --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/stackdriverlogging-metric-filter-does-not-exist-audit-config-changes.json @@ -0,0 +1,30 @@ +{ + "description": "Log Metric Filter Doesn't Exist For Audit Configuration Changes", + "rationale": "Configuring the metric filter and alerts for audit configuration changes ensures the recommended state of audit configuration is maintained so that all activities in the project are audit-able at any point in time.", + "remediation":"From console:
    1. Go to Logging/Logs by visiting https://console.cloud.google.com/logs/metrics and click \"CREATE METRIC\".
    2. Click the down arrow symbol on the Filter Bar at the rightmost corner and select Convert to Advanced Filter.
    3. Clear any text and add:
      protoPayload.methodName=\"SetIamPolicy\" AND protoPayload.serviceData.policyDelta.auditConfigDeltas:*
    4. Click Submit Filter. The logs display based on the filter text entered by the user.
    5. In the Metric Editor menu on the right,fill out the name field. Set Units to 1(default) and the Type to Counter. This ensures that the log metric counts the number of log entries matching the advanced logs query.
    6. Click CreateMetric.
    ", + "compliance": [ + { + "name": "CIS Google Cloud Platform Foundations", + "version": "1.1.0", + "reference": "2.5" + } + ], + "references": [ + "https://cloud.google.com/logging/docs/logs-based-metrics/", + "https://cloud.google.com/monitoring/custom-metrics/", + "https://cloud.google.com/monitoring/alerts/", + "https://cloud.google.com/logging/docs/reference/tools/gcloud-logging", + "https://cloud.google.com/logging/docs/audit/configure-data-access#getiampolicy-setiampolicy" + ], + "dashboard_name": "Logging Configurations", + "path": "stackdriverlogging.projects.id.logging_metrics.id", + "conditions": [ + "and", + [ + "stackdriverlogging.projects.id.logging_metrics.id.audit_config_change", + "false", + "" + ] + ], + "id_suffix": "audit_config_change" +} diff --git a/ScoutSuite/providers/gcp/rules/findings/stackdriverlogging-metric-filter-does-not-exist-cloud-storage-iam-permission-changes.json b/ScoutSuite/providers/gcp/rules/findings/stackdriverlogging-metric-filter-does-not-exist-cloud-storage-iam-permission-changes.json new file mode 100644 index 000000000..59da64811 --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/stackdriverlogging-metric-filter-does-not-exist-cloud-storage-iam-permission-changes.json @@ -0,0 +1,31 @@ +{ + "description": "Log Metric Filter Doesn't Exist For Cloud Storage IAM Permission Changes", + "rationale": "Monitoring changes to cloud storage bucket permissions may reduce the time needed to detect and correct permissions on sensitive cloud storage buckets and objects inside the bucket.", + "remediation":"From console:
    1. Go to Logging/Logs by visiting https://console.cloud.google.com/logs/metrics and click \"CREATE METRIC\".
    2. Click the down arrow symbol on the Filter Bar at the rightmost corner and select Convert to Advanced Filter.
    3. Clear any text and add:
      resource.type=gcs_bucket AND protoPayload.methodName=\"storage.setIamPermissions\"
    4. Click Submit Filter. The logs display based on the filter text entered by the user.
    5. In the Metric Editor menu on the right,fill out the name field. Set Units to 1(default) and the Type to Counter. This ensures that the log metric counts the number of log entries matching the advanced logs query.
    6. Click CreateMetric.
    ", + "compliance": [ + { + "name": "CIS Google Cloud Platform Foundations", + "version": "1.1.0", + "reference": "2.10" + } + ], + "references": [ + "https://cloud.google.com/logging/docs/logs-based-metrics/", + "https://cloud.google.com/monitoring/custom-metrics/", + "https://cloud.google.com/monitoring/alerts/", + "https://cloud.google.com/logging/docs/reference/tools/gcloud-logging", + "https://cloud.google.com/storage/docs", + "https://cloud.google.com/storage/docs/access-control/iam-roles" + ], + "dashboard_name": "Logging Configurations", + "path": "stackdriverlogging.projects.id.logging_metrics.id", + "conditions": [ + "and", + [ + "stackdriverlogging.projects.id.logging_metrics.id.cloud_storage_iam_permission_change", + "false", + "" + ] + ], + "id_suffix": "cloud_storage_iam_permission_change" +} diff --git a/ScoutSuite/providers/gcp/rules/findings/stackdriverlogging-metric-filter-does-not-exist-custom-role-changes.json b/ScoutSuite/providers/gcp/rules/findings/stackdriverlogging-metric-filter-does-not-exist-custom-role-changes.json new file mode 100644 index 000000000..99bc09582 --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/stackdriverlogging-metric-filter-does-not-exist-custom-role-changes.json @@ -0,0 +1,30 @@ +{ + "description": "Log Metric Filter Doesn't Exist For Custom Role Changes", + "rationale": "Google Cloud IAM provides predefined roles that give granular access to specific Google Cloud Platform resources and prevent unwanted access to other resources. However, to cater to organization-specific needs, Cloud IAM also provides the ability to create custom roles. Project owners and administrators with the Organization Role Administrator role or the IAM Role Administrator role can create custom roles. Monitoring role creation, deletion and updating activities will help in identifying any over-privileged role at early stages.", + "remediation":"From console:
    1. Go to Logging/Logs by visiting https://console.cloud.google.com/logs/metrics and click \"CREATE METRIC\".
    2. Click the down arrow symbol on the Filter Bar at the rightmost corner and select Convert to Advanced Filter.
    3. Clear any text and add:
      resource.type=\"iam_role\" AND protoPayload.methodName = \"google.iam.admin.v1.CreateRole\" OR protoPayload.methodName=\"google.iam.admin.v1.DeleteRole\" OR protoPayload.methodName=\"google.iam.admin.v1.UpdateRole\"
    4. Click Submit Filter. The logs display based on the filter text entered by the user.
    5. In the Metric Editor menu on the right,fill out the name field. Set Units to 1(default) and the Type to Counter. This ensures that the log metric counts the number of log entries matching the advanced logs query.
    6. Click CreateMetric.
    ", + "compliance": [ + { + "name": "CIS Google Cloud Platform Foundations", + "version": "1.1.0", + "reference": "2.6" + } + ], + "references": [ + "https://cloud.google.com/logging/docs/logs-based-metrics/", + "https://cloud.google.com/monitoring/custom-metrics/", + "https://cloud.google.com/monitoring/alerts/", + "https://cloud.google.com/logging/docs/reference/tools/gcloud-logging", + "https://cloud.google.com/iam/docs/understanding-custom-roles" + ], + "dashboard_name": "Logging Configurations", + "path": "stackdriverlogging.projects.id.logging_metrics.id", + "conditions": [ + "and", + [ + "stackdriverlogging.projects.id.logging_metrics.id.custom_role_change", + "false", + "" + ] + ], + "id_suffix": "custom_role_change" +} diff --git a/ScoutSuite/providers/gcp/rules/findings/stackdriverlogging-metric-filter-does-not-exist-project-ownership-assignment.json b/ScoutSuite/providers/gcp/rules/findings/stackdriverlogging-metric-filter-does-not-exist-project-ownership-assignment.json new file mode 100644 index 000000000..d87d2f876 --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/stackdriverlogging-metric-filter-does-not-exist-project-ownership-assignment.json @@ -0,0 +1,29 @@ +{ + "description": "Log Metric Filter Doesn't Exist For Project Ownership Assignments/Changes", + "rationale": "Project ownership has the highest level of privileges on a project. To avoid misuse of project resources, the project ownership assignment/change actions mentioned above should be monitored and alerted to concerned recipients.", + "remediation":"From console:
    1. Go to Logging/Logs by visiting https://console.cloud.google.com/logs/metrics and click \"CREATE METRIC\".
    2. Click the down arrow symbol on the Filter Bar at the rightmost corner and select Convert to Advanced Filter.
    3. Clear any text and add:
      (protoPayload.serviceName=\"cloudresourcemanager.googleapis.com\") AND (ProjectOwnership OR projectOwnerInvitee) OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"REMOVE\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\") OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"ADD\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\")
    4. Click Submit Filter. The logs display based on the filter text entered by the user.
    5. In the Metric Editor menu on the right,fill out the name field. Set Units to 1(default) and the Type to Counter. This ensures that the log metric counts the number of log entries matching the advanced logs query.
    6. Click CreateMetric.
    ", + "compliance": [ + { + "name": "CIS Google Cloud Platform Foundations", + "version": "1.1.0", + "reference": "2.4" + } + ], + "references": [ + "https://cloud.google.com/logging/docs/logs-based-metrics/", + "https://cloud.google.com/monitoring/custom-metrics/", + "https://cloud.google.com/monitoring/alerts/", + "https://cloud.google.com/logging/docs/reference/tools/gcloud-logging" + ], + "dashboard_name": "Logging Configurations", + "path": "stackdriverlogging.projects.id.logging_metrics.id", + "conditions": [ + "and", + [ + "stackdriverlogging.projects.id.logging_metrics.id.project_ownership_assignments", + "false", + "" + ] + ], + "id_suffix": "project_ownership_assignments" +} diff --git a/ScoutSuite/providers/gcp/rules/findings/stackdriverlogging-metric-filter-does-not-exist-sql-instance-config-changes.json b/ScoutSuite/providers/gcp/rules/findings/stackdriverlogging-metric-filter-does-not-exist-sql-instance-config-changes.json new file mode 100644 index 000000000..8e49b8e4c --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/stackdriverlogging-metric-filter-does-not-exist-sql-instance-config-changes.json @@ -0,0 +1,33 @@ +{ + "description": "Log Metric Filter Doesn't Exist For SQL Instance Configuration Changes", + "rationale": "Monitoring changes to SQL instance configuration changes may reduce the time needed to detect and correct misconfigurations done on the SQL server.", + "remediation":"From console:
    1. Go to Logging/Logs by visiting https://console.cloud.google.com/logs/metrics and click \"CREATE METRIC\".
    2. Click the down arrow symbol on the Filter Bar at the rightmost corner and select Convert to Advanced Filter.
    3. Clear any text and add:
      protoPayload.methodName=\"cloudsql.instances.update\"
    4. Click Submit Filter. The logs display based on the filter text entered by the user.
    5. In the Metric Editor menu on the right,fill out the name field. Set Units to 1(default) and the Type to Counter. This ensures that the log metric counts the number of log entries matching the advanced logs query.
    6. Click CreateMetric.
    ", + "compliance": [ + { + "name": "CIS Google Cloud Platform Foundations", + "version": "1.1.0", + "reference": "2.11" + } + ], + "references": [ + "https://cloud.google.com/logging/docs/logs-based-metrics/", + "https://cloud.google.com/monitoring/custom-metrics/", + "https://cloud.google.com/monitoring/alerts/", + "https://cloud.google.com/logging/docs/reference/tools/gcloud-logging", + "https://cloud.google.com/storage/docs", + "https://cloud.google.com/sql/docs/", + "https://cloud.google.com/sql/docs/mysql/", + "https://cloud.google.com/sql/docs/postgres/" + ], + "dashboard_name": "Logging Configurations", + "path": "stackdriverlogging.projects.id.logging_metrics.id", + "conditions": [ + "and", + [ + "stackdriverlogging.projects.id.logging_metrics.id.sql_instance_conf_change", + "false", + "" + ] + ], + "id_suffix": "sql_instance_conf_change" +} diff --git a/ScoutSuite/providers/gcp/rules/findings/stackdriverlogging-metric-filter-does-not-exist-vpc-network-changes.json b/ScoutSuite/providers/gcp/rules/findings/stackdriverlogging-metric-filter-does-not-exist-vpc-network-changes.json new file mode 100644 index 000000000..82617e411 --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/stackdriverlogging-metric-filter-does-not-exist-vpc-network-changes.json @@ -0,0 +1,30 @@ +{ + "description": "Log Metric Filter Doesn't Exist For VPC Network Changes", + "rationale": "It is possible to have more than one VPC within a project. In addition, it is also possible to create a peer connection between two VPCs enablingnetwork traffic to route between VPCs.Monitoring changes to a VPC will help ensure VPC traffic flow is not getting impacted.", + "remediation":"From console:
    1. Go to Logging/Logs by visiting https://console.cloud.google.com/logs/metrics and click \"CREATE METRIC\".
    2. Click the down arrow symbol on the Filter Bar at the rightmost corner and select Convert to Advanced Filter.
    3. Clear any text and add:
      resource.type=gce_network AND jsonPayload.event_subtype=\"compute.networks.insert\" \n85| P a g eOR jsonPayload.event_subtype=\"compute.networks.patch\" OR jsonPayload.event_subtype=\"compute.networks.delete\" OR jsonPayload.event_subtype=\"compute.networks.removePeering\" OR jsonPayload.event_subtype=\"compute.networks.addPeering\"
    4. Click Submit Filter. The logs display based on the filter text entered by the user.
    5. In the Metric Editor menu on the right,fill out the name field. Set Units to 1(default) and the Type to Counter. This ensures that the log metric counts the number of log entries matching the advanced logs query.
    6. Click CreateMetric.
    ", + "compliance": [ + { + "name": "CIS Google Cloud Platform Foundations", + "version": "1.1.0", + "reference": "2.9" + } + ], + "references": [ + "https://cloud.google.com/logging/docs/logs-based-metrics/", + "https://cloud.google.com/monitoring/custom-metrics/", + "https://cloud.google.com/monitoring/alerts/", + "https://cloud.google.com/logging/docs/reference/tools/gcloud-logging", + "https://cloud.google.com/vpc/docs/overview" + ], + "dashboard_name": "Logging Configurations", + "path": "stackdriverlogging.projects.id.logging_metrics.id", + "conditions": [ + "and", + [ + "stackdriverlogging.projects.id.logging_metrics.id.vpc_network_change", + "false", + "" + ] + ], + "id_suffix": "vpc_network_change" +} diff --git a/ScoutSuite/providers/gcp/rules/findings/stackdriverlogging-metric-filter-does-not-exist-vpc-network-firewall-rule-changes.json b/ScoutSuite/providers/gcp/rules/findings/stackdriverlogging-metric-filter-does-not-exist-vpc-network-firewall-rule-changes.json new file mode 100644 index 000000000..91d86b23c --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/stackdriverlogging-metric-filter-does-not-exist-vpc-network-firewall-rule-changes.json @@ -0,0 +1,30 @@ +{ + "description": "Log Metric Filter Doesn't Exist For VPC Network Firewall Rule Changes", + "rationale": "Monitoring for Create or Update Firewall rule events gives insight to network access changes and may reduce the time it takes to detect suspicious activity.", + "remediation":"From console:
    1. Go to Logging/Logs by visiting https://console.cloud.google.com/logs/metrics and click \"CREATE METRIC\".
    2. Click the down arrow symbol on the Filter Bar at the rightmost corner and select Convert to Advanced Filter.
    3. Clear any text and add:
      resource.type=\"gce_firewall_rule\" AND jsonPayload.event_subtype=\"compute.firewalls.patch\" OR jsonPayload.event_subtype=\"compute.firewalls.insert\"
    4. Click Submit Filter. The logs display based on the filter text entered by the user.
    5. In the Metric Editor menu on the right,fill out the name field. Set Units to 1(default) and the Type to Counter. This ensures that the log metric counts the number of log entries matching the advanced logs query.
    6. Click CreateMetric.
    ", + "compliance": [ + { + "name": "CIS Google Cloud Platform Foundations", + "version": "1.1.0", + "reference": "2.7" + } + ], + "references": [ + "https://cloud.google.com/logging/docs/logs-based-metrics/", + "https://cloud.google.com/monitoring/custom-metrics/", + "https://cloud.google.com/monitoring/alerts/", + "https://cloud.google.com/logging/docs/reference/tools/gcloud-logging", + "https://cloud.google.com/vpc/docs/firewalls" + ], + "dashboard_name": "Logging Configurations", + "path": "stackdriverlogging.projects.id.logging_metrics.id", + "conditions": [ + "and", + [ + "stackdriverlogging.projects.id.logging_metrics.id.vpc_network_firewall_rule_change", + "false", + "" + ] + ], + "id_suffix": "vpc_network_firewall_rule_change" +} diff --git a/ScoutSuite/providers/gcp/rules/findings/stackdriverlogging-metric-filter-does-not-exist-vpc-network-route-changes.json b/ScoutSuite/providers/gcp/rules/findings/stackdriverlogging-metric-filter-does-not-exist-vpc-network-route-changes.json new file mode 100644 index 000000000..67297b833 --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/stackdriverlogging-metric-filter-does-not-exist-vpc-network-route-changes.json @@ -0,0 +1,30 @@ +{ + "description": "Log Metric Filter Doesn't Exist For VPC Network Route Changes", + "rationale": "Google Cloud Platform (GCP) routes define the paths network traffic takes from a VM instance to another destination. The other destination can be inside the organization VPC network (such as another VM) or outside of it. Every route consists of a destination and a next hop. Traffic whose destination IP is within the destination range is sent to the next hop for delivery.Monitoring changes to route tables will help ensure that all VPC traffic flows through an expected path.", + "remediation":"From console:
    1. Go to Logging/Logs by visiting https://console.cloud.google.com/logs/metrics and click \"CREATE METRIC\".
    2. Click the down arrow symbol on the Filter Bar at the rightmost corner and select Convert to Advanced Filter.
    3. Clear any text and add:
      resource.type=\"gce_route\" AND jsonPayload.event_subtype=\"compute.routes.delete\" OR jsonPayload.event_subtype=\"compute.routes.insert\"
    4. Click Submit Filter. The logs display based on the filter text entered by the user.
    5. In the Metric Editor menu on the right,fill out the name field. Set Units to 1(default) and the Type to Counter. This ensures that the log metric counts the number of log entries matching the advanced logs query.
    6. Click CreateMetric.
    ", + "compliance": [ + { + "name": "CIS Google Cloud Platform Foundations", + "version": "1.1.0", + "reference": "2.8" + } + ], + "references": [ + "https://cloud.google.com/logging/docs/logs-based-metrics/", + "https://cloud.google.com/monitoring/custom-metrics/", + "https://cloud.google.com/monitoring/alerts/", + "https://cloud.google.com/logging/docs/reference/tools/gcloud-logging", + "https://cloud.google.com/storage/docs/access-control/iam" + ], + "dashboard_name": "Logging Configurations", + "path": "stackdriverlogging.projects.id.logging_metrics.id", + "conditions": [ + "and", + [ + "stackdriverlogging.projects.id.logging_metrics.id.vpc_network_route_change", + "false", + "" + ] + ], + "id_suffix": "vpc_network_route_change" +} diff --git a/ScoutSuite/providers/gcp/rules/findings/stackdrivermonitoring-alerts-does-not-exist-audit-config-changes.json b/ScoutSuite/providers/gcp/rules/findings/stackdrivermonitoring-alerts-does-not-exist-audit-config-changes.json new file mode 100644 index 000000000..f6ad2e922 --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/stackdrivermonitoring-alerts-does-not-exist-audit-config-changes.json @@ -0,0 +1,30 @@ +{ + "description": "Alerts Doesn't Exist For Audit Configuration Changes", + "rationale": "Configuring the metric filter and alerts for audit configuration changes ensures the recommended state of audit configuration is maintained so that all activities in the project are audit-able at any point in time.", + "remediation":"From console:
    1. Identify the audit configuration changes metric under the section User-defined Metrics at https://console.cloud.google.com/logs/metrics.
    2. Click the 3-dot icon in the rightmost column for the desired metric and select Create alert from Metric. A new page opens.
    3. Fill out the alert policy configuration and click Save. Choose the alerting threshold and configuration that makes sense for the user's organization. For example, a threshold of zero(0) for the most recent value will ensure that a notification is triggered for every owner change in the project::
      Set `Aggregator` to `Count`
      Set `Configuration`:
      -Condition: above
      -Threshold: 0
      -For: most recent value
    4. Configure the desired notifications channels in the section Notifications.
    5. Name the policy and click Save.
    ", + "compliance": [ + { + "name": "CIS Google Cloud Platform Foundations", + "version": "1.1.0", + "reference": "2.5" + } + ], + "references": [ + "https://cloud.google.com/logging/docs/logs-based-metrics/", + "https://cloud.google.com/monitoring/custom-metrics/", + "https://cloud.google.com/monitoring/alerts/", + "https://cloud.google.com/logging/docs/reference/tools/gcloud-logging", + "https://cloud.google.com/logging/docs/audit/configure-data-access#getiampolicy-setiampolicy" + ], + "dashboard_name": "Monitoring Alerts", + "path": "stackdrivermonitoring.projects.id.monitoring_alert_policies.id", + "conditions": [ + "and", + [ + "stackdrivermonitoring.projects.id.monitoring_alert_policies.id.audit_config_change", + "false", + "" + ] + ], + "id_suffix": "audit_config_change" +} diff --git a/ScoutSuite/providers/gcp/rules/findings/stackdrivermonitoring-alerts-does-not-exist-cloud-storage-iam-permission-changes.json b/ScoutSuite/providers/gcp/rules/findings/stackdrivermonitoring-alerts-does-not-exist-cloud-storage-iam-permission-changes.json new file mode 100644 index 000000000..b07ebc8d3 --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/stackdrivermonitoring-alerts-does-not-exist-cloud-storage-iam-permission-changes.json @@ -0,0 +1,31 @@ +{ + "description": "Alerts Doesn't Exist For Cloud Storage IAM Permission Changes", + "rationale": "Monitoring changes to cloud storage bucket permissions may reduce the time needed to detect and correct permissions on sensitive cloud storage buckets and objects inside the bucket.", + "remediation":"From console:
    1. Identify the cloud storage IAM permission changes metric under the section User-defined Metrics at https://console.cloud.google.com/logs/metrics.
    2. Click the 3-dot icon in the rightmost column for the desired metric and select Create alert from Metric. A new page opens.
    3. Fill out the alert policy configuration and click Save. Choose the alerting threshold and configuration that makes sense for the user's organization. For example, a threshold of zero(0) for the most recent value will ensure that a notification is triggered for every owner change in the project::
      Set `Aggregator` to `Count`
      Set `Configuration`:
      -Condition: above
      -Threshold: 0
      -For: most recent value
    4. Configure the desired notifications channels in the section Notifications.
    5. Name the policy and click Save.
    ", + "compliance": [ + { + "name": "CIS Google Cloud Platform Foundations", + "version": "1.1.0", + "reference": "2.10" + } + ], + "references": [ + "https://cloud.google.com/logging/docs/logs-based-metrics/", + "https://cloud.google.com/monitoring/custom-metrics/", + "https://cloud.google.com/monitoring/alerts/", + "https://cloud.google.com/logging/docs/reference/tools/gcloud-logging", + "https://cloud.google.com/storage/docs", + "https://cloud.google.com/storage/docs/access-control/iam-roles" + ], + "dashboard_name": "Monitoring Alerts", + "path": "stackdrivermonitoring.projects.id.monitoring_alert_policies.id", + "conditions": [ + "and", + [ + "stackdrivermonitoring.projects.id.monitoring_alert_policies.id.cloud_storage_iam_permission_change", + "false", + "" + ] + ], + "id_suffix": "cloud_storage_iam_permission_change" +} diff --git a/ScoutSuite/providers/gcp/rules/findings/stackdrivermonitoring-alerts-does-not-exist-custom-role-changes.json b/ScoutSuite/providers/gcp/rules/findings/stackdrivermonitoring-alerts-does-not-exist-custom-role-changes.json new file mode 100644 index 000000000..17be6be71 --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/stackdrivermonitoring-alerts-does-not-exist-custom-role-changes.json @@ -0,0 +1,30 @@ +{ + "description": "Alerts Doesn't Exist For Custom Role Changes", + "rationale": "Google Cloud IAM provides predefined roles that give granular access to specific Google Cloud Platform resources and prevent unwanted access to other resources. However, to cater to organization-specific needs, Cloud IAM also provides the ability to create custom roles. Project owners and administrators with the Organization Role Administrator role or the IAM Role Administrator role can create custom roles. Monitoring role creation, deletion and updating activities will help in identifying any over-privileged role at early stages.", + "remediation":"From console:
    1. Identify the custom role changes metric under the section User-defined Metrics at https://console.cloud.google.com/logs/metrics.
    2. Click the 3-dot icon in the rightmost column for the desired metric and select Create alert from Metric. A new page opens.
    3. Fill out the alert policy configuration and click Save. Choose the alerting threshold and configuration that makes sense for the user's organization. For example, a threshold of zero(0) for the most recent value will ensure that a notification is triggered for every owner change in the project::
      Set `Aggregator` to `Count`
      Set `Configuration`:
      -Condition: above
      -Threshold: 0
      -For: most recent value
    4. Configure the desired notifications channels in the section Notifications.
    5. Name the policy and click Save.
    ", + "compliance": [ + { + "name": "CIS Google Cloud Platform Foundations", + "version": "1.1.0", + "reference": "2.6" + } + ], + "references": [ + "https://cloud.google.com/logging/docs/logs-based-metrics/", + "https://cloud.google.com/monitoring/custom-metrics/", + "https://cloud.google.com/monitoring/alerts/", + "https://cloud.google.com/logging/docs/reference/tools/gcloud-logging", + "https://cloud.google.com/iam/docs/understanding-custom-roles" + ], + "dashboard_name": "Monitoring Alerts", + "path": "stackdrivermonitoring.projects.id.monitoring_alert_policies.id", + "conditions": [ + "and", + [ + "stackdrivermonitoring.projects.id.monitoring_alert_policies.id.custom_role_change", + "false", + "" + ] + ], + "id_suffix": "custom_role_change" +} diff --git a/ScoutSuite/providers/gcp/rules/findings/stackdrivermonitoring-alerts-does-not-exist-project-ownership-assignment.json b/ScoutSuite/providers/gcp/rules/findings/stackdrivermonitoring-alerts-does-not-exist-project-ownership-assignment.json new file mode 100644 index 000000000..42baacdc7 --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/stackdrivermonitoring-alerts-does-not-exist-project-ownership-assignment.json @@ -0,0 +1,29 @@ +{ + "description": "Alerts Doesn't Exist For Project Ownership Assignments/Changes", + "rationale": "Project ownership has the highest level of privileges on a project. To avoid misuse of project resources, the project ownership assignment/change actions mentioned above should be monitored and alerted to concerned recipients.", + "remediation":"From console:
    1. Identify the project ownership assignment/changes metric under the section User-defined Metrics at https://console.cloud.google.com/logs/metrics.
    2. Click the 3-dot icon in the rightmost column for the desired metric and select Create alert from Metric. A new page opens.
    3. Fill out the alert policy configuration and click Save. Choose the alerting threshold and configuration that makes sense for the user's organization. For example, a threshold of zero(0) for the most recent value will ensure that a notification is triggered for every owner change in the project::
      Set `Aggregator` to `Count`
      Set `Configuration`:
      -Condition: above
      -Threshold: 0
      -For: most recent value
    4. Configure the desired notifications channels in the section Notifications.
    5. Name the policy and click Save.
    ", + "compliance": [ + { + "name": "CIS Google Cloud Platform Foundations", + "version": "1.1.0", + "reference": "2.4" + } + ], + "references": [ + "https://cloud.google.com/logging/docs/logs-based-metrics/", + "https://cloud.google.com/monitoring/custom-metrics/", + "https://cloud.google.com/monitoring/alerts/", + "https://cloud.google.com/logging/docs/reference/tools/gcloud-logging" + ], + "dashboard_name": "Monitoring Alerts", + "path": "stackdrivermonitoring.projects.id.monitoring_alert_policies.id", + "conditions": [ + "and", + [ + "stackdrivermonitoring.projects.id.monitoring_alert_policies.id.project_ownership_assignments", + "false", + "" + ] + ], + "id_suffix": "project_ownership_assignments" +} diff --git a/ScoutSuite/providers/gcp/rules/findings/stackdrivermonitoring-alerts-does-not-exist-sql-instance-config-changes.json b/ScoutSuite/providers/gcp/rules/findings/stackdrivermonitoring-alerts-does-not-exist-sql-instance-config-changes.json new file mode 100644 index 000000000..769d272df --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/stackdrivermonitoring-alerts-does-not-exist-sql-instance-config-changes.json @@ -0,0 +1,33 @@ +{ + "description": "Alerts Doesn't Exist For SQL Instance Configuration Changes", + "rationale": "Monitoring changes to SQL instance configuration changes may reduce the time needed to detect and correct misconfigurations done on the SQL server.", + "remediation":"From console:
    1. Identify the sql instance configuration changes metric under the section User-defined Metrics at https://console.cloud.google.com/logs/metrics.
    2. Click the 3-dot icon in the rightmost column for the desired metric and select Create alert from Metric. A new page opens.
    3. Fill out the alert policy configuration and click Save. Choose the alerting threshold and configuration that makes sense for the user's organization. For example, a threshold of zero(0) for the most recent value will ensure that a notification is triggered for every owner change in the project::
      Set `Aggregator` to `Count`
      Set `Configuration`:
      -Condition: above
      -Threshold: 0
      -For: most recent value
    4. Configure the desired notifications channels in the section Notifications.
    5. Name the policy and click Save.
    ", + "compliance": [ + { + "name": "CIS Google Cloud Platform Foundations", + "version": "1.1.0", + "reference": "2.11" + } + ], + "references": [ + "https://cloud.google.com/logging/docs/logs-based-metrics/", + "https://cloud.google.com/monitoring/custom-metrics/", + "https://cloud.google.com/monitoring/alerts/", + "https://cloud.google.com/logging/docs/reference/tools/gcloud-logging", + "https://cloud.google.com/storage/docs", + "https://cloud.google.com/sql/docs/", + "https://cloud.google.com/sql/docs/mysql/", + "https://cloud.google.com/sql/docs/postgres/" + ], + "dashboard_name": "Monitoring Alerts", + "path": "stackdrivermonitoring.projects.id.monitoring_alert_policies.id", + "conditions": [ + "and", + [ + "stackdrivermonitoring.projects.id.monitoring_alert_policies.id.sql_instance_conf_change", + "false", + "" + ] + ], + "id_suffix": "sql_instance_conf_change" +} diff --git a/ScoutSuite/providers/gcp/rules/findings/stackdrivermonitoring-alerts-does-not-exist-vpc-network-changes.json b/ScoutSuite/providers/gcp/rules/findings/stackdrivermonitoring-alerts-does-not-exist-vpc-network-changes.json new file mode 100644 index 000000000..6cbdce37c --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/stackdrivermonitoring-alerts-does-not-exist-vpc-network-changes.json @@ -0,0 +1,30 @@ +{ + "description": "Alerts Doesn't Exist For VPC Network Changes", + "rationale": "It is possible to have more than one VPC within a project. In addition, it is also possible to create a peer connection between two VPCs enablingnetwork traffic to route between VPCs.Monitoring changes to a VPC will help ensure VPC traffic flow is not getting impacted.", + "remediation":"From console:
    1. Identify the vpc network changes metric under the section User-defined Metrics at https://console.cloud.google.com/logs/metrics.
    2. Click the 3-dot icon in the rightmost column for the desired metric and select Create alert from Metric. A new page opens.
    3. Fill out the alert policy configuration and click Save. Choose the alerting threshold and configuration that makes sense for the user's organization. For example, a threshold of zero(0) for the most recent value will ensure that a notification is triggered for every owner change in the project::
      Set `Aggregator` to `Count`
      Set `Configuration`:
      -Condition: above
      -Threshold: 0
      -For: most recent value
    4. Configure the desired notifications channels in the section Notifications.
    5. Name the policy and click Save.
    ", + "compliance": [ + { + "name": "CIS Google Cloud Platform Foundations", + "version": "1.1.0", + "reference": "2.9" + } + ], + "references": [ + "https://cloud.google.com/logging/docs/logs-based-metrics/", + "https://cloud.google.com/monitoring/custom-metrics/", + "https://cloud.google.com/monitoring/alerts/", + "https://cloud.google.com/logging/docs/reference/tools/gcloud-logging", + "https://cloud.google.com/vpc/docs/overview" + ], + "dashboard_name": "Monitoring Alerts", + "path": "stackdrivermonitoring.projects.id.monitoring_alert_policies.id", + "conditions": [ + "and", + [ + "stackdrivermonitoring.projects.id.monitoring_alert_policies.id.vpc_network_change", + "false", + "" + ] + ], + "id_suffix": "vpc_network_change" +} diff --git a/ScoutSuite/providers/gcp/rules/findings/stackdrivermonitoring-alerts-does-not-exist-vpc-network-firewall-rule-changes.json b/ScoutSuite/providers/gcp/rules/findings/stackdrivermonitoring-alerts-does-not-exist-vpc-network-firewall-rule-changes.json new file mode 100644 index 000000000..5833b5d1b --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/stackdrivermonitoring-alerts-does-not-exist-vpc-network-firewall-rule-changes.json @@ -0,0 +1,30 @@ +{ + "description": "Alerts Doesn't Exist For VPC Network Firewall Rule Changes", + "rationale": "Monitoring for Create or Update Firewall rule events gives insight to network access changes and may reduce the time it takes to detect suspicious activity.", + "remediation":"From console:
    1. Identify the vpc network firewall rule changes metric under the section User-defined Metrics at https://console.cloud.google.com/logs/metrics.
    2. Click the 3-dot icon in the rightmost column for the desired metric and select Create alert from Metric. A new page opens.
    3. Fill out the alert policy configuration and click Save. Choose the alerting threshold and configuration that makes sense for the user's organization. For example, a threshold of zero(0) for the most recent value will ensure that a notification is triggered for every owner change in the project::
      Set `Aggregator` to `Count`
      Set `Configuration`:
      -Condition: above
      -Threshold: 0
      -For: most recent value
    4. Configure the desired notifications channels in the section Notifications.
    5. Name the policy and click Save.
    ", + "compliance": [ + { + "name": "CIS Google Cloud Platform Foundations", + "version": "1.1.0", + "reference": "2.7" + } + ], + "references": [ + "https://cloud.google.com/logging/docs/logs-based-metrics/", + "https://cloud.google.com/monitoring/custom-metrics/", + "https://cloud.google.com/monitoring/alerts/", + "https://cloud.google.com/logging/docs/reference/tools/gcloud-logging", + "https://cloud.google.com/vpc/docs/firewalls" + ], + "dashboard_name": "Monitoring Alerts", + "path": "stackdrivermonitoring.projects.id.monitoring_alert_policies.id", + "conditions": [ + "and", + [ + "stackdrivermonitoring.projects.id.monitoring_alert_policies.id.vpc_network_firewall_rule_change", + "false", + "" + ] + ], + "id_suffix": "vpc_network_firewall_rule_change" +} diff --git a/ScoutSuite/providers/gcp/rules/findings/stackdrivermonitoring-alerts-does-not-exist-vpc-network-route-changes.json b/ScoutSuite/providers/gcp/rules/findings/stackdrivermonitoring-alerts-does-not-exist-vpc-network-route-changes.json new file mode 100644 index 000000000..7e28beafb --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/stackdrivermonitoring-alerts-does-not-exist-vpc-network-route-changes.json @@ -0,0 +1,30 @@ +{ + "description": "Alerts Doesn't Exist For VPC Network Route Changes", + "rationale": "Google Cloud Platform (GCP) routes define the paths network traffic takes from a VM instance to another destination. The other destination can be inside the organization VPC network (such as another VM) or outside of it. Every route consists of a destination and a next hop. Traffic whose destination IP is within the destination range is sent to the next hop for delivery.Monitoring changes to route tables will help ensure that all VPC traffic flows through an expected path.", + "remediation":"From console:
    1. Identify the vpc network route changes metric under the section User-defined Metrics at https://console.cloud.google.com/logs/metrics.
    2. Click the 3-dot icon in the rightmost column for the desired metric and select Create alert from Metric. A new page opens.
    3. Fill out the alert policy configuration and click Save. Choose the alerting threshold and configuration that makes sense for the user's organization. For example, a threshold of zero(0) for the most recent value will ensure that a notification is triggered for every owner change in the project::
      Set `Aggregator` to `Count`
      Set `Configuration`:
      -Condition: above
      -Threshold: 0
      -For: most recent value
    4. Configure the desired notifications channels in the section Notifications.
    5. Name the policy and click Save.
    ", + "compliance": [ + { + "name": "CIS Google Cloud Platform Foundations", + "version": "1.1.0", + "reference": "2.8" + } + ], + "references": [ + "https://cloud.google.com/logging/docs/logs-based-metrics/", + "https://cloud.google.com/monitoring/custom-metrics/", + "https://cloud.google.com/monitoring/alerts/", + "https://cloud.google.com/logging/docs/reference/tools/gcloud-logging", + "https://cloud.google.com/storage/docs/access-control/iam" + ], + "dashboard_name": "Monitoring Alerts", + "path": "stackdrivermonitoring.projects.id.monitoring_alert_policies.id", + "conditions": [ + "and", + [ + "stackdrivermonitoring.projects.id.monitoring_alert_policies.id.vpc_network_route_change", + "false", + "" + ] + ], + "id_suffix": "vpc_network_route_change" +} diff --git a/ScoutSuite/providers/gcp/rules/rulesets/cis-1.1.0.json b/ScoutSuite/providers/gcp/rules/rulesets/cis-1.1.0.json index 611ae8b3b..14c1991e1 100644 --- a/ScoutSuite/providers/gcp/rules/rulesets/cis-1.1.0.json +++ b/ScoutSuite/providers/gcp/rules/rulesets/cis-1.1.0.json @@ -132,6 +132,102 @@ "enabled": true, "level": "warning" } + ], + "stackdriverlogging-metric-filter-does-not-exist-project-ownership-assignment.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "stackdriverlogging-metric-filter-does-not-exist-audit-config-changes.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "stackdriverlogging-metric-filter-does-not-exist-custom-role-changes.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "stackdriverlogging-metric-filter-does-not-exist-vpc-network-firewall-rule-changes.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "stackdriverlogging-metric-filter-does-not-exist-vpc-network-route-changes.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "stackdriverlogging-metric-filter-does-not-exist-vpc-network-changes.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "stackdriverlogging-metric-filter-does-not-exist-cloud-storage-iam-permission-changes.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "stackdriverlogging-metric-filter-does-not-exist-sql-instance-config-changes.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "stackdrivermonitoring-alerts-does-not-exist-project-ownership-assignment.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "stackdrivermonitoring-alerts-does-not-exist-audit-config-changes.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "stackdrivermonitoring-alerts-does-not-exist-custom-role-changes.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "stackdrivermonitoring-alerts-does-not-exist-vpc-network-firewall-rule-changes.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "stackdrivermonitoring-alerts-does-not-exist-vpc-network-route-changes.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "stackdrivermonitoring-alerts-does-not-exist-vpc-network-changes.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "stackdrivermonitoring-alerts-does-not-exist-cloud-storage-iam-permission-changes.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "stackdrivermonitoring-alerts-does-not-exist-sql-instance-config-changes.json": [ + { + "enabled": true, + "level": "warning" + } ] } diff --git a/ScoutSuite/providers/gcp/rules/rulesets/default.json b/ScoutSuite/providers/gcp/rules/rulesets/default.json index 7c53eac47..f0d3618e1 100755 --- a/ScoutSuite/providers/gcp/rules/rulesets/default.json +++ b/ScoutSuite/providers/gcp/rules/rulesets/default.json @@ -457,6 +457,102 @@ "enabled": true, "level": "warning" } + ], + "stackdriverlogging-metric-filter-does-not-exist-project-ownership-assignment.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "stackdriverlogging-metric-filter-does-not-exist-audit-config-changes.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "stackdriverlogging-metric-filter-does-not-exist-custom-role-changes.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "stackdriverlogging-metric-filter-does-not-exist-vpc-network-firewall-rule-changes.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "stackdriverlogging-metric-filter-does-not-exist-vpc-network-route-changes.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "stackdriverlogging-metric-filter-does-not-exist-vpc-network-changes.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "stackdriverlogging-metric-filter-does-not-exist-cloud-storage-iam-permission-changes.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "stackdriverlogging-metric-filter-does-not-exist-sql-instance-config-changes.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "stackdrivermonitoring-alerts-does-not-exist-project-ownership-assignment.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "stackdrivermonitoring-alerts-does-not-exist-audit-config-changes.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "stackdrivermonitoring-alerts-does-not-exist-custom-role-changes.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "stackdrivermonitoring-alerts-does-not-exist-vpc-network-firewall-rule-changes.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "stackdrivermonitoring-alerts-does-not-exist-vpc-network-route-changes.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "stackdrivermonitoring-alerts-does-not-exist-vpc-network-changes.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "stackdrivermonitoring-alerts-does-not-exist-cloud-storage-iam-permission-changes.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "stackdrivermonitoring-alerts-does-not-exist-sql-instance-config-changes.json": [ + { + "enabled": true, + "level": "warning" + } ] } } From dc25a2ca985aa4db933460e0e7cf6ba7b0770595 Mon Sep 17 00:00:00 2001 From: Sophie Dorval <42855086+SophieDorval@users.noreply.github.com> Date: Thu, 8 Apr 2021 12:31:15 -0400 Subject: [PATCH 122/193] Enhancement/gcp sql refactor 6.6 (#1238) * refactor gcp sql rules and added rule 6.6 * fix issue * Update ScoutSuite/providers/gcp/rules/rulesets/default.json Co-authored-by: Xavier Garceau-Aranda * Update ScoutSuite/providers/gcp/rules/rulesets/cis-1.1.0.json Co-authored-by: Xavier Garceau-Aranda Co-authored-by: Sophie Co-authored-by: Xavier Garceau-Aranda --- .../cloudsql-instance-backups-disabled.json | 13 +++++++-- ...loudsql-instance-is-open-to-the-world.json | 14 ++++++++- .../cloudsql-instance-ssl-not-required.json | 10 +++++-- .../cloudsql-instances-public-ips.json | 29 +++++++++++++++++++ .../gcp/rules/rulesets/cis-1.1.0.json | 8 ++++- .../providers/gcp/rules/rulesets/default.json | 6 ++++ 6 files changed, 74 insertions(+), 6 deletions(-) create mode 100644 ScoutSuite/providers/gcp/rules/findings/cloudsql-instances-public-ips.json diff --git a/ScoutSuite/providers/gcp/rules/findings/cloudsql-instance-backups-disabled.json b/ScoutSuite/providers/gcp/rules/findings/cloudsql-instance-backups-disabled.json index 5c47c1d1e..b6c4b2dc1 100755 --- a/ScoutSuite/providers/gcp/rules/findings/cloudsql-instance-backups-disabled.json +++ b/ScoutSuite/providers/gcp/rules/findings/cloudsql-instance-backups-disabled.json @@ -1,8 +1,17 @@ { "description": "Instance with Automatic Backups Disabled", - "rationale": "Automatic backups should be configured for Cloud SQL instances in order to ensure backups are created regularly.", + "rationale": "Backups provide a way to restore a Cloud SQL instance to recover lost data or recover from a problem with that instance. Automated backups need to be set for any instance that contains data that should be protected from loss or damage.", + "remediation": "From console:
    1. Go to the Cloud SQL Instances page in the Google Cloud Console by visiting https://console.cloud.google.com/sql/instances.
    2. Select the instance where the backups need to be configured.
    3. Click Edit
    4. In the Backups section, check `Enable automated backups', and choose a backup window.
    5. Click Save
    ", + "compliance": [ + { + "name": "CIS Google Cloud Platform Foundations", + "version": "1.1.0", + "reference": "6.7" + } + ], "references": [ - "https://cloud.google.com/sql/docs/mysql/backup-recovery/backups" + "https://cloud.google.com/sql/docs/mysql/backup-recovery/backups", + "https://cloud.google.com/sql/docs/postgres/backup-recovery/backing-up" ], "dashboard_name": "Instances", "path": "cloudsql.projects.id.instances.id", diff --git a/ScoutSuite/providers/gcp/rules/findings/cloudsql-instance-is-open-to-the-world.json b/ScoutSuite/providers/gcp/rules/findings/cloudsql-instance-is-open-to-the-world.json index 734723b62..4100bda55 100755 --- a/ScoutSuite/providers/gcp/rules/findings/cloudsql-instance-is-open-to-the-world.json +++ b/ScoutSuite/providers/gcp/rules/findings/cloudsql-instance-is-open-to-the-world.json @@ -1,13 +1,25 @@ { "description": "Instance Allowing All Incoming Connections", - "rationale": "Database instances should accept connections from trusted IPs and networks only.", + "rationale": "To minimize attack surface on a Database server instance, only trusted/known and required IP(s) should be white-listed to connect to it.An authorized network should not have IPs/networks configured to 0.0.0.0/0which will allow access to the instance from anywhere in the world.", + "remediation": "From console:
    1. Go to the Cloud SQL Instances page in the Google Cloud Console by visiting https://console.cloud.google.com/sql/instances.
    2. Click the instance name to open its Instance details page.
    3. Under the Configuration section click Edit configurations.
    4. Under Configuration options expand the Connectivity section.
    5. Click the delete icon for the authorized network 0.0.0.0/0.
    6. Click Save to update the instance.
    ", "compliance": [ { "name": "CIS Google Cloud Platform Foundations", "version": "1.0.0", "reference": "6.2" + }, + { + "name": "CIS Google Cloud Platform Foundations", + "version": "1.1.0", + "reference": "6.5" } ], + "references": [ + "https://cloud.google.com/sql/docs/mysql/configure-ip", + "https://console.cloud.google.com/iam-admin/orgpolicies/sql-restrictAuthorizedNetworks", + "https://cloud.google.com/resource-manager/docs/organization-policy/org-policy-constraints", + "https://cloud.google.com/sql/docs/mysql/connection-org-policy" + ], "dashboard_name": "Instances", "display_path": "cloudsql.projects.id.instances.id", "path": "cloudsql.projects.id.instances.id.authorized_networks.id", diff --git a/ScoutSuite/providers/gcp/rules/findings/cloudsql-instance-ssl-not-required.json b/ScoutSuite/providers/gcp/rules/findings/cloudsql-instance-ssl-not-required.json index a9b1853d4..a4817120a 100755 --- a/ScoutSuite/providers/gcp/rules/findings/cloudsql-instance-ssl-not-required.json +++ b/ScoutSuite/providers/gcp/rules/findings/cloudsql-instance-ssl-not-required.json @@ -1,15 +1,21 @@ { "description": "Instance Not Requiring SSL for Incoming Connections", - "rationale": "All incoming connections to databases should require the use of SSL.", + "rationale": "SQL database connections if successfully trapped (MITM); can reveal sensitive data like credentials, database queries, query outputs etc. For security, it is recommended to always use SSL encryption when connecting to your instance.", + "remediation": "From console:
    1. Go to https://console.cloud.google.com/sql/instances.
    2. Click on an instance name to see its configuration overview.
    3. In the left-side panel, select Connections
    4. In the SSL connections section, click Allow only SSL connections.
    5. Under Configure SSL server certificates click Create new certificate.
    6. Under Configure SSL server certificates click Create a client certificate.
    7. Follow the instructions shown to learn how to connect to your instance.
    ", "compliance": [ { "name": "CIS Google Cloud Platform Foundations", "version": "1.0.0", "reference": "6.1" + }, + { + "name": "CIS Google Cloud Platform Foundations", + "version": "1.1.0", + "reference": "6.4" } ], "references": [ - "https://cloud.google.com/sql/docs/mysql/authorize-ssl" + "https://cloud.google.com/sql/docs/postgres/configure-ssl-instance" ], "dashboard_name": "Instances", "path": "cloudsql.projects.id.instances.id", diff --git a/ScoutSuite/providers/gcp/rules/findings/cloudsql-instances-public-ips.json b/ScoutSuite/providers/gcp/rules/findings/cloudsql-instances-public-ips.json new file mode 100644 index 000000000..07df46728 --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/cloudsql-instances-public-ips.json @@ -0,0 +1,29 @@ +{ + "description": "Cloud SQL Database Instances Have Public IPs", + "rationale": "To lower the organization's attack surface, Cloud SQL databases should not have public IPs. Private IPs provide improved network security and lower latency for your application.", + "remediation": "From console:
    1. Go to the Cloud SQL Instances page in the Google Cloud Console by visiting https://console.cloud.google.com/sql/instances.
    2. Click the instance name to open its Instance details page.
    3. Select the Connections tab.
    4. Deselect the Public IP checkbox.
    5. Click Save to update the instance.
    ", + "compliance": [ + { + "name": "CIS Google Cloud Platform Foundations", + "version": "1.1.0", + "reference": "6.6" + } + ], + "references": [ + "https://cloud.google.com/sql/docs/mysql/configure-private-ip", + "https://cloud.google.com/sql/docs/mysql/private-ip", + "https://cloud.google.com/resource-manager/docs/organization-policy/org-policy-constraints", + "https://console.cloud.google.com/iam-admin/orgpolicies/sql-restrictPublicIp" + ], + "dashboard_name": "Instances", + "path": "cloudsql.projects.id.instances.id", + "conditions": [ + "and", + [ + "cloudsql.projects.id.instances.id.public_ip", + "notEmpty", + "" + ] + ], + "id_suffix": "public_ip" +} \ No newline at end of file diff --git a/ScoutSuite/providers/gcp/rules/rulesets/cis-1.1.0.json b/ScoutSuite/providers/gcp/rules/rulesets/cis-1.1.0.json index 611ae8b3b..216bb58f2 100644 --- a/ScoutSuite/providers/gcp/rules/rulesets/cis-1.1.0.json +++ b/ScoutSuite/providers/gcp/rules/rulesets/cis-1.1.0.json @@ -1,6 +1,12 @@ { "about": "This ruleset attempts to cover as many recommendations from the CIS Google Cloud Platform Foundation v1.1.0.", "rules": { + "cloudsql-instances-public-ips.json": [ + { + "enabled": true, + "level": "danger" + } + ], "cloudsql-sqlservers-instances-cross-db-ownership-chaining-on.json": [ { "enabled": true, @@ -135,4 +141,4 @@ ] } -} \ No newline at end of file +} diff --git a/ScoutSuite/providers/gcp/rules/rulesets/default.json b/ScoutSuite/providers/gcp/rules/rulesets/default.json index 7c53eac47..6d31926dd 100755 --- a/ScoutSuite/providers/gcp/rules/rulesets/default.json +++ b/ScoutSuite/providers/gcp/rules/rulesets/default.json @@ -37,6 +37,12 @@ "level": "warning" } ], + "cloudsql-instances-public-ips.json": [ + { + "enabled": true, + "level": "danger" + } + ], "cloudsql-sqlservers-instances-cross-db-ownership-chaining-on.json": [ { "enabled": true, From cdd5e65897965ee0414bc3999b06fbd0c4737cd2 Mon Sep 17 00:00:00 2001 From: Sophie Dorval <42855086+SophieDorval@users.noreply.github.com> Date: Fri, 9 Apr 2021 13:21:24 -0400 Subject: [PATCH 123/193] Enhancement/gcp sql 6.2 (#1237) * added rule 6.2 from gcp * fix issue * fix issue * fix issue Co-authored-by: Sophie --- ...rvices.cloudsql.projects.id.instances.html | 8 +++ .../resources/cloudsql/database_instances.py | 52 ++++++++++++++++++- ...tgresql-instances-log-checkpoints-off.json | 27 ++++++++++ ...tgresql-instances-log-connections-off.json | 27 ++++++++++ ...esql-instances-log-disconnections-off.json | 27 ++++++++++ ...stgresql-instances-log-lock-waits-off.json | 27 ++++++++++ ...-instances-log-min-duration-not-set-1.json | 27 ++++++++++ ...ql-instances-log-min-messages-not-set.json | 27 ++++++++++ ...ql-instances-log-temp-files-not-set-0.json | 27 ++++++++++ .../gcp/rules/rulesets/cis-1.1.0.json | 42 +++++++++++++++ .../providers/gcp/rules/rulesets/default.json | 42 +++++++++++++++ 11 files changed, 332 insertions(+), 1 deletion(-) create mode 100644 ScoutSuite/providers/gcp/rules/findings/cloudsql-postgresql-instances-log-checkpoints-off.json create mode 100644 ScoutSuite/providers/gcp/rules/findings/cloudsql-postgresql-instances-log-connections-off.json create mode 100644 ScoutSuite/providers/gcp/rules/findings/cloudsql-postgresql-instances-log-disconnections-off.json create mode 100644 ScoutSuite/providers/gcp/rules/findings/cloudsql-postgresql-instances-log-lock-waits-off.json create mode 100644 ScoutSuite/providers/gcp/rules/findings/cloudsql-postgresql-instances-log-min-duration-not-set-1.json create mode 100644 ScoutSuite/providers/gcp/rules/findings/cloudsql-postgresql-instances-log-min-messages-not-set.json create mode 100644 ScoutSuite/providers/gcp/rules/findings/cloudsql-postgresql-instances-log-temp-files-not-set-0.json diff --git a/ScoutSuite/output/data/html/partials/gcp/services.cloudsql.projects.id.instances.html b/ScoutSuite/output/data/html/partials/gcp/services.cloudsql.projects.id.instances.html index 08ffcbb6b..78494547d 100755 --- a/ScoutSuite/output/data/html/partials/gcp/services.cloudsql.projects.id.instances.html +++ b/ScoutSuite/output/data/html/partials/gcp/services.cloudsql.projects.id.instances.html @@ -17,6 +17,14 @@

    Information

    Cross db Ownership Chaining Flag is Off: {{value_or_none cross_db_ownership_chaining_off}}
    Contained Database Authentication Flag is Off: {{value_or_none contained_database_authentication_off}}
    +
    Log Checkpoints Flag is On: {{value_or_none log_checkpoints_on}}
    +
    Log Connections Flag is On: {{value_or_none log_connections_on}}
    +
    Log Disconnections Flag is On: {{value_or_none log_disconnections_on}}
    +
    Log Lock Waits Flag is On: {{value_or_none log_lock_waits_on}}
    +
    Log Min Messages Flag set Appropriately: {{value_or_none log_min_messages}}
    +
    Log Temp Files Flag set to 0: {{value_or_none log_temp_files_0}}
    +
    Log Min Duration Statement Flag set to -1: {{value_or_none log_min_duration_statement_-1}}
    + {{#if authorized_networks}}
    Authorized Networks:
      diff --git a/ScoutSuite/providers/gcp/resources/cloudsql/database_instances.py b/ScoutSuite/providers/gcp/resources/cloudsql/database_instances.py index c10841acb..22857213a 100755 --- a/ScoutSuite/providers/gcp/resources/cloudsql/database_instances.py +++ b/ScoutSuite/providers/gcp/resources/cloudsql/database_instances.py @@ -42,13 +42,31 @@ def _parse_instance(self, raw_instance): if raw_instance['settings'].get('databaseFlags', None): instance_dict['local_infile_off'] = self._mysql_local_infile_flag_off(raw_instance) + instance_dict['log_checkpoints_on'] = self._postgres_flags_on(raw_instance, 'log_checkpoints') + instance_dict['log_connections_on'] = self._postgres_flags_on(raw_instance, 'log_connections') + instance_dict['log_disconnections_on'] = self._postgres_flags_on(raw_instance, 'log_disconnections') + instance_dict['log_lock_waits_on'] = self._postgres_flags_on(raw_instance, 'log_lock_waits') + instance_dict['log_min_messages'] = self._postgres_log_min_error_statement_flags(raw_instance) + instance_dict['log_temp_files_0'] = self._postgres_log_temp_files_flags_0(raw_instance) + instance_dict['log_min_duration_statement_-1'] = self._postgres_log_min_duration_statement_flags_1( + raw_instance) + instance_dict['cross_db_ownership_chaining_off'] = self._sqlservers_cross_db_ownership_chaining_flag_off( raw_instance, 'cross db ownership chaining') instance_dict['contained_database_authentication_off'] = self._sqlservers_cross_db_ownership_chaining_flag_off( raw_instance, 'contained database authentication') + else: instance_dict['local_infile_off'] = True + instance_dict['log_checkpoints_on'] = self._check_database_type(raw_instance) + instance_dict['log_connections_on'] = self._check_database_type(raw_instance) + instance_dict['log_disconnections_on'] = self._check_database_type(raw_instance) + instance_dict['log_lock_waits_on'] = self._check_database_type(raw_instance) + instance_dict['log_min_messages'] = self._check_database_type(raw_instance) + instance_dict['log_temp_files_0'] = self._check_database_type(raw_instance) + instance_dict['log_min_duration_statement_-1'] = self._check_database_type(raw_instance) + instance_dict['cross_db_ownership_chaining_off'] = True instance_dict['contained_database_authentication_off'] = True @@ -87,7 +105,6 @@ def _get_last_backup_timestamp(self, backups): lambda k: backups[k]['creation_timestamp'])) return backups[last_backup_id]['creation_timestamp'] - def _mysql_local_infile_flag_off(self, raw_instance): if 'MYSQL' in raw_instance['databaseVersion']: for flag in raw_instance['settings']['databaseFlags']: @@ -95,6 +112,39 @@ def _mysql_local_infile_flag_off(self, raw_instance): return False return True + def _check_database_type(self, raw_instance): + if 'POSTGRES' in raw_instance['databaseVersion']: + return False + return True + + def _postgres_flags_on(self, raw_instance, flag_name: str): + if 'POSTGRES' in raw_instance['databaseVersion']: + for flag in raw_instance['settings']['databaseFlags']: + if flag['name'] == flag_name and flag['value'] == 'off': + return False + return True + + def _postgres_log_min_error_statement_flags(self, raw_instance): + if 'POSTGRES' in raw_instance['databaseVersion']: + for flag in raw_instance['settings']['databaseFlags']: + if flag['name'] == 'log_min_error_statement' and flag['value'] is not None: + return True + return False + + def _postgres_log_temp_files_flags_0(self, raw_instance): + if 'POSTGRES' in raw_instance['databaseVersion']: + for flag in raw_instance['settings']['databaseFlags']: + if flag['name'] == 'log_temp_files' and flag['value'] != 0: + return False + return True + + def _postgres_log_min_duration_statement_flags_1(self, raw_instance): + if 'POSTGRES' in raw_instance['databaseVersion']: + for flag in raw_instance['settings']['databaseFlags']: + if flag['name'] == 'log_min_duration_statement' and flag['value'] != -1: + return False + return True + def _sqlservers_cross_db_ownership_chaining_flag_off(self, raw_instance, flag_name: str): if 'SQLSERVER' in raw_instance['databaseVersion']: for flag in raw_instance['settings']['databaseFlags']: diff --git a/ScoutSuite/providers/gcp/rules/findings/cloudsql-postgresql-instances-log-checkpoints-off.json b/ScoutSuite/providers/gcp/rules/findings/cloudsql-postgresql-instances-log-checkpoints-off.json new file mode 100644 index 000000000..b1ad37341 --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/cloudsql-postgresql-instances-log-checkpoints-off.json @@ -0,0 +1,27 @@ +{ + "description": "Log Checkpoints Database Flag For PostgreSQL Instance Is Off", + "rationale": "Enabling log_checkpoints causes checkpoints and restart points to be logged in the server log. Some statistics are included in the log messages, including the number of buffers written and the time spent writing them. This parameter can only be set in the postgresql.conf file or on the server command line. This recommendation is applicable to PostgreSQL database instances.", + "remediation": "From console:
      1. Go to the Cloud SQL Instances page in the Google Cloud Console by visiting https://console.cloud.google.com/sql/instances.
      2. Select the PostgreSQL instance where the database flag needs to be enabled.
      3. Click Edit
      4. Scroll down to the Flags section.
      5. To set a flag that has not been set on the instance before, click Add item, choose the flag log_checkpoints from the drop-down menu, and set its value to off.
      6. Click Save
      7. Confirm the changes under Flags on the Overview page.
      ", + "compliance": [ + { + "name": "CIS Google Cloud Platform Foundations", + "version": "1.1.0", + "reference": "6.2.1" + } + ], + "references": [ + "https://www.postgresql.org/docs/9.6/runtime-config-logging.html#RUNTIME-CONFIG-LOGGING-WHAT", + "https://cloud.google.com/sql/docs/postgres/flags#setting_a_database_flag" + ], + "dashboard_name": "Instances", + "path": "cloudsql.projects.id.instances.id", + "conditions": [ + "and", + [ + "cloudsql.projects.id.instances.id.log_checkpoints_on", + "false", + "" + ] + ], + "id_suffix": "log_checkpoints_on" +} \ No newline at end of file diff --git a/ScoutSuite/providers/gcp/rules/findings/cloudsql-postgresql-instances-log-connections-off.json b/ScoutSuite/providers/gcp/rules/findings/cloudsql-postgresql-instances-log-connections-off.json new file mode 100644 index 000000000..89bd43db0 --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/cloudsql-postgresql-instances-log-connections-off.json @@ -0,0 +1,27 @@ +{ + "description": "Log Connections Database Flag For PostgreSQL Instance Is Off", + "rationale": "PostgreSQL does not log attempted connections by default.Enabling the log_connections setting will create log entries for each attempted connection as well as successful completion of client authentication which can be useful in troubleshooting issues and to determine any unusual connection attempts to the server. This recommendation is applicable to PostgreSQL database instances.", + "remediation": "From console:
      1. Go to the Cloud SQL Instances page in the Google Cloud Console by visiting https://console.cloud.google.com/sql/instances.
      2. Select the PostgreSQL instance where the database flag needs to be enabled.
      3. Click Edit
      4. Scroll down to the Flags section.
      5. To set a flag that has not been set on the instance before, click Add item, choose the flag log_connections from the drop-down menu, and set its value to off.
      6. Click Save
      7. Confirm the changes under Flags on the Overview page.
      ", + "compliance": [ + { + "name": "CIS Google Cloud Platform Foundations", + "version": "1.1.0", + "reference": "6.2.2" + } + ], + "references": [ + "https://www.postgresql.org/docs/9.6/runtime-config-logging.html#RUNTIME-CONFIG-LOGGING-WHAT", + "https://cloud.google.com/sql/docs/postgres/flags" + ], + "dashboard_name": "Instances", + "path": "cloudsql.projects.id.instances.id", + "conditions": [ + "and", + [ + "cloudsql.projects.id.instances.id.log_connections_on", + "false", + "" + ] + ], + "id_suffix": "log_connections_on" +} \ No newline at end of file diff --git a/ScoutSuite/providers/gcp/rules/findings/cloudsql-postgresql-instances-log-disconnections-off.json b/ScoutSuite/providers/gcp/rules/findings/cloudsql-postgresql-instances-log-disconnections-off.json new file mode 100644 index 000000000..74e0ae1be --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/cloudsql-postgresql-instances-log-disconnections-off.json @@ -0,0 +1,27 @@ +{ + "description": "Log Disconnections Database Flag For PostgreSQL Instance Is Off", + "rationale": "PostgreSQL does not log session details such as duration and session end by default. Enabling the log_disconnections setting will create log entries at the end of each session which can be useful in troubleshooting issues and determine any unusual activity across a time period. The log_disconnections and log_connections work hand in hand and generally, the pair would be enabled/disabled together. This recommendation is applicable to PostgreSQL database instances.", + "remediation": "From console:
      1. Go to the Cloud SQL Instances page in the Google Cloud Console by visiting https://console.cloud.google.com/sql/instances.
      2. Select the PostgreSQL instance where the database flag needs to be enabled.
      3. Click Edit
      4. Scroll down to the Flags section.
      5. To set a flag that has not been set on the instance before, click Add item, choose the flag log_disconnections from the drop-down menu, and set its value to off.
      6. Click Save
      7. Confirm the changes under Flags on the Overview page.
      ", + "compliance": [ + { + "name": "CIS Google Cloud Platform Foundations", + "version": "1.1.0", + "reference": "6.2.3" + } + ], + "references": [ + "https://www.postgresql.org/docs/9.6/runtime-config-logging.html#RUNTIME-CONFIG-LOGGING-WHAT", + "https://cloud.google.com/sql/docs/postgres/flags" + ], + "dashboard_name": "Instances", + "path": "cloudsql.projects.id.instances.id", + "conditions": [ + "and", + [ + "cloudsql.projects.id.instances.id.log_disconnections_on", + "false", + "" + ] + ], + "id_suffix": "log_disconnections_on" +} \ No newline at end of file diff --git a/ScoutSuite/providers/gcp/rules/findings/cloudsql-postgresql-instances-log-lock-waits-off.json b/ScoutSuite/providers/gcp/rules/findings/cloudsql-postgresql-instances-log-lock-waits-off.json new file mode 100644 index 000000000..e71d5c59c --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/cloudsql-postgresql-instances-log-lock-waits-off.json @@ -0,0 +1,27 @@ +{ + "description": "Log Lock Waits Database Flag For PostgreSQL Instance Is Off", + "rationale": "The deadlock timeout defines the time to wait on a lock before checking for any conditions. Frequent run overs on deadlock timeout can be an indication of an underlying issue. Logging such waits on locks by enabling the log_lock_waits flag can be used to identify poor performance due to locking delays or if a specially-crafted SQL is attempting to starve resources through holding locks for excessive amounts of time. This recommendation is applicable to PostgreSQL database instances.", + "remediation": "From console:
      1. Go to the Cloud SQL Instances page in the Google Cloud Console by visiting https://console.cloud.google.com/sql/instances.
      2. Select the PostgreSQL instance where the database flag needs to be enabled.
      3. Click Edit
      4. Scroll down to the Flags section.
      5. To set a flag that has not been set on the instance before, click Add item, choose the flag log_lock_waits from the drop-down menu, and set its value to off.
      6. Click Save
      7. Confirm the changes under Flags on the Overview page.
      ", + "compliance": [ + { + "name": "CIS Google Cloud Platform Foundations", + "version": "1.1.0", + "reference": "6.2.4" + } + ], + "references": [ + "https://www.postgresql.org/docs/9.6/runtime-config-logging.html#GUC-LOG-MIN-DURATION-STATEMENT", + "https://cloud.google.com/sql/docs/postgres/flags" + ], + "dashboard_name": "Instances", + "path": "cloudsql.projects.id.instances.id", + "conditions": [ + "and", + [ + "cloudsql.projects.id.instances.id.log_lock_waits_on", + "false", + "" + ] + ], + "id_suffix": "log_lock_waits_on" +} \ No newline at end of file diff --git a/ScoutSuite/providers/gcp/rules/findings/cloudsql-postgresql-instances-log-min-duration-not-set-1.json b/ScoutSuite/providers/gcp/rules/findings/cloudsql-postgresql-instances-log-min-duration-not-set-1.json new file mode 100644 index 000000000..b7926fd89 --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/cloudsql-postgresql-instances-log-min-duration-not-set-1.json @@ -0,0 +1,27 @@ +{ + "description": "Log Min Duration Statement Database Flag For PostgreSQL Instance Is Not Set To -1", + "rationale": "Logging SQL statements may include sensitive information that should not be recorded in logs. This recommendation is applicable to PostgreSQL database instances.", + "remediation": "From console:
      1. Go to the Cloud SQL Instances page in the Google Cloud Console by visiting https://console.cloud.google.com/sql/instances.
      2. Select the PostgreSQL instance where the database flag needs to be enabled.
      3. Click Edit
      4. Scroll down to the Flags section.
      5. To set a flag that has not been set on the instance before, click Add item, choose the flag log_min_duration_statement from the drop-down menu, and set its value to -1.
      6. Click Save
      7. Confirm the changes under Flags on the Overview page.
      ", + "compliance": [ + { + "name": "CIS Google Cloud Platform Foundations", + "version": "1.1.0", + "reference": "6.2.7" + } + ], + "references": [ + "https://www.postgresql.org/docs/current/runtime-config-logging.html#RUNTIME-CONFIG-LOGGING-WHAT", + "https://cloud.google.com/sql/docs/postgres/flags" + ], + "dashboard_name": "Instances", + "path": "cloudsql.projects.id.instances.id", + "conditions": [ + "and", + [ + "cloudsql.projects.id.instances.id.log_min_duration_statement_-1", + "false", + "" + ] + ], + "id_suffix": "log_min_duration_statement_-1" +} \ No newline at end of file diff --git a/ScoutSuite/providers/gcp/rules/findings/cloudsql-postgresql-instances-log-min-messages-not-set.json b/ScoutSuite/providers/gcp/rules/findings/cloudsql-postgresql-instances-log-min-messages-not-set.json new file mode 100644 index 000000000..7e7308458 --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/cloudsql-postgresql-instances-log-min-messages-not-set.json @@ -0,0 +1,27 @@ +{ + "description": "Log Min Messages Database Flag For PostgreSQL Instance Is Not Set", + "rationale": "Auditing helps in troubleshooting operational problems and also permits forensic analysis. If log_min_error_statement is not set to the correct value, messages may not be classified as error messages appropriately. Considering general log messages as error messages would make it difficult to find actual errors, while considering only stricter severity levels as error messages may skip actual errors to log their SQL statements. The log_min_error_statement flag should be set in accordance with the organization's logging policy. This recommendation is applicable to PostgreSQL database instances.", + "remediation": "From console:
      1. Go to the Cloud SQL Instances page in the Google Cloud Console by visiting https://console.cloud.google.com/sql/instances.
      2. Select the PostgreSQL instance where the database flag needs to be enabled.
      3. Click Edit
      4. Scroll down to the Flags section.
      5. To set a flag that has not been set on the instance before, click Add item, choose the flag log_min_error_statement from the drop-down menu, and set appropriate value.
      6. Click Save
      7. Confirm the changes under Flags on the Overview page.
      ", + "compliance": [ + { + "name": "CIS Google Cloud Platform Foundations", + "version": "1.1.0", + "reference": "6.2.5" + } + ], + "references": [ + "https://www.postgresql.org/docs/9.6/runtime-config-logging.html#RUNTIME-CONFIG-LOGGING-WHEN", + "https://cloud.google.com/sql/docs/postgres/flags" + ], + "dashboard_name": "Instances", + "path": "cloudsql.projects.id.instances.id", + "conditions": [ + "and", + [ + "cloudsql.projects.id.instances.id.log_min_messages", + "false", + "" + ] + ], + "id_suffix": "log_min_messages" +} \ No newline at end of file diff --git a/ScoutSuite/providers/gcp/rules/findings/cloudsql-postgresql-instances-log-temp-files-not-set-0.json b/ScoutSuite/providers/gcp/rules/findings/cloudsql-postgresql-instances-log-temp-files-not-set-0.json new file mode 100644 index 000000000..b978c7869 --- /dev/null +++ b/ScoutSuite/providers/gcp/rules/findings/cloudsql-postgresql-instances-log-temp-files-not-set-0.json @@ -0,0 +1,27 @@ +{ + "description": "Log Temp Files Database Flag For PostgreSQL Instance Is Not Set To 0", + "rationale": "If all temporary files are not logged, it may be more difficult to identify potential performance issues that may be due to either poor application coding or deliberate resource starvation attempts.", + "remediation": "From console:
      1. Go to the Cloud SQL Instances page in the Google Cloud Console by visiting https://console.cloud.google.com/sql/instances.
      2. Select the PostgreSQL instance where the database flag needs to be enabled.
      3. Click Edit
      4. Scroll down to the Flags section.
      5. To set a flag that has not been set on the instance before, click Add item, choose the flag log_temp_files from the drop-down menu, and set its value to 0.
      6. Click Save
      7. Confirm the changes under Flags on the Overview page.
      ", + "compliance": [ + { + "name": "CIS Google Cloud Platform Foundations", + "version": "1.1.0", + "reference": "6.2.6" + } + ], + "references": [ + "https://www.postgresql.org/docs/9.6/runtime-config-logging.html#GUC-LOG-TEMP-FILES", + "https://cloud.google.com/sql/docs/postgres/flags" + ], + "dashboard_name": "Instances", + "path": "cloudsql.projects.id.instances.id", + "conditions": [ + "and", + [ + "cloudsql.projects.id.instances.id.log_temp_files_0", + "false", + "" + ] + ], + "id_suffix": "log_temp_files_0" +} \ No newline at end of file diff --git a/ScoutSuite/providers/gcp/rules/rulesets/cis-1.1.0.json b/ScoutSuite/providers/gcp/rules/rulesets/cis-1.1.0.json index e8944a1fb..40d8b57d3 100644 --- a/ScoutSuite/providers/gcp/rules/rulesets/cis-1.1.0.json +++ b/ScoutSuite/providers/gcp/rules/rulesets/cis-1.1.0.json @@ -1,6 +1,48 @@ { "about": "This ruleset attempts to cover as many recommendations from the CIS Google Cloud Platform Foundation v1.1.0.", "rules": { + "cloudsql-postgresql-instances-log-checkpoints-off.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "cloudsql-postgresql-instances-log-connections-off.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "cloudsql-postgresql-instances-log-disconnections-off.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "cloudsql-postgresql-instances-log-lock-waits-off.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "cloudsql-postgresql-instances-log-min-messages-not-set.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "cloudsql-postgresql-instances-log-temp-files-not-set-0.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "cloudsql-postgresql-instances-log-min-duration-not-set-1.json": [ + { + "enabled": true, + "level": "warning" + } + ], "cloudsql-instances-public-ips.json": [ { "enabled": true, diff --git a/ScoutSuite/providers/gcp/rules/rulesets/default.json b/ScoutSuite/providers/gcp/rules/rulesets/default.json index 8b0e62ef0..c0e87197e 100755 --- a/ScoutSuite/providers/gcp/rules/rulesets/default.json +++ b/ScoutSuite/providers/gcp/rules/rulesets/default.json @@ -37,6 +37,30 @@ "level": "warning" } ], + "cloudsql-postgresql-instances-log-checkpoints-off.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "cloudsql-postgresql-instances-log-connections-off.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "cloudsql-postgresql-instances-log-disconnections-off.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "cloudsql-postgresql-instances-log-lock-waits-off.json": [ + { + "enabled": true, + "level": "warning" + } + ], "cloudsql-instances-public-ips.json": [ { "enabled": true, @@ -49,12 +73,30 @@ "level": "warning" } ], + "cloudsql-postgresql-instances-log-min-messages-not-set.json": [ + { + "enabled": true, + "level": "warning" + } + ], "cloudsql-sqlservers-instances-contained-database-authentication-on.json": [ { "enabled": true, "level": "warning" } ], + "cloudsql-postgresql-instances-log-temp-files-not-set-0.json": [ + { + "enabled": true, + "level": "warning" + } + ], + "cloudsql-postgresql-instances-log-min-duration-not-set-1.json": [ + { + "enabled": true, + "level": "warning" + } + ], "cloudsql-mysql-instances-local-infile-on.json": [ { "enabled": true, From 557f2cdf31edc745bb0b58c48244616f1dc370f2 Mon Sep 17 00:00:00 2001 From: Andy Gu Date: Sun, 11 Apr 2021 23:19:40 -0400 Subject: [PATCH 124/193] comments --- .../computeengine-vpc-flow-logs-disabled.json | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/ScoutSuite/providers/gcp/rules/findings/computeengine-vpc-flow-logs-disabled.json b/ScoutSuite/providers/gcp/rules/findings/computeengine-vpc-flow-logs-disabled.json index dbfc66d28..2d8482163 100644 --- a/ScoutSuite/providers/gcp/rules/findings/computeengine-vpc-flow-logs-disabled.json +++ b/ScoutSuite/providers/gcp/rules/findings/computeengine-vpc-flow-logs-disabled.json @@ -2,6 +2,16 @@ "description": "VPC Flow Logs Not Enabled", "rationale": "VPC Flow Logs were not enabled for this subnet. It is best practice to enable Flow Logs to some degree in order to have network visibility in the event of resource compromise, as well as source data for threat detections.", "dashboard_name": "Subnetwork", + "compliance": [ + { + "name": "CIS Google Cloud Platform Foundations", + "version": "1.1.0", + "reference": "3.8" + } + ], + "references": [ + "https://cloud.google.com/vpc/docs/using-flow-logs#enabling_vpc_flow_logging" + ], "path": "computeengine.projects.id.regions.id.subnetworks.id", "conditions": [ "and", @@ -11,5 +21,5 @@ "" ] ], - "id_suffix": "name" + "id_suffix": "flowlogs_enabled" } From 05285f0b150c2db2538dbab0fa9d472f5afa4572 Mon Sep 17 00:00:00 2001 From: xga Date: Mon, 12 Apr 2021 10:46:44 +0200 Subject: [PATCH 125/193] Remove faulty requirement --- .../providers/gcp/resources/cloudstorage/buckets.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/ScoutSuite/providers/gcp/resources/cloudstorage/buckets.py b/ScoutSuite/providers/gcp/resources/cloudstorage/buckets.py index 38e7caa55..9c3dff437 100755 --- a/ScoutSuite/providers/gcp/resources/cloudstorage/buckets.py +++ b/ScoutSuite/providers/gcp/resources/cloudstorage/buckets.py @@ -58,10 +58,9 @@ def _get_cloudstorage_bucket_iam_member_bindings(self, raw_bucket): member_bindings = {} if bucket_iam_policy: for binding in bucket_iam_policy._bindings: - if 'legacy' not in binding['role']: - for member in binding['members']: - if member not in member_bindings: - member_bindings[member] = [binding['role']] - else: - member_bindings[member].append(binding['role']) + for member in binding['members']: + if member not in member_bindings: + member_bindings[member] = [binding['role']] + else: + member_bindings[member].append(binding['role']) return member_bindings From a7caed46091df176c1668927354552750435358b Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Mon, 12 Apr 2021 14:10:28 +0200 Subject: [PATCH 126/193] Removed } at the end which caused parsing errors --- .../aws/rules/findings/iam-root-account-with-active-keys.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ScoutSuite/providers/aws/rules/findings/iam-root-account-with-active-keys.json b/ScoutSuite/providers/aws/rules/findings/iam-root-account-with-active-keys.json index 8c2592bfe..51d533d66 100755 --- a/ScoutSuite/providers/aws/rules/findings/iam-root-account-with-active-keys.json +++ b/ScoutSuite/providers/aws/rules/findings/iam-root-account-with-active-keys.json @@ -48,4 +48,4 @@ ] ] ] -}} +} From 82058935da3080b2a45c187710659b9335288051 Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Mon, 12 Apr 2021 14:11:28 +0200 Subject: [PATCH 127/193] Added Lightspin vulnerability finding to default ruleset --- ScoutSuite/providers/aws/rules/rulesets/default.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ScoutSuite/providers/aws/rules/rulesets/default.json b/ScoutSuite/providers/aws/rules/rulesets/default.json index 3e986a574..20ef90c2f 100755 --- a/ScoutSuite/providers/aws/rules/rulesets/default.json +++ b/ScoutSuite/providers/aws/rules/rulesets/default.json @@ -567,6 +567,12 @@ "level": "danger" } ], + "iam-lightspin-user-action-denied-for-group.json": [ + { + "enabled": true, + "level": "danger" + } + ], "iam-managed-policy-allows-NotActions.json": [ { "enabled": true, From cd3dfe7d177f8d75ad2fb6bae4f728b56ff01992 Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Mon, 12 Apr 2021 14:12:06 +0200 Subject: [PATCH 128/193] Added new test to match regex in lists --- ScoutSuite/core/conditions.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ScoutSuite/core/conditions.py b/ScoutSuite/core/conditions.py index df9f7fbe2..5a6ec9ed0 100755 --- a/ScoutSuite/core/conditions.py +++ b/ScoutSuite/core/conditions.py @@ -181,6 +181,18 @@ def pass_condition(b, test, a): if re.match(c, b): result = True break + elif test == 'matchInList': + if type(a) != list: + a = [a] + if type(b) !=list: + b = [b] + for c in a: + for d in b: + if re.match(c, d): + result = True + break + if result: + break elif test == 'notMatch': result = (not pass_condition(b, 'match', a)) From 96293b3d3ff56fd653dba9f945f12d604f43afc7 Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Mon, 12 Apr 2021 14:17:17 +0200 Subject: [PATCH 129/193] New finding for Lightspin vulnerability --- ...ightspin-user-action-denied-for-group.json | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 ScoutSuite/providers/aws/rules/findings/iam-lightspin-user-action-denied-for-group.json diff --git a/ScoutSuite/providers/aws/rules/findings/iam-lightspin-user-action-denied-for-group.json b/ScoutSuite/providers/aws/rules/findings/iam-lightspin-user-action-denied-for-group.json new file mode 100644 index 000000000..487318c3c --- /dev/null +++ b/ScoutSuite/providers/aws/rules/findings/iam-lightspin-user-action-denied-for-group.json @@ -0,0 +1,87 @@ +{ + "description": "Policy with Denied User Actions for Group Objects (Lightspin Vulnerability)", + "rationale": "When a deny policy is specified for User object actions on a group resource, this will only affect the specific IAM group but not the group members. This could lead to privilege escalation if the user can perform other privileged actions targeting the sepecific members of the group.", + "remediation": "Define all relevant users in the resource field of the affected policies to avoid ineffective IAM actions and deny all group actions. The alternative would be to use the condition \"iam:ResourceTag\" in the policy.", + "references": [ + "https://blog.lightspin.io/aws-iam-groups-authorization-bypass", + "https://github.com/lightspin-tech/red-shadow" + ], + "dashboard_name": "Policies", + "display_path": "iam.policies.id", + "path": "iam.policies.id.PolicyDocument.Statement.id", + "conditions": [ + "and", + [ + "iam.policies.id.PolicyDocument.Statement.id.Effect", + "equal", + "Deny" + ], + [ + "iam.policies.id.PolicyDocument.Statement.id.Resource", + "matchInList", + "arn:aws:iam::[0-9]+:group/.*" + ], + [ + "and", + [ + "iam.policies.id.PolicyDocument.Statement.id.", + "withKey", + "Action" + ], + [ + "iam.policies.id.PolicyDocument.Statement.id.Action", + "containAtLeastOneOf", + [ + "*", + "iam:CreateUser", + "iam:GetUser", + "iam:UpdateUser", + "iam:DeleteUser", + "iam:GetUserPolicy", + "iam:PutUserPolicy", + "iam:DeleteUserPolicy", + "iam:ListUserPolicies", + "iam:AttachUserPolicy", + "iam:DetachUserPolicy", + "iam:ListAttachedUserPolicies", + "iam:SimulatePrincipalPolicy", + "iam:GetContextKeysForPrincipalPolicy", + "iam:TagUser", + "iam:UpdateSSHPublicKey", + "iam:UntagUser", + "iam:GetSSHPublicKey", + "iam:ListUserTags", + "iam:DeleteSSHPublicKey", + "iam:GetLoginProfile", + "iam:GetAccessKeyLastUsed", + "iam:UpdateLoginProfile", + "iam:UploadSigningCertificate", + "iam:DeleteLoginProfile", + "iam:ListSigningCertificates", + "iam:CreateLoginProfile", + "iam:UpdateSigningCertificate", + "iam:EnableMFADevice", + "iam:DeleteSigningCertificate", + "iam:ResyncMFADevice", + "iam:ListServiceSpecificCredentials", + "iam:ListMFADevices", + "iam:ResetServiceSpecificCredential", + "iam:DeactivateMFADevice", + "iam:CreateServiceSpecificCredential", + "iam:ChangePassword", + "iam:UpdateServiceSpecificCredential", + "iam:CreateAccessKey", + "iam:DeleteServiceSpecificCredential", + "iam:ListAccessKeys", + "iam:PutUserPermissionsBoundary", + "iam:UpdateAccessKey", + "iam:DeleteUserPermissionsBoundary", + "iam:DeleteAccessKey", + "iam:ListGroupsForUser", + "iam:ListSSHPublicKeys", + "iam:UploadSSHPublicKey" + ] + ] + ] + ] +} \ No newline at end of file From 2b3aca385be1f054f64cf8bda7621c30f798771c Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Tue, 13 Apr 2021 11:10:12 +0200 Subject: [PATCH 130/193] Expose partition in AWSFacade --- ScoutSuite/providers/aws/facade/base.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ScoutSuite/providers/aws/facade/base.py b/ScoutSuite/providers/aws/facade/base.py index c4e17a59d..d40fe0f2b 100755 --- a/ScoutSuite/providers/aws/facade/base.py +++ b/ScoutSuite/providers/aws/facade/base.py @@ -26,7 +26,7 @@ from ScoutSuite.providers.aws.facade.sns import SNSFacade from ScoutSuite.providers.aws.facade.sqs import SQSFacade from ScoutSuite.providers.aws.facade.secretsmanager import SecretsManagerFacade -from ScoutSuite.providers.aws.utils import get_aws_account_id +from ScoutSuite.providers.aws.utils import get_aws_account_id, get_partition_name from ScoutSuite.providers.utils import run_concurrently from ScoutSuite.core.conditions import print_error @@ -66,6 +66,7 @@ class AWSFacade(AWSBaseFacade): def __init__(self, credentials=None): super().__init__() self.owner_id = get_aws_account_id(credentials.session) + self.partition = get_partition_name(credentials.session) self.session = credentials.session self._instantiate_facades() From 2fc4164b37d6f236ef285a33364833d4d71bff02 Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Tue, 13 Apr 2021 11:11:00 +0200 Subject: [PATCH 131/193] Get partition from the facade for each resource type --- ScoutSuite/providers/aws/resources/cloudwatch/metric_filters.py | 2 +- ScoutSuite/providers/aws/resources/directconnect/connections.py | 2 +- ScoutSuite/providers/aws/resources/ec2/ami.py | 2 +- ScoutSuite/providers/aws/resources/ec2/instances.py | 2 +- ScoutSuite/providers/aws/resources/ec2/networkinterfaces.py | 2 +- ScoutSuite/providers/aws/resources/ec2/securitygroups.py | 2 +- ScoutSuite/providers/aws/resources/ec2/snapshots.py | 2 +- ScoutSuite/providers/aws/resources/ec2/volumes.py | 2 +- ScoutSuite/providers/aws/resources/efs/filesystems.py | 2 +- ScoutSuite/providers/aws/resources/elasticache/cluster.py | 2 +- ScoutSuite/providers/aws/resources/elb/load_balancers.py | 2 +- ScoutSuite/providers/aws/resources/elb/policies.py | 2 +- ScoutSuite/providers/aws/resources/kms/grants.py | 2 +- .../aws/resources/redshift/cluster_parameter_groups.py | 2 +- .../providers/aws/resources/redshift/cluster_parameters.py | 2 +- ScoutSuite/providers/aws/resources/redshift/clusters.py | 2 +- ScoutSuite/providers/aws/resources/route53/domains.py | 2 +- ScoutSuite/providers/aws/resources/route53/hosted_zones.py | 2 +- ScoutSuite/providers/aws/resources/ses/identities.py | 2 +- ScoutSuite/providers/aws/resources/ses/identity_policies.py | 2 +- ScoutSuite/providers/aws/resources/vpc/flow_logs.py | 2 +- ScoutSuite/providers/aws/resources/vpc/network_acls.py | 2 +- ScoutSuite/providers/aws/resources/vpc/peering_connections.py | 2 +- ScoutSuite/providers/aws/resources/vpcs.py | 2 +- 24 files changed, 24 insertions(+), 24 deletions(-) diff --git a/ScoutSuite/providers/aws/resources/cloudwatch/metric_filters.py b/ScoutSuite/providers/aws/resources/cloudwatch/metric_filters.py index 339b271fc..aa3911cc5 100644 --- a/ScoutSuite/providers/aws/resources/cloudwatch/metric_filters.py +++ b/ScoutSuite/providers/aws/resources/cloudwatch/metric_filters.py @@ -8,7 +8,7 @@ class MetricFilters(AWSResources): def __init__(self, facade: AWSFacade, region: str): super(MetricFilters, self).__init__(facade) self.region = region - self.partition = get_partition_name(facade.session) + self.partition = facade.partition self.service = 'cloudwatch' self.resource_type = 'metric-filter' diff --git a/ScoutSuite/providers/aws/resources/directconnect/connections.py b/ScoutSuite/providers/aws/resources/directconnect/connections.py index 6884b296d..e0e4534ba 100755 --- a/ScoutSuite/providers/aws/resources/directconnect/connections.py +++ b/ScoutSuite/providers/aws/resources/directconnect/connections.py @@ -7,7 +7,7 @@ class Connections(AWSResources): def __init__(self, facade: AWSFacade, region: str): super().__init__(facade) self.region = region - self.partition = get_partition_name(facade.session) + self.partition = facade.partition self.service = 'directconnect' self.resource_type = 'connection' diff --git a/ScoutSuite/providers/aws/resources/ec2/ami.py b/ScoutSuite/providers/aws/resources/ec2/ami.py index f65a897a5..45660121f 100755 --- a/ScoutSuite/providers/aws/resources/ec2/ami.py +++ b/ScoutSuite/providers/aws/resources/ec2/ami.py @@ -7,7 +7,7 @@ class AmazonMachineImages(AWSResources): def __init__(self, facade: AWSFacade, region: str): super().__init__(facade) self.region = region - self.partition = get_partition_name(facade.session) + self.partition = facade.partition self.service = 'ec2' self.resource_type = 'amazon-machine-image' diff --git a/ScoutSuite/providers/aws/resources/ec2/instances.py b/ScoutSuite/providers/aws/resources/ec2/instances.py index b8e0b9781..3ea82c513 100755 --- a/ScoutSuite/providers/aws/resources/ec2/instances.py +++ b/ScoutSuite/providers/aws/resources/ec2/instances.py @@ -10,7 +10,7 @@ def __init__(self, facade: AWSFacade, region: str, vpc: str): super().__init__(facade) self.region = region self.vpc = vpc - self.partition = get_partition_name(facade.session) + self.partition = facade.partition self.service = 'ec2' self.resource_type = 'instance' diff --git a/ScoutSuite/providers/aws/resources/ec2/networkinterfaces.py b/ScoutSuite/providers/aws/resources/ec2/networkinterfaces.py index 1315ea96c..2961b9fb4 100755 --- a/ScoutSuite/providers/aws/resources/ec2/networkinterfaces.py +++ b/ScoutSuite/providers/aws/resources/ec2/networkinterfaces.py @@ -8,7 +8,7 @@ def __init__(self, facade: AWSFacade, region: str, vpc: str): super().__init__(facade) self.region = region self.vpc = vpc - self.partition = get_partition_name(facade.session) + self.partition = facade.partition self.service = 'ec2' self.resource_type = 'network-interface' diff --git a/ScoutSuite/providers/aws/resources/ec2/securitygroups.py b/ScoutSuite/providers/aws/resources/ec2/securitygroups.py index 2890ed730..0bb0a6b62 100755 --- a/ScoutSuite/providers/aws/resources/ec2/securitygroups.py +++ b/ScoutSuite/providers/aws/resources/ec2/securitygroups.py @@ -12,7 +12,7 @@ def __init__(self, facade: AWSFacade, region: str, vpc: str): super().__init__(facade) self.region = region self.vpc = vpc - self.partition = get_partition_name(facade.session) + self.partition = facade.partition self.service = 'ec2' self.resource_type = 'security-group' diff --git a/ScoutSuite/providers/aws/resources/ec2/snapshots.py b/ScoutSuite/providers/aws/resources/ec2/snapshots.py index 3f9111753..6ac808ef6 100755 --- a/ScoutSuite/providers/aws/resources/ec2/snapshots.py +++ b/ScoutSuite/providers/aws/resources/ec2/snapshots.py @@ -7,7 +7,7 @@ class Snapshots(AWSResources): def __init__(self, facade: AWSFacade, region: str): super().__init__(facade) self.region = region - self.partition = get_partition_name(facade.session) + self.partition = facade.partition self.service = 'ec2' self.resource_type = 'snapshot' diff --git a/ScoutSuite/providers/aws/resources/ec2/volumes.py b/ScoutSuite/providers/aws/resources/ec2/volumes.py index 5f1ca0701..fed2bbf63 100755 --- a/ScoutSuite/providers/aws/resources/ec2/volumes.py +++ b/ScoutSuite/providers/aws/resources/ec2/volumes.py @@ -7,7 +7,7 @@ class Volumes(AWSResources): def __init__(self, facade: AWSFacade, region: str): super().__init__(facade) self.region = region - self.partition = get_partition_name(facade.session) + self.partition = facade.partition self.service = 'ec2' self.resource_type = 'volume' diff --git a/ScoutSuite/providers/aws/resources/efs/filesystems.py b/ScoutSuite/providers/aws/resources/efs/filesystems.py index f416be827..b69aac044 100755 --- a/ScoutSuite/providers/aws/resources/efs/filesystems.py +++ b/ScoutSuite/providers/aws/resources/efs/filesystems.py @@ -7,7 +7,7 @@ class FileSystems(AWSResources): def __init__(self, facade: AWSFacade, region: str): super().__init__(facade) self.region = region - self.partition = get_partition_name(facade.session) + self.partition = facade.partition self.service = 'elasticfilesystem' self.resource_type = 'file-system' diff --git a/ScoutSuite/providers/aws/resources/elasticache/cluster.py b/ScoutSuite/providers/aws/resources/elasticache/cluster.py index a2aeb9cfd..4eabf96ac 100755 --- a/ScoutSuite/providers/aws/resources/elasticache/cluster.py +++ b/ScoutSuite/providers/aws/resources/elasticache/cluster.py @@ -8,7 +8,7 @@ def __init__(self, facade: AWSFacade, region: str, vpc: str): super().__init__(facade) self.region = region self.vpc = vpc - self.partition = get_partition_name(facade.session) + self.partition = facade.partition self.service = 'elasticache' self.resource_type = 'cluster' diff --git a/ScoutSuite/providers/aws/resources/elb/load_balancers.py b/ScoutSuite/providers/aws/resources/elb/load_balancers.py index 49bf16a20..4e4a719b4 100755 --- a/ScoutSuite/providers/aws/resources/elb/load_balancers.py +++ b/ScoutSuite/providers/aws/resources/elb/load_balancers.py @@ -9,7 +9,7 @@ def __init__(self, facade: AWSFacade, region: str, vpc: str): super().__init__(facade) self.region = region self.vpc = vpc - self.partition = get_partition_name(facade.session) + self.partition = facade.partition self.service = 'elb' self.resource_type = 'load-balancer' diff --git a/ScoutSuite/providers/aws/resources/elb/policies.py b/ScoutSuite/providers/aws/resources/elb/policies.py index d461e5ad2..a5869379c 100755 --- a/ScoutSuite/providers/aws/resources/elb/policies.py +++ b/ScoutSuite/providers/aws/resources/elb/policies.py @@ -8,7 +8,7 @@ class Policies(AWSResources): def __init__(self, facade: AWSFacade, region: str): super().__init__(facade) self.region = region - self.partition = get_partition_name(facade.session) + self.partition = facade.partition self.service = 'elb' self.resource_type = 'policy' diff --git a/ScoutSuite/providers/aws/resources/kms/grants.py b/ScoutSuite/providers/aws/resources/kms/grants.py index 54cc20c56..e8c762920 100755 --- a/ScoutSuite/providers/aws/resources/kms/grants.py +++ b/ScoutSuite/providers/aws/resources/kms/grants.py @@ -8,7 +8,7 @@ def __init__(self, facade: AWSFacade, region: str, key_id: str): super().__init__(facade) self.region = region self.key_id = key_id - self.partition = get_partition_name(facade.session) + self.partition = facade.partition self.service = 'kms' self.resource_type = 'grant' diff --git a/ScoutSuite/providers/aws/resources/redshift/cluster_parameter_groups.py b/ScoutSuite/providers/aws/resources/redshift/cluster_parameter_groups.py index 407e72880..50dff3de5 100755 --- a/ScoutSuite/providers/aws/resources/redshift/cluster_parameter_groups.py +++ b/ScoutSuite/providers/aws/resources/redshift/cluster_parameter_groups.py @@ -14,7 +14,7 @@ class ClusterParameterGroups(AWSCompositeResources): def __init__(self, facade: AWSFacade, region: str): super().__init__(facade) self.region = region - self.partition = get_partition_name(facade.session) + self.partition = facade.partition self.service = 'redshift' self.resource_type = 'parametergroup' diff --git a/ScoutSuite/providers/aws/resources/redshift/cluster_parameters.py b/ScoutSuite/providers/aws/resources/redshift/cluster_parameters.py index 93d8bae34..56b4b9670 100755 --- a/ScoutSuite/providers/aws/resources/redshift/cluster_parameters.py +++ b/ScoutSuite/providers/aws/resources/redshift/cluster_parameters.py @@ -8,7 +8,7 @@ def __init__(self, facade: AWSFacade, region: str, parameter_group_name: str): super().__init__(facade) self.region = region self.parameter_group_name = parameter_group_name - self.partition = get_partition_name(facade.session) + self.partition = facade.partition self.service = 'redshift' self.resource_type = 'cluster-parameter' diff --git a/ScoutSuite/providers/aws/resources/redshift/clusters.py b/ScoutSuite/providers/aws/resources/redshift/clusters.py index c02425ffc..33e26b9ad 100755 --- a/ScoutSuite/providers/aws/resources/redshift/clusters.py +++ b/ScoutSuite/providers/aws/resources/redshift/clusters.py @@ -8,7 +8,7 @@ def __init__(self, facade: AWSFacade, region: str, vpc: str): super().__init__(facade) self.region = region self.vpc = vpc - self.partition = get_partition_name(facade.session) + self.partition = facade.partition self.service = 'redshift' self.resource_type = 'cluster' diff --git a/ScoutSuite/providers/aws/resources/route53/domains.py b/ScoutSuite/providers/aws/resources/route53/domains.py index 833091dc4..e0fd64bf7 100755 --- a/ScoutSuite/providers/aws/resources/route53/domains.py +++ b/ScoutSuite/providers/aws/resources/route53/domains.py @@ -8,7 +8,7 @@ class Domains(AWSResources): def __init__(self, facade: AWSFacade, region: str): super().__init__(facade) self.region = region - self.partition = get_partition_name(facade.session) + self.partition = facade.partition self.service = 'route53' self.resource_type = 'domain' diff --git a/ScoutSuite/providers/aws/resources/route53/hosted_zones.py b/ScoutSuite/providers/aws/resources/route53/hosted_zones.py index d2a44d692..e7894c794 100755 --- a/ScoutSuite/providers/aws/resources/route53/hosted_zones.py +++ b/ScoutSuite/providers/aws/resources/route53/hosted_zones.py @@ -7,7 +7,7 @@ class HostedZones(AWSResources): def __init__(self, facade: AWSFacade, region: str): super().__init__(facade) self.region = region - self.partition = get_partition_name(facade.session) + self.partition = facade.partition self.service = 'route53' self.resource_type = 'hosted-zone' diff --git a/ScoutSuite/providers/aws/resources/ses/identities.py b/ScoutSuite/providers/aws/resources/ses/identities.py index be092195d..86680469a 100755 --- a/ScoutSuite/providers/aws/resources/ses/identities.py +++ b/ScoutSuite/providers/aws/resources/ses/identities.py @@ -14,7 +14,7 @@ class Identities(AWSCompositeResources): def __init__(self, facade: AWSFacade, region: str): super().__init__(facade) self.region = region - self.partition = get_partition_name(facade.session) + self.partition = facade.partition self.service = 'ses' self.resource_type = 'identity' diff --git a/ScoutSuite/providers/aws/resources/ses/identity_policies.py b/ScoutSuite/providers/aws/resources/ses/identity_policies.py index 6f2671a1a..cd2ae4118 100755 --- a/ScoutSuite/providers/aws/resources/ses/identity_policies.py +++ b/ScoutSuite/providers/aws/resources/ses/identity_policies.py @@ -11,7 +11,7 @@ def __init__(self, facade: AWSFacade, region: str, identity_name: str): super().__init__(facade) self.region = region self.identity_name = identity_name - self.partition = get_partition_name(facade.session) + self.partition = facade.partition self.service = 'ses' self.resource_type = 'identity-policy' diff --git a/ScoutSuite/providers/aws/resources/vpc/flow_logs.py b/ScoutSuite/providers/aws/resources/vpc/flow_logs.py index f0666b4b7..e4cfd1e53 100755 --- a/ScoutSuite/providers/aws/resources/vpc/flow_logs.py +++ b/ScoutSuite/providers/aws/resources/vpc/flow_logs.py @@ -8,7 +8,7 @@ def __init__(self, facade: AWSFacade, region: str): super().__init__(facade) self.facade = facade self.region = region - self.partition = get_partition_name(facade.session) + self.partition = facade.partition self.service = 'vpc' self.resource_type = 'flow-log' diff --git a/ScoutSuite/providers/aws/resources/vpc/network_acls.py b/ScoutSuite/providers/aws/resources/vpc/network_acls.py index 5492be967..479e62715 100755 --- a/ScoutSuite/providers/aws/resources/vpc/network_acls.py +++ b/ScoutSuite/providers/aws/resources/vpc/network_acls.py @@ -10,7 +10,7 @@ class NetworkACLs(AWSResources): def __init__(self, facade: AWSFacade, region: str, vpc: str): self.region = region self.vpc = vpc - self.partition = get_partition_name(facade.session) + self.partition = facade.partition self.service = 'vpc' self.resource_type = 'network-acl' diff --git a/ScoutSuite/providers/aws/resources/vpc/peering_connections.py b/ScoutSuite/providers/aws/resources/vpc/peering_connections.py index 97c44a8a4..3907a72ab 100755 --- a/ScoutSuite/providers/aws/resources/vpc/peering_connections.py +++ b/ScoutSuite/providers/aws/resources/vpc/peering_connections.py @@ -8,7 +8,7 @@ def __init__(self, facade: AWSFacade, region: str): super().__init__(facade) self.facade = facade self.region = region - self.partition = get_partition_name(facade.session) + self.partition = facade.partition self.service = 'vpc' self.resource_type = 'peering-connection' diff --git a/ScoutSuite/providers/aws/resources/vpcs.py b/ScoutSuite/providers/aws/resources/vpcs.py index 157a5b6c7..627891472 100755 --- a/ScoutSuite/providers/aws/resources/vpcs.py +++ b/ScoutSuite/providers/aws/resources/vpcs.py @@ -11,7 +11,7 @@ def __init__(self, facade, region: str, add_ec2_classic=False): super().__init__(facade) self.region = region self.add_ec2_classic = add_ec2_classic - self.partition = get_partition_name(facade.session) + self.partition = facade.partition self.service = 'vpc' self.resource_type = 'virtual-private-cloud' From c82baa1b1bae56f003e83a3afee9221124e6127e Mon Sep 17 00:00:00 2001 From: lowSoA <66413174+lowSoA@users.noreply.github.com> Date: Tue, 13 Apr 2021 14:01:15 +0200 Subject: [PATCH 132/193] Ammend rule description field Co-authored-by: Xavier Garceau-Aranda --- .../findings/iam-lightspin-user-action-denied-for-group.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ScoutSuite/providers/aws/rules/findings/iam-lightspin-user-action-denied-for-group.json b/ScoutSuite/providers/aws/rules/findings/iam-lightspin-user-action-denied-for-group.json index 487318c3c..82ec71e28 100644 --- a/ScoutSuite/providers/aws/rules/findings/iam-lightspin-user-action-denied-for-group.json +++ b/ScoutSuite/providers/aws/rules/findings/iam-lightspin-user-action-denied-for-group.json @@ -1,5 +1,5 @@ { - "description": "Policy with Denied User Actions for Group Objects (Lightspin Vulnerability)", + "description": "Policy with Denied User Actions for Group Objects", "rationale": "When a deny policy is specified for User object actions on a group resource, this will only affect the specific IAM group but not the group members. This could lead to privilege escalation if the user can perform other privileged actions targeting the sepecific members of the group.", "remediation": "Define all relevant users in the resource field of the affected policies to avoid ineffective IAM actions and deny all group actions. The alternative would be to use the condition \"iam:ResourceTag\" in the policy.", "references": [ @@ -84,4 +84,4 @@ ] ] ] -} \ No newline at end of file +} From fcd446c3f8ed786d50d04a994aa22b7f4986a92d Mon Sep 17 00:00:00 2001 From: Sophie Dorval <42855086+SophieDorval@users.noreply.github.com> Date: Tue, 13 Apr 2021 10:05:38 -0400 Subject: [PATCH 133/193] fix rule 6.6 from gcp cis (#1267) Co-authored-by: Sophie --- .../gcp/rules/findings/cloudsql-instances-public-ips.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ScoutSuite/providers/gcp/rules/findings/cloudsql-instances-public-ips.json b/ScoutSuite/providers/gcp/rules/findings/cloudsql-instances-public-ips.json index 07df46728..725d806df 100644 --- a/ScoutSuite/providers/gcp/rules/findings/cloudsql-instances-public-ips.json +++ b/ScoutSuite/providers/gcp/rules/findings/cloudsql-instances-public-ips.json @@ -23,6 +23,11 @@ "cloudsql.projects.id.instances.id.public_ip", "notEmpty", "" + ], + [ + "cloudsql.projects.id.instances.id.public_ip", + "notEqual", + "None" ] ], "id_suffix": "public_ip" From 2dbe2750d812abdb8d7c4134f85457ae03ae2d8c Mon Sep 17 00:00:00 2001 From: Sophie Dorval <42855086+SophieDorval@users.noreply.github.com> Date: Tue, 13 Apr 2021 10:06:17 -0400 Subject: [PATCH 134/193] Enhancement/gcp fix errors (#1251) * fix error in kms keys * revert to old way * fix problem with rule * fix comments Co-authored-by: Sophie --- ScoutSuite/providers/gcp/resources/kms/keys.py | 5 ++++- .../kms-encryption-keys-not-rotated.json | 16 ++++++++-------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/ScoutSuite/providers/gcp/resources/kms/keys.py b/ScoutSuite/providers/gcp/resources/kms/keys.py index db9603a41..755cc3c5c 100755 --- a/ScoutSuite/providers/gcp/resources/kms/keys.py +++ b/ScoutSuite/providers/gcp/resources/kms/keys.py @@ -40,9 +40,12 @@ def _parse_key(self, raw_key): key_dict['algorithm'] = raw_key.get('primary', {}).get('algorithm', None) key_dict['next_rotation_datetime'] = raw_key.get('nextRotationTime', None) key_dict['purpose'] = raw_key['purpose'] + key_dict['rotation_period'] = raw_key.get('rotationPeriod', None) if key_dict['rotation_period']: - key_dict['rotation_period'] = int("".join(filter(str.isdigit, key_dict['rotation_period']))) + rotation_period = int("".join(filter(str.isdigit, key_dict['rotation_period']))) + # get values in days instead of seconds + key_dict['rotation_period'] = rotation_period//(24*3600) key_dict['next_rotation_time_days'] = None if key_dict['next_rotation_datetime']: diff --git a/ScoutSuite/providers/gcp/rules/findings/kms-encryption-keys-not-rotated.json b/ScoutSuite/providers/gcp/rules/findings/kms-encryption-keys-not-rotated.json index e1069435e..fd283eaa7 100644 --- a/ScoutSuite/providers/gcp/rules/findings/kms-encryption-keys-not-rotated.json +++ b/ScoutSuite/providers/gcp/rules/findings/kms-encryption-keys-not-rotated.json @@ -22,26 +22,26 @@ "or", [ "kms.projects.id.keyrings.id.keys.id.rotation_period", - "greaterThan", - "7776000" + "equal", + "None" ], [ "kms.projects.id.keyrings.id.keys.id.rotation_period", - "equal", - "None" + "moreThan", + "90" ] ], [ "or", [ "kms.projects.id.keyrings.id.keys.id.next_rotation_time_days", - "greaterThan", - "90" + "equal", + "None" ], [ "kms.projects.id.keyrings.id.keys.id.next_rotation_time_days", - "equal", - "None" + "moreThan", + "90" ] ] ], From fe7c7cbd09c360fc60a6e52ce5acb36488e17ab5 Mon Sep 17 00:00:00 2001 From: Sophie Dorval <42855086+SophieDorval@users.noreply.github.com> Date: Tue, 13 Apr 2021 10:54:09 -0400 Subject: [PATCH 135/193] Enhancement/gcp name fix (#1270) * fix error in kms keys * revert to old way * fix problem with rule * fix comments * added name value in dictionary Co-authored-by: Sophie --- .../providers/gcp/resources/iam/bindings_separation_duties.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ScoutSuite/providers/gcp/resources/iam/bindings_separation_duties.py b/ScoutSuite/providers/gcp/resources/iam/bindings_separation_duties.py index cd2be9bc4..bdbbc1f2e 100644 --- a/ScoutSuite/providers/gcp/resources/iam/bindings_separation_duties.py +++ b/ScoutSuite/providers/gcp/resources/iam/bindings_separation_duties.py @@ -11,11 +11,11 @@ async def fetch_all(self): raw_bindings = await self.facade.cloudresourcemanager.get_member_bindings(self.project_id) binding_id, binding = await self._parse_binding_separation(raw_bindings) self[binding_id] = binding - x=1 async def _parse_binding_separation(self, raw_bindings): binding_dict = {} binding_dict['id'] = self.project_id + binding_dict['name'] = self.project_id binding_dict["account_separation_duties"] = self.ensure_seperation_duties(raw_bindings) binding_dict["kms_separation_duties"] = self.ensure_KMS_seperation_duties(raw_bindings) From 535c5779ac4646754d3eed6226a70c9c37e2b6c1 Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Tue, 13 Apr 2021 19:21:31 +0200 Subject: [PATCH 136/193] Removed unused import due to previous refactoring --- ScoutSuite/providers/aws/resources/cloudwatch/metric_filters.py | 2 +- ScoutSuite/providers/aws/resources/directconnect/connections.py | 2 +- ScoutSuite/providers/aws/resources/ec2/ami.py | 2 +- ScoutSuite/providers/aws/resources/ec2/networkinterfaces.py | 2 +- ScoutSuite/providers/aws/resources/ec2/securitygroups.py | 2 +- ScoutSuite/providers/aws/resources/efs/filesystems.py | 2 +- ScoutSuite/providers/aws/resources/elasticache/cluster.py | 2 +- ScoutSuite/providers/aws/resources/elb/policies.py | 2 +- ScoutSuite/providers/aws/resources/kms/grants.py | 2 +- .../aws/resources/redshift/cluster_parameter_groups.py | 2 +- .../providers/aws/resources/redshift/cluster_parameters.py | 2 +- ScoutSuite/providers/aws/resources/redshift/clusters.py | 2 +- ScoutSuite/providers/aws/resources/route53/domains.py | 2 +- ScoutSuite/providers/aws/resources/route53/hosted_zones.py | 2 +- ScoutSuite/providers/aws/resources/s3/buckets.py | 2 +- ScoutSuite/providers/aws/resources/ses/identities.py | 2 +- ScoutSuite/providers/aws/resources/ses/identity_policies.py | 2 +- ScoutSuite/providers/aws/resources/vpc/peering_connections.py | 2 +- ScoutSuite/providers/aws/resources/vpcs.py | 2 +- 19 files changed, 19 insertions(+), 19 deletions(-) diff --git a/ScoutSuite/providers/aws/resources/cloudwatch/metric_filters.py b/ScoutSuite/providers/aws/resources/cloudwatch/metric_filters.py index aa3911cc5..a2b1f32f8 100644 --- a/ScoutSuite/providers/aws/resources/cloudwatch/metric_filters.py +++ b/ScoutSuite/providers/aws/resources/cloudwatch/metric_filters.py @@ -1,7 +1,7 @@ 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 -from ScoutSuite.providers.aws.utils import get_partition_name, format_arn +from ScoutSuite.providers.aws.utils import format_arn class MetricFilters(AWSResources): diff --git a/ScoutSuite/providers/aws/resources/directconnect/connections.py b/ScoutSuite/providers/aws/resources/directconnect/connections.py index e0e4534ba..b09955703 100755 --- a/ScoutSuite/providers/aws/resources/directconnect/connections.py +++ b/ScoutSuite/providers/aws/resources/directconnect/connections.py @@ -1,6 +1,6 @@ from ScoutSuite.providers.aws.facade.base import AWSFacade from ScoutSuite.providers.aws.resources.base import AWSResources -from ScoutSuite.providers.aws.utils import get_partition_name, format_arn +from ScoutSuite.providers.aws.utils import format_arn class Connections(AWSResources): diff --git a/ScoutSuite/providers/aws/resources/ec2/ami.py b/ScoutSuite/providers/aws/resources/ec2/ami.py index 45660121f..041f62cbe 100755 --- a/ScoutSuite/providers/aws/resources/ec2/ami.py +++ b/ScoutSuite/providers/aws/resources/ec2/ami.py @@ -1,6 +1,6 @@ from ScoutSuite.providers.aws.resources.base import AWSResources from ScoutSuite.providers.aws.facade.base import AWSFacade -from ScoutSuite.providers.aws.utils import get_partition_name, format_arn +from ScoutSuite.providers.aws.utils import format_arn class AmazonMachineImages(AWSResources): diff --git a/ScoutSuite/providers/aws/resources/ec2/networkinterfaces.py b/ScoutSuite/providers/aws/resources/ec2/networkinterfaces.py index 2961b9fb4..6fbbfe4bb 100755 --- a/ScoutSuite/providers/aws/resources/ec2/networkinterfaces.py +++ b/ScoutSuite/providers/aws/resources/ec2/networkinterfaces.py @@ -1,6 +1,6 @@ from ScoutSuite.providers.aws.resources.base import AWSResources from ScoutSuite.providers.aws.facade.base import AWSFacade -from ScoutSuite.providers.aws.utils import get_partition_name, format_arn +from ScoutSuite.providers.aws.utils import format_arn class NetworkInterfaces(AWSResources): diff --git a/ScoutSuite/providers/aws/resources/ec2/securitygroups.py b/ScoutSuite/providers/aws/resources/ec2/securitygroups.py index 0bb0a6b62..836c04aa5 100755 --- a/ScoutSuite/providers/aws/resources/ec2/securitygroups.py +++ b/ScoutSuite/providers/aws/resources/ec2/securitygroups.py @@ -1,6 +1,6 @@ from ScoutSuite.providers.aws.resources.base import AWSResources from ScoutSuite.providers.aws.facade.base import AWSFacade -from ScoutSuite.providers.aws.utils import get_partition_name, format_arn +from ScoutSuite.providers.aws.utils import format_arn from ScoutSuite.utils import manage_dictionary from ScoutSuite.core.fs import load_data diff --git a/ScoutSuite/providers/aws/resources/efs/filesystems.py b/ScoutSuite/providers/aws/resources/efs/filesystems.py index b69aac044..5777adf22 100755 --- a/ScoutSuite/providers/aws/resources/efs/filesystems.py +++ b/ScoutSuite/providers/aws/resources/efs/filesystems.py @@ -1,6 +1,6 @@ from ScoutSuite.providers.aws.facade.base import AWSFacade from ScoutSuite.providers.aws.resources.base import AWSResources -from ScoutSuite.providers.aws.utils import get_partition_name, format_arn +from ScoutSuite.providers.aws.utils import format_arn class FileSystems(AWSResources): diff --git a/ScoutSuite/providers/aws/resources/elasticache/cluster.py b/ScoutSuite/providers/aws/resources/elasticache/cluster.py index 4eabf96ac..289c3c205 100755 --- a/ScoutSuite/providers/aws/resources/elasticache/cluster.py +++ b/ScoutSuite/providers/aws/resources/elasticache/cluster.py @@ -1,6 +1,6 @@ from ScoutSuite.providers.aws.facade.base import AWSFacade from ScoutSuite.providers.aws.resources.base import AWSResources -from ScoutSuite.providers.aws.utils import get_partition_name, format_arn +from ScoutSuite.providers.aws.utils import format_arn class Clusters(AWSResources): diff --git a/ScoutSuite/providers/aws/resources/elb/policies.py b/ScoutSuite/providers/aws/resources/elb/policies.py index a5869379c..06982bb2d 100755 --- a/ScoutSuite/providers/aws/resources/elb/policies.py +++ b/ScoutSuite/providers/aws/resources/elb/policies.py @@ -1,6 +1,6 @@ from ScoutSuite.providers.aws.resources.base import AWSResources from ScoutSuite.providers.aws.facade.base import AWSFacade -from ScoutSuite.providers.aws.utils import get_partition_name, format_arn +from ScoutSuite.providers.aws.utils import format_arn from ScoutSuite.providers.utils import get_non_provider_id diff --git a/ScoutSuite/providers/aws/resources/kms/grants.py b/ScoutSuite/providers/aws/resources/kms/grants.py index e8c762920..b3ed47917 100755 --- a/ScoutSuite/providers/aws/resources/kms/grants.py +++ b/ScoutSuite/providers/aws/resources/kms/grants.py @@ -1,6 +1,6 @@ from ScoutSuite.providers.aws.facade.base import AWSFacade from ScoutSuite.providers.aws.resources.base import AWSResources -from ScoutSuite.providers.aws.utils import get_partition_name, format_arn +from ScoutSuite.providers.aws.utils import format_arn class Grants(AWSResources): diff --git a/ScoutSuite/providers/aws/resources/redshift/cluster_parameter_groups.py b/ScoutSuite/providers/aws/resources/redshift/cluster_parameter_groups.py index 50dff3de5..222f7ddb9 100755 --- a/ScoutSuite/providers/aws/resources/redshift/cluster_parameter_groups.py +++ b/ScoutSuite/providers/aws/resources/redshift/cluster_parameter_groups.py @@ -1,7 +1,7 @@ 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 ScoutSuite.providers.aws.utils import get_partition_name, format_arn +from ScoutSuite.providers.aws.utils import format_arn from .cluster_parameters import ClusterParameters diff --git a/ScoutSuite/providers/aws/resources/redshift/cluster_parameters.py b/ScoutSuite/providers/aws/resources/redshift/cluster_parameters.py index 56b4b9670..efd879ae7 100755 --- a/ScoutSuite/providers/aws/resources/redshift/cluster_parameters.py +++ b/ScoutSuite/providers/aws/resources/redshift/cluster_parameters.py @@ -1,6 +1,6 @@ from ScoutSuite.providers.aws.facade.base import AWSFacade from ScoutSuite.providers.aws.resources.base import AWSResources -from ScoutSuite.providers.aws.utils import get_partition_name, format_arn +from ScoutSuite.providers.aws.utils import format_arn class ClusterParameters(AWSResources): diff --git a/ScoutSuite/providers/aws/resources/redshift/clusters.py b/ScoutSuite/providers/aws/resources/redshift/clusters.py index 33e26b9ad..71f96a642 100755 --- a/ScoutSuite/providers/aws/resources/redshift/clusters.py +++ b/ScoutSuite/providers/aws/resources/redshift/clusters.py @@ -1,6 +1,6 @@ from ScoutSuite.providers.aws.facade.base import AWSFacade from ScoutSuite.providers.aws.resources.base import AWSResources -from ScoutSuite.providers.aws.utils import get_partition_name, format_arn +from ScoutSuite.providers.aws.utils import format_arn class Clusters(AWSResources): diff --git a/ScoutSuite/providers/aws/resources/route53/domains.py b/ScoutSuite/providers/aws/resources/route53/domains.py index e0fd64bf7..35dafb7ef 100755 --- a/ScoutSuite/providers/aws/resources/route53/domains.py +++ b/ScoutSuite/providers/aws/resources/route53/domains.py @@ -1,7 +1,7 @@ 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 -from ScoutSuite.providers.aws.utils import get_partition_name, format_arn +from ScoutSuite.providers.aws.utils import format_arn class Domains(AWSResources): diff --git a/ScoutSuite/providers/aws/resources/route53/hosted_zones.py b/ScoutSuite/providers/aws/resources/route53/hosted_zones.py index e7894c794..fc12e22b9 100755 --- a/ScoutSuite/providers/aws/resources/route53/hosted_zones.py +++ b/ScoutSuite/providers/aws/resources/route53/hosted_zones.py @@ -1,6 +1,6 @@ from ScoutSuite.providers.aws.resources.base import AWSResources from ScoutSuite.providers.aws.facade.base import AWSFacade -from ScoutSuite.providers.aws.utils import get_partition_name, format_arn +from ScoutSuite.providers.aws.utils import format_arn class HostedZones(AWSResources): diff --git a/ScoutSuite/providers/aws/resources/s3/buckets.py b/ScoutSuite/providers/aws/resources/s3/buckets.py index 19760f51c..4a912e06c 100755 --- a/ScoutSuite/providers/aws/resources/s3/buckets.py +++ b/ScoutSuite/providers/aws/resources/s3/buckets.py @@ -1,5 +1,5 @@ from ScoutSuite.providers.aws.resources.base import AWSResources -from ScoutSuite.providers.aws.utils import get_partition_name, format_arn +from ScoutSuite.providers.aws.utils import format_arn from ScoutSuite.providers.utils import get_non_provider_id diff --git a/ScoutSuite/providers/aws/resources/ses/identities.py b/ScoutSuite/providers/aws/resources/ses/identities.py index 86680469a..94efa9a26 100755 --- a/ScoutSuite/providers/aws/resources/ses/identities.py +++ b/ScoutSuite/providers/aws/resources/ses/identities.py @@ -1,7 +1,7 @@ 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 ScoutSuite.providers.aws.utils import get_partition_name, format_arn +from ScoutSuite.providers.aws.utils import format_arn from .identity_policies import IdentityPolicies diff --git a/ScoutSuite/providers/aws/resources/ses/identity_policies.py b/ScoutSuite/providers/aws/resources/ses/identity_policies.py index cd2ae4118..1ff4821f2 100755 --- a/ScoutSuite/providers/aws/resources/ses/identity_policies.py +++ b/ScoutSuite/providers/aws/resources/ses/identity_policies.py @@ -2,7 +2,7 @@ from ScoutSuite.providers.aws.facade.base import AWSFacade from ScoutSuite.providers.aws.resources.base import AWSResources -from ScoutSuite.providers.aws.utils import get_partition_name, format_arn +from ScoutSuite.providers.aws.utils import format_arn class IdentityPolicies(AWSResources): diff --git a/ScoutSuite/providers/aws/resources/vpc/peering_connections.py b/ScoutSuite/providers/aws/resources/vpc/peering_connections.py index 3907a72ab..7f7b155f7 100755 --- a/ScoutSuite/providers/aws/resources/vpc/peering_connections.py +++ b/ScoutSuite/providers/aws/resources/vpc/peering_connections.py @@ -1,6 +1,6 @@ from ScoutSuite.providers.aws.facade.base import AWSFacade from ScoutSuite.providers.aws.resources.base import AWSResources -from ScoutSuite.providers.aws.utils import get_partition_name, format_arn +from ScoutSuite.providers.aws.utils import format_arn class PeeringConnections(AWSResources): diff --git a/ScoutSuite/providers/aws/resources/vpcs.py b/ScoutSuite/providers/aws/resources/vpcs.py index 627891472..a7b3f3256 100755 --- a/ScoutSuite/providers/aws/resources/vpcs.py +++ b/ScoutSuite/providers/aws/resources/vpcs.py @@ -1,5 +1,5 @@ from ScoutSuite.providers.aws.resources.base import AWSCompositeResources -from ScoutSuite.providers.aws.utils import get_partition_name, format_arn +from ScoutSuite.providers.aws.utils import format_arn class Vpcs(AWSCompositeResources): """ From d5fa2799ca13f4c56043ab4e59708f3f0d977cc1 Mon Sep 17 00:00:00 2001 From: Sophie Date: Wed, 14 Apr 2021 12:00:08 -0400 Subject: [PATCH 137/193] fix id in html for dns --- .../gcp/services.dns.projects.id.managed_zones.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ScoutSuite/output/data/html/partials/gcp/services.dns.projects.id.managed_zones.html b/ScoutSuite/output/data/html/partials/gcp/services.dns.projects.id.managed_zones.html index bf7ead99e..87d54db50 100755 --- a/ScoutSuite/output/data/html/partials/gcp/services.dns.projects.id.managed_zones.html +++ b/ScoutSuite/output/data/html/partials/gcp/services.dns.projects.id.managed_zones.html @@ -19,9 +19,9 @@
      Key Algorithm: {{key_algorithm}} -
    • Key Type: {{key_type}}
    • -
    • Length: {{length}}
    • +
    • Key Algorithm: {{key_algorithm}}
    • +
    • Key Type: {{key_type}}
    • +
    • Length: {{length}}
    {{else}}
  • None
  • From 61facf05e433445e7c48aa1ef8470e988c98e115 Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Mon, 19 Apr 2021 16:28:22 +0200 Subject: [PATCH 138/193] Added 'scope' to IAM policy --- ScoutSuite/providers/aws/resources/iam/policies.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ScoutSuite/providers/aws/resources/iam/policies.py b/ScoutSuite/providers/aws/resources/iam/policies.py index 6ffb742da..8a7ae90df 100755 --- a/ScoutSuite/providers/aws/resources/iam/policies.py +++ b/ScoutSuite/providers/aws/resources/iam/policies.py @@ -15,5 +15,6 @@ def _parse_policy(self, raw_policy): policy['arn'] = raw_policy.pop('Arn') policy['PolicyDocument'] = raw_policy.pop('PolicyDocument') policy['attached_to'] = raw_policy.pop('attached_to') + policy['scope'] = 'AWS' if policy['arn'].startswith('arn:aws:iam::aws:') else 'Local' return policy['id'], policy From 444f304c8a8e4c911d7f09309cd30530858678eb Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Mon, 19 Apr 2021 16:45:39 +0200 Subject: [PATCH 139/193] Contemplate other partition cases --- ScoutSuite/providers/aws/resources/iam/policies.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ScoutSuite/providers/aws/resources/iam/policies.py b/ScoutSuite/providers/aws/resources/iam/policies.py index 8a7ae90df..17a4e075c 100755 --- a/ScoutSuite/providers/aws/resources/iam/policies.py +++ b/ScoutSuite/providers/aws/resources/iam/policies.py @@ -15,6 +15,6 @@ def _parse_policy(self, raw_policy): policy['arn'] = raw_policy.pop('Arn') policy['PolicyDocument'] = raw_policy.pop('PolicyDocument') policy['attached_to'] = raw_policy.pop('attached_to') - policy['scope'] = 'AWS' if policy['arn'].startswith('arn:aws:iam::aws:') else 'Local' + policy['scope'] = 'AWS' if policy['arn'].startswith(f"arn:{self.facade.partition}:iam::aws:") else 'Local' return policy['id'], policy From a664ca38a77eaa1075bed30deb43a42bcc48914c Mon Sep 17 00:00:00 2001 From: Sophie Date: Tue, 20 Apr 2021 12:03:13 -0400 Subject: [PATCH 140/193] blank commit --- ScoutSuite/providers/gcp/rules/rulesets/default.json | 1 + 1 file changed, 1 insertion(+) diff --git a/ScoutSuite/providers/gcp/rules/rulesets/default.json b/ScoutSuite/providers/gcp/rules/rulesets/default.json index de47991a9..0734847df 100755 --- a/ScoutSuite/providers/gcp/rules/rulesets/default.json +++ b/ScoutSuite/providers/gcp/rules/rulesets/default.json @@ -610,3 +610,4 @@ ] } } + From 1108809d5804213d3a92f6cd9c79d74814a0e39b Mon Sep 17 00:00:00 2001 From: Rogerio Bastos <2397391+rogeriobastos@users.noreply.github.com> Date: Mon, 3 May 2021 12:14:42 -0300 Subject: [PATCH 141/193] Downloda awscli from AWS official URL --- docker/bin/container-install-aws2.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/bin/container-install-aws2.sh b/docker/bin/container-install-aws2.sh index d8f9e521b..b35bb7a0d 100755 --- a/docker/bin/container-install-aws2.sh +++ b/docker/bin/container-install-aws2.sh @@ -15,7 +15,7 @@ echo -e "\n\nAWS2 CLI Installation Starting...\n\n" # install AWS CLI v2 # ===================================== cd ${TMPDIR} -curl "https://d1vvhvl2y92vvt.cloudfront.net/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" +curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" unzip awscliv2.zip ./aws/install --update From f0c3961d660a9d0a92955d6a9bade43331b22742 Mon Sep 17 00:00:00 2001 From: "Alessandro.Gonzalez" Date: Fri, 7 May 2021 16:04:40 +0100 Subject: [PATCH 142/193] Fix small bug with GCP monitoring alerts --- .../stackdrivermonitoring/monitoring_alert_policies.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ScoutSuite/providers/gcp/resources/stackdrivermonitoring/monitoring_alert_policies.py b/ScoutSuite/providers/gcp/resources/stackdrivermonitoring/monitoring_alert_policies.py index 7c03ca07b..4c1fa5ee0 100644 --- a/ScoutSuite/providers/gcp/resources/stackdrivermonitoring/monitoring_alert_policies.py +++ b/ScoutSuite/providers/gcp/resources/stackdrivermonitoring/monitoring_alert_policies.py @@ -28,7 +28,7 @@ def _parse_alert_policy(self, raw_alert_policies): def _specific_alert_policy_present(self, alert_policies): for alert_policy in alert_policies: - for condition in alert_policy.conditions._values: + for condition in alert_policy.conditions: if condition.condition_threshold.filter == 'metric.type=\"logging.googleapis.com/user/\"' and alert_policy.enabled.value: return True From a15cff3bd9845bcf9046a02241525366e784f7ba Mon Sep 17 00:00:00 2001 From: "Alessandro.Gonzalez" Date: Fri, 7 May 2021 16:31:27 +0100 Subject: [PATCH 143/193] Fixed small typo in new rule --- .../findings/iam-lightspin-user-action-denied-for-group.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ScoutSuite/providers/aws/rules/findings/iam-lightspin-user-action-denied-for-group.json b/ScoutSuite/providers/aws/rules/findings/iam-lightspin-user-action-denied-for-group.json index 82ec71e28..0d9ea6cf3 100644 --- a/ScoutSuite/providers/aws/rules/findings/iam-lightspin-user-action-denied-for-group.json +++ b/ScoutSuite/providers/aws/rules/findings/iam-lightspin-user-action-denied-for-group.json @@ -1,6 +1,6 @@ { "description": "Policy with Denied User Actions for Group Objects", - "rationale": "When a deny policy is specified for User object actions on a group resource, this will only affect the specific IAM group but not the group members. This could lead to privilege escalation if the user can perform other privileged actions targeting the sepecific members of the group.", + "rationale": "When a deny policy is specified for User object actions on a group resource, this will only affect the specific IAM group but not the group members. This could lead to privilege escalation if the user can perform other privileged actions targeting the specific members of the group.", "remediation": "Define all relevant users in the resource field of the affected policies to avoid ineffective IAM actions and deny all group actions. The alternative would be to use the condition \"iam:ResourceTag\" in the policy.", "references": [ "https://blog.lightspin.io/aws-iam-groups-authorization-bypass", From 9b4ca9bd79aff6ca716ea5c55a0477772251419a Mon Sep 17 00:00:00 2001 From: x4v13r64 Date: Tue, 18 May 2021 16:32:15 +0200 Subject: [PATCH 144/193] Fix bug in evaluation --- .../providers/gcp/resources/cloudsql/database_instances.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ScoutSuite/providers/gcp/resources/cloudsql/database_instances.py b/ScoutSuite/providers/gcp/resources/cloudsql/database_instances.py index 22857213a..17d615515 100755 --- a/ScoutSuite/providers/gcp/resources/cloudsql/database_instances.py +++ b/ScoutSuite/providers/gcp/resources/cloudsql/database_instances.py @@ -128,8 +128,8 @@ def _postgres_log_min_error_statement_flags(self, raw_instance): if 'POSTGRES' in raw_instance['databaseVersion']: for flag in raw_instance['settings']['databaseFlags']: if flag['name'] == 'log_min_error_statement' and flag['value'] is not None: - return True - return False + return False + return True def _postgres_log_temp_files_flags_0(self, raw_instance): if 'POSTGRES' in raw_instance['databaseVersion']: From c3a15fe6d5fd3b1757a44a98880335ab86d22140 Mon Sep 17 00:00:00 2001 From: x4v13r64 Date: Tue, 18 May 2021 17:29:26 +0200 Subject: [PATCH 145/193] Fix bug in evaluation logic --- .../resources/cloudsql/database_instances.py | 40 ++++++++++++------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/ScoutSuite/providers/gcp/resources/cloudsql/database_instances.py b/ScoutSuite/providers/gcp/resources/cloudsql/database_instances.py index 17d615515..509318cc1 100755 --- a/ScoutSuite/providers/gcp/resources/cloudsql/database_instances.py +++ b/ScoutSuite/providers/gcp/resources/cloudsql/database_instances.py @@ -115,39 +115,49 @@ def _mysql_local_infile_flag_off(self, raw_instance): def _check_database_type(self, raw_instance): if 'POSTGRES' in raw_instance['databaseVersion']: return False - return True + return None def _postgres_flags_on(self, raw_instance, flag_name: str): if 'POSTGRES' in raw_instance['databaseVersion']: for flag in raw_instance['settings']['databaseFlags']: - if flag['name'] == flag_name and flag['value'] == 'off': - return False - return True + if flag['name'] == flag_name and flag['value'] != 'off': + return True + return False + else: + return None def _postgres_log_min_error_statement_flags(self, raw_instance): if 'POSTGRES' in raw_instance['databaseVersion']: for flag in raw_instance['settings']['databaseFlags']: if flag['name'] == 'log_min_error_statement' and flag['value'] is not None: - return False - return True + return True + return False + else: + return None def _postgres_log_temp_files_flags_0(self, raw_instance): if 'POSTGRES' in raw_instance['databaseVersion']: for flag in raw_instance['settings']['databaseFlags']: - if flag['name'] == 'log_temp_files' and flag['value'] != 0: - return False - return True + if flag['name'] == 'log_temp_files' and flag['value'] == 0: + return True + return False + else: + return None def _postgres_log_min_duration_statement_flags_1(self, raw_instance): if 'POSTGRES' in raw_instance['databaseVersion']: for flag in raw_instance['settings']['databaseFlags']: - if flag['name'] == 'log_min_duration_statement' and flag['value'] != -1: - return False - return True + if flag['name'] == 'log_min_duration_statement' and flag['value'] == -1: + return True + return False + else: + return None def _sqlservers_cross_db_ownership_chaining_flag_off(self, raw_instance, flag_name: str): if 'SQLSERVER' in raw_instance['databaseVersion']: for flag in raw_instance['settings']['databaseFlags']: - if flag['name'] == flag_name and flag['value'] == 'on': - return False - return True + if flag['name'] == flag_name and flag['value'] == 'off': + return True + return False + else: + return None From e4de10eed0f2bbda27d26f5cdfdae86eb7ac53aa Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Mon, 24 May 2021 13:13:50 +0200 Subject: [PATCH 146/193] Added case where SQL database threat detection period is 0/unlimited --- ...sqldatabase-databases-threat-detection-low-retention.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ScoutSuite/providers/azure/rules/findings/sqldatabase-databases-threat-detection-low-retention.json b/ScoutSuite/providers/azure/rules/findings/sqldatabase-databases-threat-detection-low-retention.json index 9bb02201a..5f880e6d7 100755 --- a/ScoutSuite/providers/azure/rules/findings/sqldatabase-databases-threat-detection-low-retention.json +++ b/ScoutSuite/providers/azure/rules/findings/sqldatabase-databases-threat-detection-low-retention.json @@ -13,6 +13,11 @@ "path": "sqldatabase.subscriptions.id.servers.id.databases.id", "conditions": [ "and", + [ + "sqldatabase.subscriptions.id.servers.id.databases.id.threat_detection.retention_days", + "notEqual", + "0" + ], [ "sqldatabase.subscriptions.id.servers.id.databases.id.threat_detection.retention_days", "lessThan", From bd75c2c9cd87b7c13f453ab98924a0034e90350a Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Mon, 24 May 2021 13:14:23 +0200 Subject: [PATCH 147/193] Added case where SQL server threat detection period is 0/unlimited --- .../sqldatabase-servers-threat-detection-low-retention.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ScoutSuite/providers/azure/rules/findings/sqldatabase-servers-threat-detection-low-retention.json b/ScoutSuite/providers/azure/rules/findings/sqldatabase-servers-threat-detection-low-retention.json index 4faaea54e..065be89d0 100755 --- a/ScoutSuite/providers/azure/rules/findings/sqldatabase-servers-threat-detection-low-retention.json +++ b/ScoutSuite/providers/azure/rules/findings/sqldatabase-servers-threat-detection-low-retention.json @@ -13,6 +13,11 @@ "path": "sqldatabase.subscriptions.id.servers.id", "conditions": [ "and", + [ + "sqldatabase.subscriptions.id.servers.id.threat_detection.retention_days", + "notEqual", + "0" + ], [ "sqldatabase.subscriptions.id.servers.id.threat_detection.retention_days", "lessThan", From 7c290f29e9e79ceedc7e8e6931f7c044e0395919 Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Mon, 24 May 2021 13:15:08 +0200 Subject: [PATCH 148/193] Added case where SQL server auditing retention period is 0/unlimited --- .../findings/sqldatabase-servers-auditing-low-retention.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ScoutSuite/providers/azure/rules/findings/sqldatabase-servers-auditing-low-retention.json b/ScoutSuite/providers/azure/rules/findings/sqldatabase-servers-auditing-low-retention.json index c4fe98624..72551cbfc 100755 --- a/ScoutSuite/providers/azure/rules/findings/sqldatabase-servers-auditing-low-retention.json +++ b/ScoutSuite/providers/azure/rules/findings/sqldatabase-servers-auditing-low-retention.json @@ -13,6 +13,11 @@ "path": "sqldatabase.subscriptions.id.servers.id", "conditions": [ "and", + [ + "sqldatabase.subscriptions.id.servers.id.auditing.retention_days", + "notEqual", + "0" + ], [ "sqldatabase.subscriptions.id.servers.id.auditing.retention_days", "lessThan", From 5b649f6f0944521ff0ffb470cf87e036a418c7dc Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Mon, 24 May 2021 14:04:34 +0200 Subject: [PATCH 149/193] Expose partition in credential report --- ScoutSuite/providers/aws/resources/iam/credentialreports.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ScoutSuite/providers/aws/resources/iam/credentialreports.py b/ScoutSuite/providers/aws/resources/iam/credentialreports.py index 966c0180c..c0f77af84 100755 --- a/ScoutSuite/providers/aws/resources/iam/credentialreports.py +++ b/ScoutSuite/providers/aws/resources/iam/credentialreports.py @@ -37,6 +37,8 @@ async def _parse_credential_reports(self, raw_credential_report): else: raw_credential_report['mfa_active_hardware'] = False + raw_credential_report['partition'] = self.facade.partition + return raw_credential_report['id'], raw_credential_report async def _user_has_hardware_mfa_devices(self, username): From 7f5df6aebccee062963ad4048647cfed6515f348 Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Mon, 24 May 2021 14:06:08 +0200 Subject: [PATCH 150/193] Check if not GovCloud partition for root account without hardware MFA finding --- .../aws/rules/findings/iam-root-account-no-hardware-mfa.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ScoutSuite/providers/aws/rules/findings/iam-root-account-no-hardware-mfa.json b/ScoutSuite/providers/aws/rules/findings/iam-root-account-no-hardware-mfa.json index c1a8fe3ce..18fcc06c1 100644 --- a/ScoutSuite/providers/aws/rules/findings/iam-root-account-no-hardware-mfa.json +++ b/ScoutSuite/providers/aws/rules/findings/iam-root-account-no-hardware-mfa.json @@ -44,6 +44,11 @@ "false", "" ] + ], + [ + "iam.credential_reports.id.partition", + "notEqual", + "aws-us-gov" ] ], "keys": [ From 9dc915e000b7fadd01920e88bc99236aa6880967 Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Mon, 24 May 2021 14:06:39 +0200 Subject: [PATCH 151/193] Check if not GovCloud partition for root account without MFA finding --- .../aws/rules/findings/iam-root-account-no-mfa.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ScoutSuite/providers/aws/rules/findings/iam-root-account-no-mfa.json b/ScoutSuite/providers/aws/rules/findings/iam-root-account-no-mfa.json index 9d6da2c60..5c604feb4 100755 --- a/ScoutSuite/providers/aws/rules/findings/iam-root-account-no-mfa.json +++ b/ScoutSuite/providers/aws/rules/findings/iam-root-account-no-mfa.json @@ -31,6 +31,11 @@ "iam.credential_reports.id.mfa_active", "notTrue", "" + ], + [ + "iam.credential_reports.id.partition", + "notEqual", + "aws-us-gov" ] ], "keys": [ From 9a1580f8d3550a2a3b326ff1f53a1936375f2a40 Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Fri, 11 Jun 2021 17:32:10 +0200 Subject: [PATCH 152/193] Added formatted service name for CodeBuild --- ScoutSuite/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ScoutSuite/utils.py b/ScoutSuite/utils.py index ab48c82f6..ab945185a 100755 --- a/ScoutSuite/utils.py +++ b/ScoutSuite/utils.py @@ -17,6 +17,7 @@ 'cloudwatch': 'CloudWatch', 'cloudfront': 'CloudFront', 'credentials': 'Credentials', + 'codebuild': 'CodeBuild', 'cognito': 'Cognito', 'config': 'Config', 'directconnect': 'Direct Connect', From b81f1785dd3eb3e9b45ffc0f78f9e1ded7196cb1 Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Fri, 11 Jun 2021 17:33:31 +0200 Subject: [PATCH 153/193] Added preprocessing step to check security groups usage with CodeBuild --- ScoutSuite/providers/aws/provider.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/ScoutSuite/providers/aws/provider.py b/ScoutSuite/providers/aws/provider.py index f9d4c81af..1e2395130 100755 --- a/ScoutSuite/providers/aws/provider.py +++ b/ScoutSuite/providers/aws/provider.py @@ -82,6 +82,9 @@ def preprocessing(self, ip_ranges=None, ip_ranges_name_key=None): if 'ec2' in self.service_list and 'vpc' in self.service_list: self._match_instances_and_vpcs() self._match_instances_and_subnets() + + if 'ec2' in self.service_list and 'codebuild' in self.service_list: + self._update_sg_usage_codebuild() if 'awslambda' in self.service_list and 'iam' in self.service_list: self._match_lambdas_and_roles() @@ -831,3 +834,17 @@ def parse_elb_policies_callback(self, current_config, path, current_path, region policy['protocols'] = protocols policy['options'] = options policy['ciphers'] = ciphers + + def _update_sg_usage_codebuild(self): + try: + for region in self.services['codebuild']['regions']: + for codebuild_project in self.services['codebuild']['regions'][region]['build_projects']: + if 'vpc' in self.services['codebuild']['regions'][region]['build_projects'][codebuild_project] and 'security_groups' in self.services['codebuild']['regions'][region]['build_projects'][codebuild_project]: + cb_project = self.services['codebuild']['regions'][region]['build_projects'][codebuild_project] + for cb_project_sg in cb_project['security_groups']: + manage_dictionary(self.services['ec2']['regions'][region]['vpcs'][cb_project['vpc']]['security_groups'][cb_project_sg], 'used_by', {'resource_type': {'codebuild_project': []}}) + self.services['ec2']['regions'][region]['vpcs'][cb_project['vpc']]['security_groups'][cb_project_sg]['used_by']['resource_type']['codebuild_project'].append({ + 'id': cb_project['arn'], 'name': cb_project['name'] + }) + except Exception as e: + print_exception(f'Failed to update security group usage for CodeBuild: {e}') From 5c00f1c6b6ab0c3120d35372680a2afe42ff6ea7 Mon Sep 17 00:00:00 2001 From: Viatcheslav Zhilin Date: Fri, 11 Jun 2021 17:35:27 +0200 Subject: [PATCH 154/193] Implemented support for CodeBuild service --- ScoutSuite/providers/aws/facade/base.py | 2 ++ ScoutSuite/providers/aws/facade/codebuild.py | 30 +++++++++++++++++++ .../aws/resources/codebuild/__init__.py | 0 .../providers/aws/resources/codebuild/base.py | 13 ++++++++ .../aws/resources/codebuild/build_projects.py | 27 +++++++++++++++++ ScoutSuite/providers/aws/services.py | 2 ++ 6 files changed, 74 insertions(+) create mode 100644 ScoutSuite/providers/aws/facade/codebuild.py create mode 100644 ScoutSuite/providers/aws/resources/codebuild/__init__.py create mode 100644 ScoutSuite/providers/aws/resources/codebuild/base.py create mode 100644 ScoutSuite/providers/aws/resources/codebuild/build_projects.py diff --git a/ScoutSuite/providers/aws/facade/base.py b/ScoutSuite/providers/aws/facade/base.py index c4e17a59d..2cf452881 100755 --- a/ScoutSuite/providers/aws/facade/base.py +++ b/ScoutSuite/providers/aws/facade/base.py @@ -7,6 +7,7 @@ from ScoutSuite.providers.aws.facade.cloudtrail import CloudTrailFacade from ScoutSuite.providers.aws.facade.cloudwatch import CloudWatch from ScoutSuite.providers.aws.facade.cloudfront import CloudFront +from ScoutSuite.providers.aws.facade.codebuild import CodeBuild from ScoutSuite.providers.aws.facade.config import ConfigFacade from ScoutSuite.providers.aws.facade.directconnect import DirectConnectFacade from ScoutSuite.providers.aws.facade.dynamodb import DynamoDBFacade @@ -257,6 +258,7 @@ def _instantiate_facades(self): self.elasticache = ElastiCacheFacade(self.session) self.route53 = Route53Facade(self.session) self.cloudfront = CloudFront(self.session) + self.codebuild = CodeBuild(self.session) self.elb = ELBFacade(self.session) self.elbv2 = ELBv2Facade(self.session) self.iam = IAMFacade(self.session) diff --git a/ScoutSuite/providers/aws/facade/codebuild.py b/ScoutSuite/providers/aws/facade/codebuild.py new file mode 100644 index 000000000..befc59c6a --- /dev/null +++ b/ScoutSuite/providers/aws/facade/codebuild.py @@ -0,0 +1,30 @@ +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, map_concurrently + + +class CodeBuild(AWSBaseFacade): + async def get_projects(self, region: str): + codebuild_client = AWSFacadeUtils.get_client('codebuild', self.session, region) + try: + projects = await run_concurrently(lambda: codebuild_client.list_projects()['projects']) + except Exception as e: + print_exception(f'Failed to get CodeBuild projects: {e}') + return [] + else: + if not projects: + return [] + return await map_concurrently(self._get_project_details, projects, region=region) + + async def _get_project_details(self, project: str, region: str): + codebuild_client = AWSFacadeUtils.get_client('codebuild', self.session, region) + try: + project_details = await run_concurrently(lambda: codebuild_client.batch_get_projects(names=[project])) + except Exception as e: + print_exception(f'Failed to get CodeBuild project details: {e}') + return project + else: + project_details.pop('ResponseMetadata') + project_details.pop('projectsNotFound') + return project_details diff --git a/ScoutSuite/providers/aws/resources/codebuild/__init__.py b/ScoutSuite/providers/aws/resources/codebuild/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/ScoutSuite/providers/aws/resources/codebuild/base.py b/ScoutSuite/providers/aws/resources/codebuild/base.py new file mode 100644 index 000000000..8431d5e2c --- /dev/null +++ b/ScoutSuite/providers/aws/resources/codebuild/base.py @@ -0,0 +1,13 @@ +from ScoutSuite.providers.aws.facade.base import AWSFacade +from ScoutSuite.providers.aws.resources.regions import Regions + +from .build_projects import BuildProjects + + +class CodeBuild(Regions): + _children = [ + (BuildProjects, 'build_projects') + ] + + def __init__(self, facade: AWSFacade): + super().__init__('codebuild', facade) diff --git a/ScoutSuite/providers/aws/resources/codebuild/build_projects.py b/ScoutSuite/providers/aws/resources/codebuild/build_projects.py new file mode 100644 index 000000000..98238f4cc --- /dev/null +++ b/ScoutSuite/providers/aws/resources/codebuild/build_projects.py @@ -0,0 +1,27 @@ +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 BuildProjects(AWSResources): + def __init__(self, facade: AWSFacade, region: str): + super().__init__(facade) + self.region = region + + async def fetch_all(self): + raw_projects = await self.facade.codebuild.get_projects(self.region) + for list_raw_project in raw_projects: + for raw_project in list_raw_project.get('projects'): + id, build_project = self._parse_build_projects(raw_project) + self[id] = build_project + + def _parse_build_projects(self, raw_build_project): + project_dict = {} + project_dict['id'] = raw_build_project.get('arn') + project_dict['arn'] = raw_build_project.get('arn') + project_dict['name'] = raw_build_project.get('name') + if 'vpcConfig' in raw_build_project: + project_dict['vpc'] = raw_build_project.get('vpcConfig').get('vpcId') + project_dict['subnets'] = raw_build_project.get('vpcConfig').get('subnets') + project_dict['security_groups'] = raw_build_project.get('vpcConfig').get('securityGroupIds') + return project_dict['id'], project_dict diff --git a/ScoutSuite/providers/aws/services.py b/ScoutSuite/providers/aws/services.py index b1a8d9d08..4b9656b28 100755 --- a/ScoutSuite/providers/aws/services.py +++ b/ScoutSuite/providers/aws/services.py @@ -5,6 +5,7 @@ from ScoutSuite.providers.aws.resources.cloudtrail.base import CloudTrail from ScoutSuite.providers.aws.resources.cloudwatch.base import CloudWatch from ScoutSuite.providers.aws.resources.cloudfront.base import CloudFront +from ScoutSuite.providers.aws.resources.codebuild.base import CodeBuild from ScoutSuite.providers.aws.resources.config.base import Config from ScoutSuite.providers.aws.resources.directconnect.base import DirectConnect from ScoutSuite.providers.aws.resources.dynamodb.base import DynamoDB @@ -94,6 +95,7 @@ def __init__(self, credentials=None, **kwargs): self.cloudtrail = CloudTrail(facade) self.cloudwatch = CloudWatch(facade) self.cloudfront = CloudFront(facade) + self.codebuild = CodeBuild(facade) self.config = Config(facade) self.directconnect = DirectConnect(facade) self.dynamodb = DynamoDB(facade) From 2cdd25ac2b8b0a0974a640e561509cca62e92ee0 Mon Sep 17 00:00:00 2001 From: lm-t Date: Wed, 14 Jul 2021 14:20:12 -0700 Subject: [PATCH 155/193] fixed typo --- .../rules/findings/cloudtrail-no-cloudwatch-integration.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ScoutSuite/providers/aws/rules/findings/cloudtrail-no-cloudwatch-integration.json b/ScoutSuite/providers/aws/rules/findings/cloudtrail-no-cloudwatch-integration.json index daed6594e..2f15c9b66 100644 --- a/ScoutSuite/providers/aws/rules/findings/cloudtrail-no-cloudwatch-integration.json +++ b/ScoutSuite/providers/aws/rules/findings/cloudtrail-no-cloudwatch-integration.json @@ -1,6 +1,6 @@ { "description": "Trail Is Not Integrated with CloudWatch", - "rationale": "The lack of integration with CloudWatch hinders ral-time and historic activity logging as well as not allowing the configuration of alarms and notifications for anomalous account activity.", + "rationale": "The lack of integration with CloudWatch hinders real-time and historic activity logging as well as not allowing the configuration of alarms and notifications for anomalous account activity.", "remediation": "Configure each Trail to have a CloudWatch Logs group attached", "compliance": [ { @@ -47,4 +47,4 @@ ] ], "id_suffix": "TrailCloudwatchNoIntegration" -} \ No newline at end of file +} From e69062f8092994bdbb69c9e179c4b5d7cb470c17 Mon Sep 17 00:00:00 2001 From: x4v13r64 Date: Thu, 29 Jul 2021 16:26:08 +0200 Subject: [PATCH 156/193] Resolve https://github.com/nccgroup/ScoutSuite/issues/1317 --- .../services.kubernetesengine.clusters.html | 52 ++++++++++--------- ScoutSuite/providers/gcp/facade/gke.py | 6 +-- ScoutSuite/providers/gcp/metadata.json | 2 +- .../providers/gcp/resources/gke/base.py | 12 +---- .../providers/gcp/resources/gke/clusters.py | 7 +-- .../providers/gcp/resources/gke/zones.py | 8 --- ...esengine-basic-authentication-enabled.json | 4 +- ...ne-certificate-authentication-enabled.json | 4 +- ...netesengine-cluster-alias-ip-disabled.json | 4 +- ...ubernetesengine-cluster-has-no-labels.json | 4 +- ...rnetesengine-cluster-logging-disabled.json | 4 +- ...r-master-authorized-networks-disabled.json | 4 +- ...tesengine-cluster-monitoring-disabled.json | 4 +- ...ngine-cluster-network-policy-disabled.json | 4 +- ...r-pod-security-policy-config-disabled.json | 4 +- ...luster-private-google-access-disabled.json | 4 +- .../kubernetesengine-dashboard-enabled.json | 4 +- ...esengine-default-service-account-used.json | 4 +- .../kubernetesengine-legacy-abac-enabled.json | 4 +- ...ine-legacy-metadata-endpoints-enabled.json | 6 +-- ...netesengine-node-auto-repair-disabled.json | 6 +-- ...etesengine-node-auto-upgrade-disabled.json | 6 +-- ...-node-container-optimized-os-not-used.json | 4 +- ...rnetesengine-private-cluster-disabled.json | 4 +- .../kubernetesengine-scopes-not-limited.json | 4 +- 25 files changed, 78 insertions(+), 91 deletions(-) delete mode 100644 ScoutSuite/providers/gcp/resources/gke/zones.py diff --git a/ScoutSuite/output/data/html/partials/gcp/services.kubernetesengine.clusters.html b/ScoutSuite/output/data/html/partials/gcp/services.kubernetesengine.clusters.html index 0b426a6f8..e179fe005 100644 --- a/ScoutSuite/output/data/html/partials/gcp/services.kubernetesengine.clusters.html +++ b/ScoutSuite/output/data/html/partials/gcp/services.kubernetesengine.clusters.html @@ -1,27 +1,29 @@ -