fix(services): verify Route53 records and handle TrustedAdvisor error (#2448)

This commit is contained in:
Sergio Garcia
2023-06-06 11:50:44 +02:00
committed by GitHub
parent eb16d7e6f9
commit 277833e388
5 changed files with 58 additions and 32 deletions

5
poetry.lock generated
View File

@@ -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"},

View File

@@ -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

View File

@@ -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

View File

@@ -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}"

View File

@@ -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")