From 455178a7d966031a61fb5924bca687798c939e23 Mon Sep 17 00:00:00 2001 From: liquidsec Date: Mon, 15 Jun 2026 22:56:08 -0400 Subject: [PATCH] Fix dnscaa crash on unrecognized CAA properties (e.g. contactemail) --- bbot/modules/dnscaa.py | 24 ++++++++++++++++++- .../module_tests/test_module_dnscaa.py | 6 +++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/bbot/modules/dnscaa.py b/bbot/modules/dnscaa.py index c132f6af5e..279acd216d 100644 --- a/bbot/modules/dnscaa.py +++ b/bbot/modules/dnscaa.py @@ -67,8 +67,21 @@ async def handle_event(self, event): if not isinstance(caa, dict): continue - tag = (caa.get("tag") or "").lower() + raw_tag = caa.get("tag") + # hickory wraps unrecognized CAA properties as {"Unknown": "name"} + if isinstance(raw_tag, dict): + raw_tag = raw_tag.get("Unknown", "") + if not isinstance(raw_tag, str): + continue + tag = raw_tag.lower() + value = caa.get("value") or {} + # hickory wraps unrecognized values as {"Unknown": [byte_array]} + if isinstance(value, dict) and isinstance(value.get("Unknown"), list): + try: + value = {"raw_text": bytes(value["Unknown"]).decode()} + except (ValueError, UnicodeDecodeError): + continue # iodef -> "Url" containing mailto: or https:// for incident reporting if tag == "iodef": @@ -90,6 +103,15 @@ async def handle_event(self, event): parent=event, ) + # contactemail (RFC 8657) -> email for CA to contact domain owner + elif tag == "contactemail": + raw_text = value.get("raw_text") if isinstance(value, dict) else None + if raw_text and self._emails: + for match in email_regex.finditer(raw_text): + await self.emit_event( + raw_text[match.start() : match.end()], "EMAIL_ADDRESS", tags=tags, parent=event + ) + # issue / issuewild -> "Issuer" containing the CA's domain elif tag.startswith("issue"): if not self._dns_names: diff --git a/bbot/test/test_step_2/module_tests/test_module_dnscaa.py b/bbot/test/test_step_2/module_tests/test_module_dnscaa.py index 8f3a614fe5..c2b0d69f15 100644 --- a/bbot/test/test_step_2/module_tests/test_module_dnscaa.py +++ b/bbot/test/test_step_2/module_tests/test_module_dnscaa.py @@ -22,6 +22,9 @@ async def setup_after_prep(self, module_test): '1 issue "digicert.com; cansignhttpexchanges=yes"', '0 issuewild "letsencrypt.org"', '128 issuewild "pki.goog; cansignhttpexchanges=yes"', + '0 contactemail "contact@blacklanternsecurity.notreal"', + '0 contactphone "+1-555-123-4567"', + '0 somefuturetag "whatever"', ], }, "caa.blacklanternsecurity.notreal": {"A": ["127.0.0.22"]}, @@ -51,6 +54,9 @@ def check(self, module_test, events): assert any(e.type == "EMAIL_ADDRESS" and e.data == "caa@blacklanternsecurity.notreal" for e in events), ( "Failed to detect email address" ) + assert any(e.type == "EMAIL_ADDRESS" and e.data == "contact@blacklanternsecurity.notreal" for e in events), ( + "Failed to detect contactemail address" + ) # make sure we're not checking CAA records for out-of-scope hosts assert not any(str(e.host) == "caa.comodoca.com" for e in events)