fix(route53_dangling_ip_subdomain_takeover): notify only IPs with AWS IP Ranges (#2396)

This commit is contained in:
Sergio Garcia
2023-05-23 16:35:13 +02:00
committed by GitHub
parent 9e9e7e1e96
commit deb9847e2b
4 changed files with 84 additions and 4 deletions

14
poetry.lock generated
View File

@@ -67,6 +67,18 @@ docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-
tests = ["attrs[tests-no-zope]", "zope-interface"]
tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
[[package]]
name = "awsipranges"
version = "0.3.3"
description = "Work with the AWS IP address ranges in native Python."
category = "main"
optional = false
python-versions = ">=3.7,<4.0"
files = [
{file = "awsipranges-0.3.3-py3-none-any.whl", hash = "sha256:f3d7a54aeaf7fe310beb5d377a4034a63a51b72677ae6af3e0967bc4de7eedaf"},
{file = "awsipranges-0.3.3.tar.gz", hash = "sha256:4f0b3f22a9dc1163c85b513bed812b6c92bdacd674e6a7b68252a3c25b99e2c0"},
]
[[package]]
name = "azure-common"
version = "1.1.28"
@@ -2891,4 +2903,4 @@ docs = ["mkdocs", "mkdocs-material"]
[metadata]
lock-version = "2.0"
python-versions = "^3.9"
content-hash = "25f5c9874335d9fe564abc5bd7938c9350fc083dc6074f5de9f75a916ba4d71c"
content-hash = "60d5a4537ef2c46cf9ae54378fe801fda4adec8251c03cf60b815889b433f48f"

View File

@@ -1,5 +1,7 @@
from ipaddress import ip_address
import awsipranges
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.ec2.ec2_client import ec2_client
from prowler.providers.aws.services.route53.route53_client import route53_client
@@ -32,8 +34,12 @@ class route53_dangling_ip_subdomain_takeover(Check):
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 = "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)

View File

@@ -26,6 +26,7 @@ version = "3.5.2"
[tool.poetry.dependencies]
alive-progress = "3.1.1"
awsipranges = "0.3.3"
azure-identity = "1.13.0"
azure-mgmt-authorization = "3.0.0"
azure-mgmt-security = "5.0.0"

View File

@@ -164,7 +164,7 @@ class Test_route53_dangling_ip_subdomain_takeover:
@mock_ec2
@mock_route53
def test_hosted_zone_dangling_public_record(self):
def test_hosted_zone_external_record(self):
conn = client("route53", region_name=AWS_REGION)
zone_id = conn.create_hosted_zone(
@@ -191,6 +191,67 @@ class Test_route53_dangling_ip_subdomain_takeover:
audit_info = self.set_mocked_audit_info()
with mock.patch(
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
new=audit_info,
):
with mock.patch(
"prowler.providers.aws.services.route53.route53_dangling_ip_subdomain_takeover.route53_dangling_ip_subdomain_takeover.route53_client",
new=Route53(audit_info),
):
with mock.patch(
"prowler.providers.aws.services.route53.route53_dangling_ip_subdomain_takeover.route53_dangling_ip_subdomain_takeover.ec2_client",
new=EC2(audit_info),
):
# Test Check
from prowler.providers.aws.services.route53.route53_dangling_ip_subdomain_takeover.route53_dangling_ip_subdomain_takeover import (
route53_dangling_ip_subdomain_takeover,
)
check = route53_dangling_ip_subdomain_takeover()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert search(
"does not belong to AWS and it is not a dangling IP",
result[0].status_extended,
)
assert result[0].resource_id == zone_id.replace("/hostedzone/", "")
assert (
result[0].resource_arn
== f"arn:{audit_info.audited_partition}:route53:::{zone_id.replace('/hostedzone/','')}"
)
@mock_ec2
@mock_route53
def test_hosted_zone_dangling_public_record(self):
conn = client("route53", region_name=AWS_REGION)
zone_id = conn.create_hosted_zone(
Name="testdns.aws.com.", CallerReference=str(hash("foo"))
)["HostedZone"]["Id"]
conn.change_resource_record_sets(
HostedZoneId=zone_id,
ChangeBatch={
"Changes": [
{
"Action": "CREATE",
"ResourceRecordSet": {
"Name": "foo.bar.testdns.aws.com",
"Type": "A",
"ResourceRecords": [{"Value": "54.152.12.70"}],
},
}
]
},
)
from prowler.providers.aws.services.ec2.ec2_service import EC2
from prowler.providers.aws.services.route53.route53_service import Route53
audit_info = self.set_mocked_audit_info()
with mock.patch(
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
new=audit_info,