mirror of
https://github.com/ghndrx/prowler.git
synced 2026-02-10 14:55:00 +00:00
fix(services): verify Route53 records and handle TrustedAdvisor error (#2448)
This commit is contained in:
5
poetry.lock
generated
5
poetry.lock
generated
@@ -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]]
|
[[package]]
|
||||||
name = "about-time"
|
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-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-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_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-macosx_12_6_arm64.whl", hash = "sha256:721bc4ba4525f53f6a611ec0967bdcee61b31df5a56801281027a3a6d1c2daf5"},
|
||||||
{file = "ruamel.yaml.clib-0.2.7-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:9c7617df90c1365638916b98cdd9be833d31d337dbcd722485597b43c4a215bf"},
|
|
||||||
{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-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-win32.whl", hash = "sha256:f6d3d39611ac2e4f62c3128a9eed45f19a6608670c5a2f4f07f24e8de3441d38"},
|
||||||
{file = "ruamel.yaml.clib-0.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:da538167284de58a52109a9b89b8f6a53ff8437dd6dc26d33b57bf6699153122"},
|
{file = "ruamel.yaml.clib-0.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:da538167284de58a52109a9b89b8f6a53ff8437dd6dc26d33b57bf6699153122"},
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import sys
|
|||||||
import tempfile
|
import tempfile
|
||||||
from hashlib import sha512
|
from hashlib import sha512
|
||||||
from io import TextIOWrapper
|
from io import TextIOWrapper
|
||||||
|
from ipaddress import ip_address
|
||||||
from os.path import exists
|
from os.path import exists
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
@@ -79,3 +80,11 @@ def detect_secrets_scan(data):
|
|||||||
return detect_secrets_output[temp_data_file.name]
|
return detect_secrets_output[temp_data_file.name]
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def validate_ip_address(ip_string):
|
||||||
|
try:
|
||||||
|
ip_address(ip_string)
|
||||||
|
return True
|
||||||
|
except ValueError:
|
||||||
|
return False
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from ipaddress import ip_address
|
|||||||
import awsipranges
|
import awsipranges
|
||||||
|
|
||||||
from prowler.lib.check.models import Check, Check_Report_AWS
|
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.ec2.ec2_client import ec2_client
|
||||||
from prowler.providers.aws.services.route53.route53_client import route53_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]
|
[interface.public_ip for interface in ec2_client.network_interfaces]
|
||||||
)
|
)
|
||||||
for record in record_set.records:
|
for record in record_set.records:
|
||||||
report = Check_Report_AWS(self.metadata())
|
# Check if record is an IP Address
|
||||||
report.resource_id = record_set.hosted_zone_id
|
if validate_ip_address(record):
|
||||||
report.resource_arn = route53_client.hosted_zones[
|
report = Check_Report_AWS(self.metadata())
|
||||||
record_set.hosted_zone_id
|
report.resource_id = record_set.hosted_zone_id
|
||||||
].arn
|
report.resource_arn = route53_client.hosted_zones[
|
||||||
report.resource_tags = route53_client.hosted_zones[
|
record_set.hosted_zone_id
|
||||||
record_set.hosted_zone_id
|
].arn
|
||||||
].tags
|
report.resource_tags = route53_client.hosted_zones[
|
||||||
report.region = record_set.region
|
record_set.hosted_zone_id
|
||||||
report.status = "PASS"
|
].tags
|
||||||
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."
|
report.region = record_set.region
|
||||||
# If Public IP check if it is in the AWS Account
|
report.status = "PASS"
|
||||||
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} is not a dangling IP."
|
||||||
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."
|
# If Public IP check if it is in the AWS Account
|
||||||
# Check if potential dangling IP is within AWS Ranges
|
if (
|
||||||
aws_ip_ranges = awsipranges.get_ranges()
|
not ip_address(record).is_private
|
||||||
if aws_ip_ranges.get(record):
|
and record not in public_ips
|
||||||
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!"
|
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
|
return findings
|
||||||
|
|||||||
@@ -47,10 +47,6 @@ class TrustedAdvisor:
|
|||||||
except ClientError as error:
|
except ClientError as error:
|
||||||
if error.response["Error"]["Code"] == "SubscriptionRequiredException":
|
if error.response["Error"]["Code"] == "SubscriptionRequiredException":
|
||||||
self.enabled = False
|
self.enabled = False
|
||||||
elif error.response["Error"]["Code"] == "InvalidParameterValueException":
|
|
||||||
logger.warning(
|
|
||||||
f"{self.client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"{self.client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
f"{self.client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
@@ -65,11 +61,20 @@ class TrustedAdvisor:
|
|||||||
try:
|
try:
|
||||||
for check in self.checks:
|
for check in self.checks:
|
||||||
if check.region == self.client.region:
|
if check.region == self.client.region:
|
||||||
response = self.client.describe_trusted_advisor_check_result(
|
try:
|
||||||
checkId=check.id
|
response = self.client.describe_trusted_advisor_check_result(
|
||||||
)
|
checkId=check.id
|
||||||
if "result" in response:
|
)
|
||||||
check.status = response["result"]["status"]
|
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:
|
except Exception as error:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"{self.client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
f"{self.client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
|||||||
7
tests/lib/utils/utils_test.py
Normal file
7
tests/lib/utils/utils_test.py
Normal 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")
|
||||||
Reference in New Issue
Block a user