From 7a1762be51c403dcd385c1bfe8cf9c78ddc735aa Mon Sep 17 00:00:00 2001 From: Jakob Rieck <135810953+rieck-srlabs@users.noreply.github.com> Date: Fri, 19 Apr 2024 12:47:03 +0200 Subject: [PATCH] fix(aws): Include record names for dangling IPs (#3821) Co-authored-by: Pepe Fagoaga --- .../route53_dangling_ip_subdomain_takeover.py | 6 +- ...e53_dangling_ip_subdomain_takeover_test.py | 81 +++++++++++-------- 2 files changed, 50 insertions(+), 37 deletions(-) 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 2f567db57b..2ef1d53fc0 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 @@ -38,18 +38,18 @@ class route53_dangling_ip_subdomain_takeover(Check): ].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." + report.status_extended = f"Route53 record {record} (name: {record_set.name}) 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." + report.status_extended = f"Route53 record {record} (name: {record_set.name}) 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." + report.status_extended = f"Route53 record {record} (name: {record_set.name}) 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) return findings diff --git a/tests/providers/aws/services/route53/route53_dangling_ip_subdomain_takeover/route53_dangling_ip_subdomain_takeover_test.py b/tests/providers/aws/services/route53/route53_dangling_ip_subdomain_takeover/route53_dangling_ip_subdomain_takeover_test.py index 93f88718cd..ea41ff0a82 100644 --- a/tests/providers/aws/services/route53/route53_dangling_ip_subdomain_takeover/route53_dangling_ip_subdomain_takeover_test.py +++ b/tests/providers/aws/services/route53/route53_dangling_ip_subdomain_takeover/route53_dangling_ip_subdomain_takeover_test.py @@ -1,4 +1,3 @@ -from re import search from unittest import mock from boto3 import client, resource @@ -6,6 +5,8 @@ from moto import mock_aws from tests.providers.aws.utils import AWS_REGION_US_EAST_1, set_mocked_aws_provider +HOSTED_ZONE_NAME = "testdns.aws.com." + class Test_route53_dangling_ip_subdomain_takeover: @mock_aws @@ -42,7 +43,7 @@ class Test_route53_dangling_ip_subdomain_takeover: conn = client("route53", region_name=AWS_REGION_US_EAST_1) conn.create_hosted_zone( - Name="testdns.aws.com.", CallerReference=str(hash("foo")) + Name=HOSTED_ZONE_NAME, CallerReference=str(hash("foo")) )["HostedZone"]["Id"] from prowler.providers.aws.services.ec2.ec2_service import EC2 @@ -77,9 +78,11 @@ class Test_route53_dangling_ip_subdomain_takeover: conn = client("route53", region_name=AWS_REGION_US_EAST_1) zone_id = conn.create_hosted_zone( - Name="testdns.aws.com.", CallerReference=str(hash("foo")) + Name=HOSTED_ZONE_NAME, CallerReference=str(hash("foo")) )["HostedZone"]["Id"] + record_set_name = "foo.bar.testdns.aws.com." + record_ip = "192.168.1.1" conn.change_resource_record_sets( HostedZoneId=zone_id, ChangeBatch={ @@ -87,9 +90,9 @@ class Test_route53_dangling_ip_subdomain_takeover: { "Action": "CREATE", "ResourceRecordSet": { - "Name": "foo.bar.testdns.aws.com", + "Name": record_set_name, "Type": "A", - "ResourceRecords": [{"Value": "192.168.1.1"}], + "ResourceRecords": [{"Value": record_ip}], }, } ] @@ -122,9 +125,9 @@ class Test_route53_dangling_ip_subdomain_takeover: assert len(result) == 1 assert result[0].status == "PASS" - assert search( - "is not a dangling IP", - result[0].status_extended, + assert ( + result[0].status_extended + == f"Route53 record {record_ip} (name: {record_set_name}) in Hosted Zone {HOSTED_ZONE_NAME} is not a dangling IP." ) assert ( result[0].resource_id @@ -140,9 +143,11 @@ class Test_route53_dangling_ip_subdomain_takeover: conn = client("route53", region_name=AWS_REGION_US_EAST_1) zone_id = conn.create_hosted_zone( - Name="testdns.aws.com.", CallerReference=str(hash("foo")) + Name=HOSTED_ZONE_NAME, CallerReference=str(hash("foo")) )["HostedZone"]["Id"] + record_set_name = "foo.bar.testdns.aws.com." + record_ip = "17.5.7.3" conn.change_resource_record_sets( HostedZoneId=zone_id, ChangeBatch={ @@ -150,9 +155,9 @@ class Test_route53_dangling_ip_subdomain_takeover: { "Action": "CREATE", "ResourceRecordSet": { - "Name": "foo.bar.testdns.aws.com", + "Name": record_set_name, "Type": "A", - "ResourceRecords": [{"Value": "17.5.7.3"}], + "ResourceRecords": [{"Value": record_ip}], }, } ] @@ -185,9 +190,9 @@ class Test_route53_dangling_ip_subdomain_takeover: 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].status_extended + == f"Route53 record {record_ip} (name: {record_set_name}) in Hosted Zone {HOSTED_ZONE_NAME} does not belong to AWS and it is not a dangling IP." ) assert ( result[0].resource_id @@ -203,9 +208,11 @@ class Test_route53_dangling_ip_subdomain_takeover: conn = client("route53", region_name=AWS_REGION_US_EAST_1) zone_id = conn.create_hosted_zone( - Name="testdns.aws.com.", CallerReference=str(hash("foo")) + Name=HOSTED_ZONE_NAME, CallerReference=str(hash("foo")) )["HostedZone"]["Id"] + record_set_name = "foo.bar.testdns.aws.com." + record_ip = "54.152.12.70" conn.change_resource_record_sets( HostedZoneId=zone_id, ChangeBatch={ @@ -213,9 +220,9 @@ class Test_route53_dangling_ip_subdomain_takeover: { "Action": "CREATE", "ResourceRecordSet": { - "Name": "foo.bar.testdns.aws.com", + "Name": record_set_name, "Type": "A", - "ResourceRecords": [{"Value": "54.152.12.70"}], + "ResourceRecords": [{"Value": record_ip}], }, } ] @@ -248,9 +255,9 @@ class Test_route53_dangling_ip_subdomain_takeover: assert len(result) == 1 assert result[0].status == "FAIL" - assert search( - "is a dangling IP", - result[0].status_extended, + assert ( + result[0].status_extended + == f"Route53 record {record_ip} (name: {record_set_name}) in Hosted Zone {HOSTED_ZONE_NAME} is a dangling IP which can lead to a subdomain takeover attack." ) assert ( result[0].resource_id @@ -266,12 +273,15 @@ class Test_route53_dangling_ip_subdomain_takeover: conn = client("route53", region_name=AWS_REGION_US_EAST_1) ec2 = client("ec2", region_name=AWS_REGION_US_EAST_1) - ec2.allocate_address(Domain="vpc", Address="17.5.7.3") + address = "17.5.7.3" + ec2.allocate_address(Domain="vpc", Address=address) zone_id = conn.create_hosted_zone( - Name="testdns.aws.com.", CallerReference=str(hash("foo")) + Name=HOSTED_ZONE_NAME, CallerReference=str(hash("foo")) )["HostedZone"]["Id"] + record_set_name = "foo.bar.testdns.aws.com." + record_ip = address conn.change_resource_record_sets( HostedZoneId=zone_id, ChangeBatch={ @@ -279,9 +289,9 @@ class Test_route53_dangling_ip_subdomain_takeover: { "Action": "CREATE", "ResourceRecordSet": { - "Name": "foo.bar.testdns.aws.com", + "Name": record_set_name, "Type": "A", - "ResourceRecords": [{"Value": "17.5.7.3"}], + "ResourceRecords": [{"Value": record_ip}], }, } ] @@ -314,9 +324,9 @@ class Test_route53_dangling_ip_subdomain_takeover: assert len(result) == 1 assert result[0].status == "PASS" - assert search( - "is not a dangling IP", - result[0].status_extended, + assert ( + result[0].status_extended + == f"Route53 record {record_ip} (name: {record_set_name}) in Hosted Zone {HOSTED_ZONE_NAME} is not a dangling IP." ) assert ( result[0].resource_id @@ -335,15 +345,18 @@ class Test_route53_dangling_ip_subdomain_takeover: vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16") subnet = ec2.create_subnet(VpcId=vpc.id, CidrBlock="10.0.0.0/18") eni_id = ec2.create_network_interface(SubnetId=subnet.id).id - eip = ec2_client.allocate_address(Domain="vpc", Address="17.5.7.3") + address = "17.5.7.3" + eip = ec2_client.allocate_address(Domain="vpc", Address=address) ec2_client.associate_address( NetworkInterfaceId=eni_id, AllocationId=eip["AllocationId"] ) zone_id = conn.create_hosted_zone( - Name="testdns.aws.com.", CallerReference=str(hash("foo")) + Name=HOSTED_ZONE_NAME, CallerReference=str(hash("foo")) )["HostedZone"]["Id"] + record_set_name = "foo.bar.testdns.aws.com." + record_ip = address conn.change_resource_record_sets( HostedZoneId=zone_id, ChangeBatch={ @@ -351,9 +364,9 @@ class Test_route53_dangling_ip_subdomain_takeover: { "Action": "CREATE", "ResourceRecordSet": { - "Name": "foo.bar.testdns.aws.com", + "Name": record_set_name, "Type": "A", - "ResourceRecords": [{"Value": "17.5.7.3"}], + "ResourceRecords": [{"Value": record_ip}], }, } ] @@ -386,9 +399,9 @@ class Test_route53_dangling_ip_subdomain_takeover: assert len(result) == 1 assert result[0].status == "PASS" - assert search( - "is not a dangling IP", - result[0].status_extended, + assert ( + result[0].status_extended + == f"Route53 record {record_ip} (name: {record_set_name}) in Hosted Zone {HOSTED_ZONE_NAME} is not a dangling IP." ) assert ( result[0].resource_id