diff --git a/poetry.lock b/poetry.lock index a1957e03..cfe4e73a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry and should not be changed by hand. +# This file is automatically @generated by Poetry 1.4.0 and should not be changed by hand. [[package]] name = "about-time" @@ -2394,8 +2394,7 @@ files = [ {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-win32.whl", hash = "sha256:763d65baa3b952479c4e972669f679fe490eee058d5aa85da483ebae2009d231"}, {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:d000f258cf42fec2b1bbf2863c61d7b8918d31ffee905da62dede869254d3b8a"}, {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:045e0626baf1c52e5527bd5db361bc83180faaba2ff586e763d3d5982a876a9e"}, - {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:1a6391a7cabb7641c32517539ca42cf84b87b667bad38b78d4d42dd23e957c81"}, - {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:9c7617df90c1365638916b98cdd9be833d31d337dbcd722485597b43c4a215bf"}, + {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_12_6_arm64.whl", hash = "sha256:721bc4ba4525f53f6a611ec0967bdcee61b31df5a56801281027a3a6d1c2daf5"}, {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:41d0f1fa4c6830176eef5b276af04c89320ea616655d01327d5ce65e50575c94"}, {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-win32.whl", hash = "sha256:f6d3d39611ac2e4f62c3128a9eed45f19a6608670c5a2f4f07f24e8de3441d38"}, {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:da538167284de58a52109a9b89b8f6a53ff8437dd6dc26d33b57bf6699153122"}, diff --git a/prowler/lib/utils/utils.py b/prowler/lib/utils/utils.py index 83da5f04..c0407e2e 100644 --- a/prowler/lib/utils/utils.py +++ b/prowler/lib/utils/utils.py @@ -4,6 +4,7 @@ import sys import tempfile from hashlib import sha512 from io import TextIOWrapper +from ipaddress import ip_address from os.path import exists from typing import Any @@ -79,3 +80,11 @@ def detect_secrets_scan(data): return detect_secrets_output[temp_data_file.name] else: return None + + +def validate_ip_address(ip_string): + try: + ip_address(ip_string) + return True + except ValueError: + return False diff --git a/prowler/providers/aws/services/route53/route53_dangling_ip_subdomain_takeover/route53_dangling_ip_subdomain_takeover.py b/prowler/providers/aws/services/route53/route53_dangling_ip_subdomain_takeover/route53_dangling_ip_subdomain_takeover.py index 34f8bc88..509cf14a 100644 --- a/prowler/providers/aws/services/route53/route53_dangling_ip_subdomain_takeover/route53_dangling_ip_subdomain_takeover.py +++ b/prowler/providers/aws/services/route53/route53_dangling_ip_subdomain_takeover/route53_dangling_ip_subdomain_takeover.py @@ -3,6 +3,7 @@ from ipaddress import ip_address import awsipranges from prowler.lib.check.models import Check, Check_Report_AWS +from prowler.lib.utils.utils import validate_ip_address from prowler.providers.aws.services.ec2.ec2_client import ec2_client from prowler.providers.aws.services.route53.route53_client import route53_client @@ -21,26 +22,31 @@ class route53_dangling_ip_subdomain_takeover(Check): [interface.public_ip for interface in ec2_client.network_interfaces] ) for record in record_set.records: - report = Check_Report_AWS(self.metadata()) - report.resource_id = record_set.hosted_zone_id - report.resource_arn = route53_client.hosted_zones[ - record_set.hosted_zone_id - ].arn - report.resource_tags = route53_client.hosted_zones[ - record_set.hosted_zone_id - ].tags - report.region = record_set.region - report.status = "PASS" - report.status_extended = f"Route53 record {record} in Hosted Zone {route53_client.hosted_zones[record_set.hosted_zone_id].name} is not a dangling IP." - # If Public IP check if it is in the AWS Account - if not ip_address(record).is_private and record not in public_ips: - report.status_extended = f"Route53 record {record} in Hosted Zone {route53_client.hosted_zones[record_set.hosted_zone_id].name} does not belong to AWS and it is not a dangling IP." - # Check if potential dangling IP is within AWS Ranges - aws_ip_ranges = awsipranges.get_ranges() - if aws_ip_ranges.get(record): - report.status = "FAIL" - report.status_extended = f"Route53 record {record} in Hosted Zone {route53_client.hosted_zones[record_set.hosted_zone_id].name} is a dangling IP which can lead to a subdomain takeover attack!" + # Check if record is an IP Address + if validate_ip_address(record): + report = Check_Report_AWS(self.metadata()) + report.resource_id = record_set.hosted_zone_id + report.resource_arn = route53_client.hosted_zones[ + record_set.hosted_zone_id + ].arn + report.resource_tags = route53_client.hosted_zones[ + record_set.hosted_zone_id + ].tags + report.region = record_set.region + report.status = "PASS" + report.status_extended = f"Route53 record {record} in Hosted Zone {route53_client.hosted_zones[record_set.hosted_zone_id].name} is not a dangling IP." + # If Public IP check if it is in the AWS Account + if ( + not ip_address(record).is_private + and record not in public_ips + ): + report.status_extended = f"Route53 record {record} in Hosted Zone {route53_client.hosted_zones[record_set.hosted_zone_id].name} does not belong to AWS and it is not a dangling IP." + # Check if potential dangling IP is within AWS Ranges + aws_ip_ranges = awsipranges.get_ranges() + if aws_ip_ranges.get(record): + report.status = "FAIL" + report.status_extended = f"Route53 record {record} in Hosted Zone {route53_client.hosted_zones[record_set.hosted_zone_id].name} is a dangling IP which can lead to a subdomain takeover attack!" - findings.append(report) + findings.append(report) return findings diff --git a/prowler/providers/aws/services/trustedadvisor/trustedadvisor_service.py b/prowler/providers/aws/services/trustedadvisor/trustedadvisor_service.py index 1cd02217..05e96b72 100644 --- a/prowler/providers/aws/services/trustedadvisor/trustedadvisor_service.py +++ b/prowler/providers/aws/services/trustedadvisor/trustedadvisor_service.py @@ -47,10 +47,6 @@ class TrustedAdvisor: except ClientError as error: if error.response["Error"]["Code"] == "SubscriptionRequiredException": self.enabled = False - elif error.response["Error"]["Code"] == "InvalidParameterValueException": - logger.warning( - f"{self.client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" - ) else: logger.error( f"{self.client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" @@ -65,11 +61,20 @@ class TrustedAdvisor: try: for check in self.checks: if check.region == self.client.region: - response = self.client.describe_trusted_advisor_check_result( - checkId=check.id - ) - if "result" in response: - check.status = response["result"]["status"] + try: + response = self.client.describe_trusted_advisor_check_result( + checkId=check.id + ) + if "result" in response: + check.status = response["result"]["status"] + except ClientError as error: + if ( + error.response["Error"]["Code"] + == "InvalidParameterValueException" + ): + logger.warning( + f"{self.client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) except Exception as error: logger.error( f"{self.client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" diff --git a/tests/lib/utils/utils_test.py b/tests/lib/utils/utils_test.py new file mode 100644 index 00000000..530c6b03 --- /dev/null +++ b/tests/lib/utils/utils_test.py @@ -0,0 +1,7 @@ +from prowler.lib.utils.utils import validate_ip_address + + +class Test_Validate_Ip_Address: + def test_validate_ip_address(self): + assert validate_ip_address("88.26.151.198") + assert not validate_ip_address("Not an IP")