From 57b405b48d30c11778df1d61396b5c16544f375d Mon Sep 17 00:00:00 2001 From: x4v13r64 Date: Wed, 17 Jan 2024 14:59:16 +0100 Subject: [PATCH 1/4] Properly evaluate bucket policy --- ...ices.cloudstorage.projects.id.buckets.html | 3 +- .../providers/gcp/facade/cloudstorage.py | 30 ++++++++++++++++--- .../gcp/resources/cloudstorage/buckets.py | 1 + .../findings/cloudstorage-bucket-member.json | 11 ++----- ...ge-bucket-no-public-access-prevention.json | 13 +++----- requirements.txt | 1 + 6 files changed, 37 insertions(+), 22 deletions(-) diff --git a/ScoutSuite/output/data/html/partials/gcp/services.cloudstorage.projects.id.buckets.html b/ScoutSuite/output/data/html/partials/gcp/services.cloudstorage.projects.id.buckets.html index 0256d60a7..a12248917 100755 --- a/ScoutSuite/output/data/html/partials/gcp/services.cloudstorage.projects.id.buckets.html +++ b/ScoutSuite/output/data/html/partials/gcp/services.cloudstorage.projects.id.buckets.html @@ -12,7 +12,8 @@

Information

Storage Class: {{storage_class}}
Logging: {{convert_bool_to_enabled logging_enabled}}
Versioning: {{convert_bool_to_enabled versioning_enabled}}
-
Public Access Prevention: {{public_access_prevention}}
+
Public Access Prevention: {{public_access_prevention}}
+
Effective Public Access Prevention: {{convert_bool_to_enabled effective_public_access_prevention}}
Uniform Bucket-Level Access: {{convert_bool_to_enabled uniform_bucket_level_access}}
diff --git a/ScoutSuite/providers/gcp/facade/cloudstorage.py b/ScoutSuite/providers/gcp/facade/cloudstorage.py index 359d632a6..68f5753e8 100755 --- a/ScoutSuite/providers/gcp/facade/cloudstorage.py +++ b/ScoutSuite/providers/gcp/facade/cloudstorage.py @@ -1,6 +1,9 @@ from google.cloud import storage +from google.cloud import orgpolicy_v2 from google.api_core.gapic_v1.client_info import ClientInfo +from ScoutSuite.providers.gcp.facade.utils import GCPFacadeUtils + from ScoutSuite.core.console import print_exception from ScoutSuite.providers.utils import run_concurrently, get_and_set_concurrently from ScoutSuite.utils import get_user_agent @@ -14,18 +17,25 @@ def get_client(self, project_id: str): client_info=client_info) return client + def get_org_policy_client(self): + client_info = ClientInfo(user_agent=get_user_agent()) + client = orgpolicy_v2.OrgPolicyClient(client_info=client_info) + return client + async def get_buckets(self, project_id: str): try: client = self.get_client(project_id) buckets = await run_concurrently(lambda: list(client.list_buckets())) - await get_and_set_concurrently([self._get_and_set_bucket_logging, - self._get_and_set_bucket_iam_policy], buckets) + await get_and_set_concurrently([self._get_and_set_bucket_logging, + self._get_and_set_bucket_iam_policy, + self._get_and_set_public_access_prevention], + buckets, project_id=project_id) return buckets except Exception as e: print_exception(f'Failed to retrieve storage buckets: {e}') return [] - async def _get_and_set_bucket_logging(self, bucket): + async def _get_and_set_bucket_logging(self, bucket, **kwargs): try: bucket_logging = await run_concurrently(lambda: bucket.get_logging()) setattr(bucket, 'logging', bucket_logging) @@ -33,10 +43,22 @@ async def _get_and_set_bucket_logging(self, bucket): print_exception(f'Failed to retrieve bucket logging: {e}') setattr(bucket, 'logging', None) - async def _get_and_set_bucket_iam_policy(self, bucket): + async def _get_and_set_bucket_iam_policy(self, bucket, **kwargs): try: bucket_iam_policy = await run_concurrently(lambda: bucket.get_iam_policy()) setattr(bucket, 'iam_policy', bucket_iam_policy) except Exception as e: print_exception(f'Failed to retrieve bucket IAM policy: {e}') setattr(bucket, 'iam_policy', None) + + async def _get_and_set_public_access_prevention(self, bucket, project_id): + try: + org_client = self.get_org_policy_client() + request = orgpolicy_v2.GetEffectivePolicyRequest(name=f"projects/{project_id}/policies/storage.publicAccessPrevention") + response = org_client.get_effective_policy(request=request) + setattr(bucket, 'effective_public_access_prevention', + 'enforce: true' in str(response.spec.rules) + ) + except Exception as e: + print_exception(f'Failed to retrieve organization policy storage.publicAccessPrevention: {e}') + setattr(bucket, 'effective_public_access_prevention', None) diff --git a/ScoutSuite/providers/gcp/resources/cloudstorage/buckets.py b/ScoutSuite/providers/gcp/resources/cloudstorage/buckets.py index 8e813e6e2..4f19ba5b6 100755 --- a/ScoutSuite/providers/gcp/resources/cloudstorage/buckets.py +++ b/ScoutSuite/providers/gcp/resources/cloudstorage/buckets.py @@ -28,6 +28,7 @@ def _parse_bucket(self, raw_bucket): bucket_dict['logging_enabled'] = raw_bucket.logging is not None bucket_dict['public_access_prevention'] = raw_bucket.iam_configuration.public_access_prevention + bucket_dict['effective_public_access_prevention'] = raw_bucket.effective_public_access_prevention iam_configuration = raw_bucket.iam_configuration.get('uniformBucketLevelAccess') or \ raw_bucket.iam_configuration.get('bucketPolicyOnly') diff --git a/ScoutSuite/providers/gcp/rules/findings/cloudstorage-bucket-member.json b/ScoutSuite/providers/gcp/rules/findings/cloudstorage-bucket-member.json index 30abee45c..c55b3cf55 100755 --- a/ScoutSuite/providers/gcp/rules/findings/cloudstorage-bucket-member.json +++ b/ScoutSuite/providers/gcp/rules/findings/cloudstorage-bucket-member.json @@ -37,14 +37,9 @@ ] ], [ - "cloudstorage.projects.id.buckets.id.public_access_prevention", - "notEqual", - "enforced" - ], - [ - "cloudstorage.projects.id.buckets.id.public_access_prevention", - "notEqual", - "inherited" + "cloudstorage.projects.id.buckets.id.effective_public_access_prevention", + "notTrue", + "" ] ], "key": "cloudstorage-bucket-_ARG_0_", diff --git a/ScoutSuite/providers/gcp/rules/findings/cloudstorage-bucket-no-public-access-prevention.json b/ScoutSuite/providers/gcp/rules/findings/cloudstorage-bucket-no-public-access-prevention.json index 34a6b16df..17c151cdd 100755 --- a/ScoutSuite/providers/gcp/rules/findings/cloudstorage-bucket-no-public-access-prevention.json +++ b/ScoutSuite/providers/gcp/rules/findings/cloudstorage-bucket-no-public-access-prevention.json @@ -9,15 +9,10 @@ "conditions": [ "and", [ - "cloudstorage.projects.id.buckets.id.public_access_prevention", - "notEqual", - "enforced" - ], - [ - "cloudstorage.projects.id.buckets.id.public_access_prevention", - "notEqual", - "inherited" + "cloudstorage.projects.id.buckets.id.effective_public_access_prevention", + "notTrue", + "" ] ], - "id_suffix": "public_access_prevention" + "class_suffix": "public_access_prevention" } diff --git a/requirements.txt b/requirements.txt index 7f53a4dfc..6c303c287 100755 --- a/requirements.txt +++ b/requirements.txt @@ -23,6 +23,7 @@ google-cloud-monitoring==1.1.0 google-cloud-resource-manager>=0.28.3 google-cloud-storage>=1.13.2 google-cloud-kms==1.3.0 +google-cloud-org-policy>=1.10.0 ## API Client Libraries google-api-python-client>=2.47.0 oauth2client>=4.1.3 From 4ad3fa8e8d7711b9d2aedd60b6eeaf3f78fcc6a3 Mon Sep 17 00:00:00 2001 From: x4v13r64 Date: Wed, 17 Jan 2024 16:03:22 +0100 Subject: [PATCH 2/4] Simplify implementation --- .../providers/gcp/facade/cloudstorage.py | 42 ++++++++++++------- .../findings/cloudstorage-bucket-member.json | 4 +- ...ge-bucket-no-public-access-prevention.json | 4 +- 3 files changed, 31 insertions(+), 19 deletions(-) diff --git a/ScoutSuite/providers/gcp/facade/cloudstorage.py b/ScoutSuite/providers/gcp/facade/cloudstorage.py index 68f5753e8..05e97620d 100755 --- a/ScoutSuite/providers/gcp/facade/cloudstorage.py +++ b/ScoutSuite/providers/gcp/facade/cloudstorage.py @@ -2,16 +2,31 @@ from google.cloud import orgpolicy_v2 from google.api_core.gapic_v1.client_info import ClientInfo -from ScoutSuite.providers.gcp.facade.utils import GCPFacadeUtils +from ScoutSuite.providers.gcp.facade.basefacade import GCPBaseFacade -from ScoutSuite.core.console import print_exception +from ScoutSuite.core.console import print_exception, print_warning from ScoutSuite.providers.utils import run_concurrently, get_and_set_concurrently from ScoutSuite.utils import get_user_agent -class CloudStorageFacade: +class CloudStorageFacade(GCPBaseFacade): - def get_client(self, project_id: str): + def __init__(self): + super().__init__('cloudresourcemanager', 'v1') + + def _get_effective_public_access_prevention(self, project_id): + try: + resourcemanager_client = self._get_client() + request = resourcemanager_client.projects().getEffectiveOrgPolicy(resource=f"projects/{project_id}", + body={ + "constraint": f"constraints/storage.publicAccessPrevention"}) + response = request.execute() + return response.get('booleanPolicy', {}).get('enforced', False) + except Exception as e: + print_warning(f'Failed to retrieve project {project_id} storage.publicAccessPrevention constraint: {e}') + return None + + def get_storage_client(self, project_id: str): client_info = ClientInfo(user_agent=get_user_agent()) client = storage.Client(project=project_id, client_info=client_info) @@ -24,12 +39,14 @@ def get_org_policy_client(self): async def get_buckets(self, project_id: str): try: - client = self.get_client(project_id) + client = self.get_storage_client(project_id) buckets = await run_concurrently(lambda: list(client.list_buckets())) await get_and_set_concurrently([self._get_and_set_bucket_logging, self._get_and_set_bucket_iam_policy, self._get_and_set_public_access_prevention], - buckets, project_id=project_id) + buckets, + effective_public_access_prevention=self._get_effective_public_access_prevention( + project_id)) return buckets except Exception as e: print_exception(f'Failed to retrieve storage buckets: {e}') @@ -49,16 +66,11 @@ async def _get_and_set_bucket_iam_policy(self, bucket, **kwargs): setattr(bucket, 'iam_policy', bucket_iam_policy) except Exception as e: print_exception(f'Failed to retrieve bucket IAM policy: {e}') - setattr(bucket, 'iam_policy', None) + setattr(bucket, 'iam_policy', None) - async def _get_and_set_public_access_prevention(self, bucket, project_id): + async def _get_and_set_public_access_prevention(self, bucket, effective_public_access_prevention): try: - org_client = self.get_org_policy_client() - request = orgpolicy_v2.GetEffectivePolicyRequest(name=f"projects/{project_id}/policies/storage.publicAccessPrevention") - response = org_client.get_effective_policy(request=request) - setattr(bucket, 'effective_public_access_prevention', - 'enforce: true' in str(response.spec.rules) - ) + setattr(bucket, 'effective_public_access_prevention', effective_public_access_prevention) except Exception as e: - print_exception(f'Failed to retrieve organization policy storage.publicAccessPrevention: {e}') + print_exception(f'Failed to set effective public access prevention for bucket {bucket}: {e}') setattr(bucket, 'effective_public_access_prevention', None) diff --git a/ScoutSuite/providers/gcp/rules/findings/cloudstorage-bucket-member.json b/ScoutSuite/providers/gcp/rules/findings/cloudstorage-bucket-member.json index c55b3cf55..e541ce11a 100755 --- a/ScoutSuite/providers/gcp/rules/findings/cloudstorage-bucket-member.json +++ b/ScoutSuite/providers/gcp/rules/findings/cloudstorage-bucket-member.json @@ -38,8 +38,8 @@ ], [ "cloudstorage.projects.id.buckets.id.effective_public_access_prevention", - "notTrue", - "" + "notEqual", + "True" ] ], "key": "cloudstorage-bucket-_ARG_0_", diff --git a/ScoutSuite/providers/gcp/rules/findings/cloudstorage-bucket-no-public-access-prevention.json b/ScoutSuite/providers/gcp/rules/findings/cloudstorage-bucket-no-public-access-prevention.json index 17c151cdd..ba8e15351 100755 --- a/ScoutSuite/providers/gcp/rules/findings/cloudstorage-bucket-no-public-access-prevention.json +++ b/ScoutSuite/providers/gcp/rules/findings/cloudstorage-bucket-no-public-access-prevention.json @@ -10,8 +10,8 @@ "and", [ "cloudstorage.projects.id.buckets.id.effective_public_access_prevention", - "notTrue", - "" + "notEqual", + "True" ] ], "class_suffix": "public_access_prevention" From 06ca63ec014e8006ec0d8303f48cc35339f47c6e Mon Sep 17 00:00:00 2001 From: x4v13r64 Date: Wed, 17 Jan 2024 16:09:22 +0100 Subject: [PATCH 3/4] Remove useless req --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 6c303c287..7f53a4dfc 100755 --- a/requirements.txt +++ b/requirements.txt @@ -23,7 +23,6 @@ google-cloud-monitoring==1.1.0 google-cloud-resource-manager>=0.28.3 google-cloud-storage>=1.13.2 google-cloud-kms==1.3.0 -google-cloud-org-policy>=1.10.0 ## API Client Libraries google-api-python-client>=2.47.0 oauth2client>=4.1.3 From 5c20627ceb8991883653fea33d7ed55d7240eb74 Mon Sep 17 00:00:00 2001 From: x4v13r64 Date: Tue, 23 Jan 2024 10:31:14 +0100 Subject: [PATCH 4/4] Add missing import --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 7f53a4dfc..f1525b866 100755 --- a/requirements.txt +++ b/requirements.txt @@ -22,6 +22,7 @@ google-cloud-logging>=2.2.0 google-cloud-monitoring==1.1.0 google-cloud-resource-manager>=0.28.3 google-cloud-storage>=1.13.2 +google-cloud-org-policy==1.10.0 google-cloud-kms==1.3.0 ## API Client Libraries google-api-python-client>=2.47.0