mirror of
https://github.com/ghndrx/prowler.git
synced 2026-02-10 14:55:00 +00:00
feat(route53): add route53_dangling_ip_subdomain_takeover check (#2288)
Co-authored-by: Pepe Fagoaga <pepe@verica.io>
This commit is contained in:
@@ -29,7 +29,9 @@ class EC2:
|
||||
self.snapshots = []
|
||||
self.__threading_call__(self.__describe_snapshots__)
|
||||
self.__get_snapshot_public__()
|
||||
self.__threading_call__(self.__describe_network_interfaces__)
|
||||
self.network_interfaces = []
|
||||
self.__threading_call__(self.__describe_public_network_interfaces__)
|
||||
self.__threading_call__(self.__describe_sg_network_interfaces__)
|
||||
self.images = []
|
||||
self.__threading_call__(self.__describe_images__)
|
||||
self.volumes = []
|
||||
@@ -220,10 +222,37 @@ class EC2:
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
def __describe_network_interfaces__(self, regional_client):
|
||||
def __describe_public_network_interfaces__(self, regional_client):
|
||||
logger.info("EC2 - Describing Network Interfaces...")
|
||||
try:
|
||||
# Get SGs Network Interfaces
|
||||
# Get Network Interfaces with Public IPs
|
||||
describe_network_interfaces_paginator = regional_client.get_paginator(
|
||||
"describe_network_interfaces"
|
||||
)
|
||||
for page in describe_network_interfaces_paginator.paginate():
|
||||
for interface in page["NetworkInterfaces"]:
|
||||
if interface.get("Association"):
|
||||
self.network_interfaces.append(
|
||||
NetworkInterface(
|
||||
public_ip=interface["Association"]["PublicIp"],
|
||||
type=interface["InterfaceType"],
|
||||
private_ip=interface["PrivateIpAddress"],
|
||||
subnet_id=interface["SubnetId"],
|
||||
vpc_id=interface["VpcId"],
|
||||
region=regional_client.region,
|
||||
tags=interface.get("TagSet"),
|
||||
)
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
def __describe_sg_network_interfaces__(self, regional_client):
|
||||
logger.info("EC2 - Describing Network Interfaces...")
|
||||
try:
|
||||
# Get Network Interfaces for Security Groups
|
||||
for sg in self.security_groups:
|
||||
regional_client = self.regional_clients[sg.region]
|
||||
describe_network_interfaces_paginator = regional_client.get_paginator(
|
||||
@@ -241,7 +270,6 @@ class EC2:
|
||||
):
|
||||
for interface in page["NetworkInterfaces"]:
|
||||
sg.network_interfaces.append(interface["NetworkInterfaceId"])
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
@@ -425,6 +453,16 @@ class NetworkACL(BaseModel):
|
||||
tags: Optional[list] = []
|
||||
|
||||
|
||||
class NetworkInterface(BaseModel):
|
||||
public_ip: str
|
||||
private_ip: str
|
||||
type: str
|
||||
subnet_id: str
|
||||
vpc_id: str
|
||||
region: str
|
||||
tags: Optional[list] = []
|
||||
|
||||
|
||||
class ElasticIP(BaseModel):
|
||||
public_ip: Optional[str]
|
||||
association_id: Optional[str]
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "route53_dangling_ip_subdomain_takeover",
|
||||
"CheckTitle": "Check if Route53 Records contains dangling IPs.",
|
||||
"CheckType": [],
|
||||
"ServiceName": "route53",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "high",
|
||||
"ResourceType": "AWSRoute53RecordSet",
|
||||
"Description": "Check if Route53 Records contains dangling IPs.",
|
||||
"Risk": "When an ephemeral AWS resource such as an Elastic IP (EIP) is released into the Amazon's Elastic IP pool, an attacker may acquire the EIP resource and effectively control the domain/subdomain associated with that EIP in your Route 53 DNS records.",
|
||||
"RelatedUrl": "",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws route53 change-resource-record-sets --hosted-zone-id <resource_id>",
|
||||
"NativeIaC": "",
|
||||
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/Route53/dangling-dns-records.html",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Ensure that any dangling DNS records are deleted from your Amazon Route 53 public hosted zones in order to maintain the integrity and authenticity of your domains/subdomains and to protect against domain hijacking attacks.",
|
||||
"Url": "https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/resource-record-sets-deleting.html"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"forensics-ready"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": ""
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
from ipaddress import ip_address
|
||||
|
||||
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
|
||||
|
||||
|
||||
class route53_dangling_ip_subdomain_takeover(Check):
|
||||
def execute(self) -> Check_Report_AWS:
|
||||
findings = []
|
||||
|
||||
for record_set in route53_client.record_sets:
|
||||
# Check only A records and avoid aliases (only need to check IPs not AWS Resources)
|
||||
if record_set.type == "A" and not record_set.is_alias:
|
||||
# Gather Elastic IPs and Network Interfaces Public IPs inside the AWS Account
|
||||
public_ips = []
|
||||
public_ips.extend([eip.public_ip for eip in ec2_client.elastic_ips])
|
||||
public_ips.extend(
|
||||
[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 = "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)
|
||||
|
||||
return findings
|
||||
@@ -10,6 +10,7 @@ class route53_public_hosted_zones_cloudwatch_logging_enabled(Check):
|
||||
if not hosted_zone.private_zone:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.resource_id = hosted_zone.id
|
||||
report.resource_arn = hosted_zone.arn
|
||||
report.resource_tags = hosted_zone.tags
|
||||
report.region = hosted_zone.region
|
||||
if (
|
||||
|
||||
@@ -15,6 +15,7 @@ class Route53:
|
||||
self.audited_partition = audit_info.audited_partition
|
||||
self.audit_resources = audit_info.audit_resources
|
||||
self.hosted_zones = {}
|
||||
self.record_sets = []
|
||||
global_client = generate_regional_clients(
|
||||
self.service, audit_info, global_service=True
|
||||
)
|
||||
@@ -24,6 +25,7 @@ class Route53:
|
||||
self.__list_hosted_zones__()
|
||||
self.__list_query_logging_configs__()
|
||||
self.__list_tags_for_resource__()
|
||||
self.__list_resource_record_sets__()
|
||||
|
||||
def __get_session__(self):
|
||||
return self.session
|
||||
@@ -55,6 +57,36 @@ class Route53:
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
def __list_resource_record_sets__(self):
|
||||
logger.info("Route53 - Listing Hosting Zones...")
|
||||
try:
|
||||
list_resource_record_sets_paginator = self.client.get_paginator(
|
||||
"list_resource_record_sets"
|
||||
)
|
||||
for zone_id in self.hosted_zones.keys():
|
||||
for page in list_resource_record_sets_paginator.paginate(
|
||||
HostedZoneId=zone_id
|
||||
):
|
||||
for record in page["ResourceRecordSets"]:
|
||||
self.record_sets.append(
|
||||
RecordSet(
|
||||
name=record["Name"],
|
||||
type=record["Type"],
|
||||
records=[
|
||||
resource_record["Value"]
|
||||
for resource_record in record["ResourceRecords"]
|
||||
],
|
||||
is_alias=True if "AliasTarget" in record else False,
|
||||
hosted_zone_id=zone_id,
|
||||
region=self.region,
|
||||
)
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
def __list_query_logging_configs__(self):
|
||||
logger.info("Route53 - Listing Query Logging Configs...")
|
||||
try:
|
||||
@@ -105,6 +137,15 @@ class HostedZone(BaseModel):
|
||||
tags: Optional[list] = []
|
||||
|
||||
|
||||
class RecordSet(BaseModel):
|
||||
name: str
|
||||
type: str
|
||||
is_alias: bool
|
||||
records: list = []
|
||||
hosted_zone_id: str
|
||||
region: str
|
||||
|
||||
|
||||
################## Route53Domains
|
||||
class Route53Domains:
|
||||
def __init__(self, audit_info):
|
||||
|
||||
@@ -344,7 +344,7 @@ class Test_EC2_Service:
|
||||
|
||||
# Test EC2 Describe Network Interfaces
|
||||
@mock_ec2
|
||||
def test__describe_network_interfaces__(self):
|
||||
def test__describe_sg_network_interfaces__(self):
|
||||
# Generate EC2 Client
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_resource = resource("ec2", region_name=AWS_REGION)
|
||||
@@ -385,6 +385,46 @@ class Test_EC2_Service:
|
||||
}
|
||||
]
|
||||
|
||||
@mock_ec2
|
||||
def test__describe_public_network_interfaces__(self):
|
||||
# Generate EC2 Client
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
ec2_resource = resource("ec2", region_name=AWS_REGION)
|
||||
# Create VPC, Subnet, SecurityGroup and Network Interface
|
||||
vpc = ec2_resource.create_vpc(CidrBlock="10.0.0.0/16")
|
||||
subnet = ec2_resource.create_subnet(VpcId=vpc.id, CidrBlock="10.0.0.0/18")
|
||||
|
||||
eni = subnet.create_network_interface(
|
||||
SubnetId=subnet.id,
|
||||
TagSpecifications=[
|
||||
{
|
||||
"ResourceType": "network-interface",
|
||||
"Tags": [
|
||||
{"Key": "string", "Value": "string"},
|
||||
],
|
||||
},
|
||||
],
|
||||
)
|
||||
eip = ec2_client.allocate_address(Domain="vpc")
|
||||
ec2_client.associate_address(
|
||||
NetworkInterfaceId=eni.id, AllocationId=eip["AllocationId"]
|
||||
)
|
||||
|
||||
# EC2 client for this test class
|
||||
audit_info = self.set_mocked_audit_info()
|
||||
ec2 = EC2(audit_info)
|
||||
|
||||
assert len(ec2.network_interfaces) == 1
|
||||
assert ec2.network_interfaces[0].public_ip == eip["PublicIp"]
|
||||
assert ec2.network_interfaces[0].private_ip == eni.private_ip_address
|
||||
assert ec2.network_interfaces[0].type == eni.interface_type
|
||||
assert ec2.network_interfaces[0].subnet_id == subnet.id
|
||||
assert ec2.network_interfaces[0].vpc_id == vpc.id
|
||||
assert ec2.network_interfaces[0].region == AWS_REGION
|
||||
assert ec2.network_interfaces[0].tags == [
|
||||
{"Key": "string", "Value": "string"},
|
||||
]
|
||||
|
||||
# Test EC2 Describe Images
|
||||
@mock_ec2
|
||||
def test__describe_images__(self):
|
||||
|
||||
@@ -0,0 +1,358 @@
|
||||
from re import search
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client, resource, session
|
||||
from moto import mock_ec2, mock_route53
|
||||
from moto.core import DEFAULT_ACCOUNT_ID
|
||||
|
||||
from prowler.providers.aws.lib.audit_info.models import AWS_Audit_Info
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_route53_dangling_ip_subdomain_takeover:
|
||||
# Mocked Audit Info
|
||||
def set_mocked_audit_info(self):
|
||||
audit_info = AWS_Audit_Info(
|
||||
session_config=None,
|
||||
original_session=None,
|
||||
audit_session=session.Session(
|
||||
profile_name=None,
|
||||
botocore_session=None,
|
||||
region_name=AWS_REGION,
|
||||
),
|
||||
audited_account=DEFAULT_ACCOUNT_ID,
|
||||
audited_user_id=None,
|
||||
audited_partition="aws",
|
||||
audited_identity_arn=None,
|
||||
profile=None,
|
||||
profile_region=None,
|
||||
credentials=None,
|
||||
assumed_role_info=None,
|
||||
audited_regions=[AWS_REGION],
|
||||
organizations_metadata=None,
|
||||
audit_resources=None,
|
||||
)
|
||||
return audit_info
|
||||
|
||||
@mock_ec2
|
||||
@mock_route53
|
||||
def test_no_hosted_zones(self):
|
||||
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,
|
||||
):
|
||||
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) == 0
|
||||
|
||||
@mock_ec2
|
||||
@mock_route53
|
||||
def test_hosted_zone_no_records(self):
|
||||
conn = client("route53", region_name=AWS_REGION)
|
||||
|
||||
conn.create_hosted_zone(
|
||||
Name="testdns.aws.com.", CallerReference=str(hash("foo"))
|
||||
)["HostedZone"]["Id"]
|
||||
|
||||
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,
|
||||
):
|
||||
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) == 0
|
||||
|
||||
@mock_ec2
|
||||
@mock_route53
|
||||
def test_hosted_zone_private_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": "192.168.1.1"}],
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
)
|
||||
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,
|
||||
):
|
||||
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(
|
||||
"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": "17.5.7.3"}],
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
)
|
||||
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,
|
||||
):
|
||||
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 == "FAIL"
|
||||
assert search(
|
||||
"is 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_eip_record(self):
|
||||
conn = client("route53", region_name=AWS_REGION)
|
||||
ec2 = client("ec2", region_name=AWS_REGION)
|
||||
|
||||
ec2.allocate_address(Domain="vpc", Address="17.5.7.3")
|
||||
|
||||
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": "17.5.7.3"}],
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
)
|
||||
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,
|
||||
):
|
||||
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(
|
||||
"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_eni_record(self):
|
||||
conn = client("route53", region_name=AWS_REGION)
|
||||
ec2 = resource("ec2", region_name=AWS_REGION)
|
||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||
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")
|
||||
ec2_client.associate_address(
|
||||
NetworkInterfaceId=eni_id, AllocationId=eip["AllocationId"]
|
||||
)
|
||||
|
||||
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": "17.5.7.3"}],
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
)
|
||||
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,
|
||||
):
|
||||
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(
|
||||
"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/','')}"
|
||||
)
|
||||
@@ -16,7 +16,7 @@ class Test_route53_public_hosted_zones_cloudwatch_logging_enabled:
|
||||
route53.hosted_zones = {}
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.route53.route53_service.Route53",
|
||||
"prowler.providers.aws.services.route53.route53_public_hosted_zones_cloudwatch_logging_enabled.route53_public_hosted_zones_cloudwatch_logging_enabled.route53_client",
|
||||
new=route53,
|
||||
):
|
||||
# Test Check
|
||||
@@ -49,7 +49,7 @@ class Test_route53_public_hosted_zones_cloudwatch_logging_enabled:
|
||||
}
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.route53.route53_service.Route53",
|
||||
"prowler.providers.aws.services.route53.route53_public_hosted_zones_cloudwatch_logging_enabled.route53_public_hosted_zones_cloudwatch_logging_enabled.route53_client",
|
||||
new=route53,
|
||||
):
|
||||
# Test Check
|
||||
@@ -84,7 +84,7 @@ class Test_route53_public_hosted_zones_cloudwatch_logging_enabled:
|
||||
}
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.route53.route53_service.Route53",
|
||||
"prowler.providers.aws.services.route53.route53_public_hosted_zones_cloudwatch_logging_enabled.route53_public_hosted_zones_cloudwatch_logging_enabled.route53_client",
|
||||
new=route53,
|
||||
):
|
||||
# Test Check
|
||||
@@ -119,7 +119,7 @@ class Test_route53_public_hosted_zones_cloudwatch_logging_enabled:
|
||||
}
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.route53.route53_service.Route53",
|
||||
"prowler.providers.aws.services.route53.route53_public_hosted_zones_cloudwatch_logging_enabled.route53_public_hosted_zones_cloudwatch_logging_enabled.route53_client",
|
||||
new=route53,
|
||||
):
|
||||
# Test Check
|
||||
|
||||
@@ -218,3 +218,42 @@ class Test_Route53_Service:
|
||||
assert not route53.hosted_zones[hosted_zone_id].logging_config
|
||||
|
||||
assert route53.hosted_zones[hosted_zone_id].region == AWS_REGION
|
||||
|
||||
@mock_route53
|
||||
def test__list_resource_record_sets__(self):
|
||||
# Create Hosted Zone
|
||||
r53_client = client("route53", region_name=AWS_REGION)
|
||||
zone = r53_client.create_hosted_zone(
|
||||
Name="testdns.aws.com", CallerReference=str(hash("foo"))
|
||||
)
|
||||
zone_id = zone["HostedZone"]["Id"]
|
||||
|
||||
r53_client.change_resource_record_sets(
|
||||
HostedZoneId=zone_id,
|
||||
ChangeBatch={
|
||||
"Changes": [
|
||||
{
|
||||
"Action": "CREATE",
|
||||
"ResourceRecordSet": {
|
||||
"Name": "foo.bar.testdns.aws.com",
|
||||
"Type": "A",
|
||||
"ResourceRecords": [{"Value": "1.2.3.4"}],
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
# Set partition for the service
|
||||
route53 = Route53(self.set_mocked_audit_info())
|
||||
assert (
|
||||
len(route53.record_sets) == 3
|
||||
) # Default NS and SOA records plus the A record just created
|
||||
for set in route53.record_sets:
|
||||
if set.type == "A":
|
||||
assert set.name == "foo.bar.testdns.aws.com."
|
||||
assert set.type == "A"
|
||||
assert not set.is_alias
|
||||
assert set.records == ["1.2.3.4"]
|
||||
assert set.hosted_zone_id == zone_id.replace("/hostedzone/", "")
|
||||
assert set.region == AWS_REGION
|
||||
|
||||
@@ -17,7 +17,7 @@ class Test_shield_advanced_protection_in_route53_hosted_zones:
|
||||
"prowler.providers.aws.services.shield.shield_service.Shield",
|
||||
new=shield_client,
|
||||
), mock.patch(
|
||||
"prowler.providers.aws.services.route53.route53_service.Route53",
|
||||
"prowler.providers.aws.services.shield.shield_advanced_protection_in_route53_hosted_zones.shield_advanced_protection_in_route53_hosted_zones.route53_client",
|
||||
new=route53_client,
|
||||
):
|
||||
# Test Check
|
||||
@@ -67,7 +67,7 @@ class Test_shield_advanced_protection_in_route53_hosted_zones:
|
||||
"prowler.providers.aws.services.shield.shield_service.Shield",
|
||||
new=shield_client,
|
||||
), mock.patch(
|
||||
"prowler.providers.aws.services.route53.route53_service.Route53",
|
||||
"prowler.providers.aws.services.shield.shield_advanced_protection_in_route53_hosted_zones.shield_advanced_protection_in_route53_hosted_zones.route53_client",
|
||||
new=route53_client,
|
||||
):
|
||||
# Test Check
|
||||
@@ -116,7 +116,7 @@ class Test_shield_advanced_protection_in_route53_hosted_zones:
|
||||
"prowler.providers.aws.services.shield.shield_service.Shield",
|
||||
new=shield_client,
|
||||
), mock.patch(
|
||||
"prowler.providers.aws.services.route53.route53_service.Route53",
|
||||
"prowler.providers.aws.services.shield.shield_advanced_protection_in_route53_hosted_zones.shield_advanced_protection_in_route53_hosted_zones.route53_client",
|
||||
new=route53_client,
|
||||
):
|
||||
# Test Check
|
||||
@@ -165,7 +165,7 @@ class Test_shield_advanced_protection_in_route53_hosted_zones:
|
||||
"prowler.providers.aws.services.shield.shield_service.Shield",
|
||||
new=shield_client,
|
||||
), mock.patch(
|
||||
"prowler.providers.aws.services.route53.route53_service.Route53",
|
||||
"prowler.providers.aws.services.shield.shield_advanced_protection_in_route53_hosted_zones.shield_advanced_protection_in_route53_hosted_zones.route53_client",
|
||||
new=route53_client,
|
||||
):
|
||||
# Test Check
|
||||
|
||||
Reference in New Issue
Block a user