mirror of
https://github.com/ghndrx/prowler.git
synced 2026-02-10 14:55:00 +00:00
fix(route53_dangling_ip_subdomain_takeover): notify only IPs with AWS IP Ranges (#2396)
This commit is contained in:
14
poetry.lock
generated
14
poetry.lock
generated
@@ -67,6 +67,18 @@ docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-
|
|||||||
tests = ["attrs[tests-no-zope]", "zope-interface"]
|
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]"]
|
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]]
|
[[package]]
|
||||||
name = "azure-common"
|
name = "azure-common"
|
||||||
version = "1.1.28"
|
version = "1.1.28"
|
||||||
@@ -2891,4 +2903,4 @@ docs = ["mkdocs", "mkdocs-material"]
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.9"
|
python-versions = "^3.9"
|
||||||
content-hash = "25f5c9874335d9fe564abc5bd7938c9350fc083dc6074f5de9f75a916ba4d71c"
|
content-hash = "60d5a4537ef2c46cf9ae54378fe801fda4adec8251c03cf60b815889b433f48f"
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
from ipaddress import ip_address
|
from ipaddress import ip_address
|
||||||
|
|
||||||
|
import awsipranges
|
||||||
|
|
||||||
from prowler.lib.check.models import Check, Check_Report_AWS
|
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.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
|
||||||
@@ -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."
|
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 Public IP check if it is in the AWS Account
|
||||||
if not ip_address(record).is_private and record not in public_ips:
|
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} does not belong to AWS and it 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} is a dangling IP which can lead to a subdomain takeover attack!"
|
# 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)
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ version = "3.5.2"
|
|||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
alive-progress = "3.1.1"
|
alive-progress = "3.1.1"
|
||||||
|
awsipranges = "0.3.3"
|
||||||
azure-identity = "1.13.0"
|
azure-identity = "1.13.0"
|
||||||
azure-mgmt-authorization = "3.0.0"
|
azure-mgmt-authorization = "3.0.0"
|
||||||
azure-mgmt-security = "5.0.0"
|
azure-mgmt-security = "5.0.0"
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ class Test_route53_dangling_ip_subdomain_takeover:
|
|||||||
|
|
||||||
@mock_ec2
|
@mock_ec2
|
||||||
@mock_route53
|
@mock_route53
|
||||||
def test_hosted_zone_dangling_public_record(self):
|
def test_hosted_zone_external_record(self):
|
||||||
conn = client("route53", region_name=AWS_REGION)
|
conn = client("route53", region_name=AWS_REGION)
|
||||||
|
|
||||||
zone_id = conn.create_hosted_zone(
|
zone_id = conn.create_hosted_zone(
|
||||||
@@ -191,6 +191,67 @@ class Test_route53_dangling_ip_subdomain_takeover:
|
|||||||
|
|
||||||
audit_info = self.set_mocked_audit_info()
|
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(
|
with mock.patch(
|
||||||
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
|
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
|
||||||
new=audit_info,
|
new=audit_info,
|
||||||
|
|||||||
Reference in New Issue
Block a user