feat(cloudflare): expand wildcard check to include MX and SRV records

Wildcard MX records can allow mail interception for arbitrary subdomains.
Wildcard SRV records can expose services on any subdomain.
This commit is contained in:
HugoPBrito
2026-01-20 14:35:21 +01:00
parent 38f6ca9514
commit 3e3f56629f
3 changed files with 116 additions and 9 deletions

View File

@@ -9,8 +9,8 @@
"Severity": "medium",
"ResourceType": "DNSRecord",
"ResourceGroup": "network",
"Description": "**Cloudflare DNS records** are assessed for **wildcard usage** by checking if A, AAAA, or CNAME records use wildcard entries (*.example.com) which can **increase attack surface** and expose unintended services.",
"Risk": "**Wildcard DNS records** can expose unintended services and increase attack surface.\n- **Confidentiality**: any subdomain resolves, potentially exposing internal naming conventions\n- **Integrity**: attackers can access unintended services via arbitrary subdomains\n- **Availability**: wildcard records may route traffic to services not designed for public access",
"Description": "**Cloudflare DNS records** are assessed for **wildcard usage** by checking if A, AAAA, CNAME, MX, or SRV records use wildcard entries (*.example.com) which can **increase attack surface**, expose unintended services, or allow mail interception.",
"Risk": "**Wildcard DNS records** can expose unintended services and increase attack surface.\n- **Confidentiality**: any subdomain resolves, potentially exposing internal naming conventions; wildcard MX allows mail interception\n- **Integrity**: attackers can access unintended services via arbitrary subdomains\n- **Availability**: wildcard records may route traffic or services not designed for public access",
"RelatedUrl": "",
"AdditionalURLs": [
"https://developers.cloudflare.com/dns/manage-dns-records/how-to/create-dns-records/"
@@ -32,5 +32,5 @@
],
"DependsOn": [],
"RelatedTo": [],
"Notes": "Wildcard DNS records (*.example.com) cause any subdomain query to resolve. While useful for some applications, they can expose services unintentionally and make subdomain enumeration easier for attackers."
"Notes": "Wildcard DNS records (*.example.com) cause any subdomain query to resolve. While useful for some applications, they can expose services unintentionally and make subdomain enumeration easier for attackers. Wildcard MX records can accept mail for any subdomain, and wildcard SRV records can expose services on arbitrary subdomains."
}

View File

@@ -1,6 +1,13 @@
from prowler.lib.check.models import Check, CheckReportCloudflare
from prowler.providers.cloudflare.services.dns.dns_client import dns_client
# Record types where wildcards pose security risks:
# - A, AAAA: Wildcard resolves any subdomain to an IP, exposing services
# - CNAME: Wildcard aliases any subdomain, potential for subdomain takeover
# - MX: Wildcard accepts mail for any subdomain, potential mail interception
# - SRV: Wildcard exposes services on any subdomain
WILDCARD_RISK_TYPES = {"A", "AAAA", "CNAME", "MX", "SRV"}
class dns_record_no_wildcard(Check):
"""Ensure that wildcard DNS records are not configured for the zone.
@@ -9,15 +16,16 @@ class dns_record_no_wildcard(Check):
an explicit record, which can unintentionally expose services or create
security risks. Attackers may discover hidden services, and wildcard
certificates combined with wildcard DNS can increase the attack surface
for subdomain takeover vulnerabilities.
for subdomain takeover vulnerabilities. Wildcard MX records can allow
mail interception for arbitrary subdomains.
"""
def execute(self) -> list[CheckReportCloudflare]:
"""Execute the wildcard DNS record check.
Iterates through all A, AAAA, and CNAME DNS records and identifies
those configured as wildcard records (starting with *.). Wildcard
records may expose unintended services.
Iterates through all security-relevant DNS records (A, AAAA, CNAME, MX, SRV)
and identifies those configured as wildcard records (starting with *.).
Wildcard records may expose unintended services or create security risks.
Returns:
A list of CheckReportCloudflare objects with PASS status if the
@@ -26,8 +34,8 @@ class dns_record_no_wildcard(Check):
findings = []
for record in dns_client.records:
# Only check A, AAAA, and CNAME records for wildcards
if record.type not in ("A", "AAAA", "CNAME"):
# Check record types where wildcards pose security risks
if record.type not in WILDCARD_RISK_TYPES:
continue
report = CheckReportCloudflare(

View File

@@ -244,3 +244,102 @@ class Test_dns_record_no_wildcard:
assert len(result) == 1
assert result[0].status == "PASS"
assert "is not a wildcard record" in result[0].status_extended
def test_mx_record_wildcard(self):
dns_client = mock.MagicMock
dns_client.records = [
CloudflareDNSRecord(
id="record-1",
zone_id=ZONE_ID,
zone_name=ZONE_NAME,
name="*.example.com",
type="MX",
content="10 mail.example.com",
)
]
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_cloudflare_provider(),
),
mock.patch(
"prowler.providers.cloudflare.services.dns.dns_record_no_wildcard.dns_record_no_wildcard.dns_client",
new=dns_client,
),
):
from prowler.providers.cloudflare.services.dns.dns_record_no_wildcard.dns_record_no_wildcard import (
dns_record_no_wildcard,
)
check = dns_record_no_wildcard()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert "is a wildcard record" in result[0].status_extended
def test_mx_record_not_wildcard(self):
dns_client = mock.MagicMock
dns_client.records = [
CloudflareDNSRecord(
id="record-1",
zone_id=ZONE_ID,
zone_name=ZONE_NAME,
name="example.com",
type="MX",
content="10 mail.example.com",
)
]
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_cloudflare_provider(),
),
mock.patch(
"prowler.providers.cloudflare.services.dns.dns_record_no_wildcard.dns_record_no_wildcard.dns_client",
new=dns_client,
),
):
from prowler.providers.cloudflare.services.dns.dns_record_no_wildcard.dns_record_no_wildcard import (
dns_record_no_wildcard,
)
check = dns_record_no_wildcard()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert "is not a wildcard record" in result[0].status_extended
def test_srv_record_wildcard(self):
dns_client = mock.MagicMock
dns_client.records = [
CloudflareDNSRecord(
id="record-1",
zone_id=ZONE_ID,
zone_name=ZONE_NAME,
name="*._tcp.example.com",
type="SRV",
content="10 5 5060 sip.example.com",
)
]
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_cloudflare_provider(),
),
mock.patch(
"prowler.providers.cloudflare.services.dns.dns_record_no_wildcard.dns_record_no_wildcard.dns_client",
new=dns_client,
),
):
from prowler.providers.cloudflare.services.dns.dns_record_no_wildcard.dns_record_no_wildcard import (
dns_record_no_wildcard,
)
check = dns_record_no_wildcard()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert "is a wildcard record" in result[0].status_extended