Test that positive answer cannot overwrite sibling NS RRs

Before the fixes for CVE-2025-40778, a positive answer was allowed to
overwrite sibling NS RRs.  The answer had to be a positive AA=1 answer
with a fake NS along with it.  This combination of conditions avoided
the code path with "unrelated <RRTYPE>" detection logic.

If it were some other answer, named from the main branch would detect
the attempt and log:

    DNS format error from 10.53.0.1#16386 resolving trigger/A for <unknown>: unrelated NS victim in trigger authority section

In short, the attacker tries to spoof at least one answer that has the
following form:

    opcode QUERY
    rcode NOERROR
    flags QR AA
    ;QUESTION
    trigger$RANDOM. IN A
    ;ANSWER
    trigger$RANDOM. 3600 IN A 10.53.0.3
    ;AUTHORITY
    victim. 3600 IN NS ns.attacker.
    ;ADDITIONAL
    ns.attacker. 3600 IN A 10.53.0.3

This attack was originally reported as "test case 1c".

Co-authored-by: Michał Kępień <michal@isc.org>
(cherry picked from commit 26eed16d61)
This commit is contained in:
Petr Špaček 2025-07-11 18:37:57 +02:00 committed by Michał Kępień (GitLab job 6660033)
parent f5b5a94439
commit 0a00d3c2c9
2 changed files with 39 additions and 0 deletions

View file

@ -29,6 +29,37 @@ ATTACKER_IP = "10.53.0.3"
TTL = 3600
class SiblingNsSpoofer(ResponseSpoofer, mode="sibling-ns"):
qname = "trigger."
async def get_responses(
self, qctx: QueryContext
) -> AsyncGenerator[ResponseAction, None]:
response = qctx.prepare_new_response(with_zone_data=False)
txt_rrset = dns.rrset.from_text(
qctx.qname,
TTL,
qctx.qclass,
dns.rdatatype.TXT,
'"spoofed answer with extra NS"',
)
response.answer.append(txt_rrset)
ns_rrset = dns.rrset.from_text(
"victim.", TTL, qctx.qclass, dns.rdatatype.NS, "ns.attacker."
)
response.authority.append(ns_rrset)
a_rrset = dns.rrset.from_text(
"ns.attacker.", TTL, qctx.qclass, dns.rdatatype.A, ATTACKER_IP
)
response.additional.append(a_rrset)
yield DnsResponseSend(response, authoritative=True)
def main() -> None:
spoofing_server().run()

View file

@ -77,3 +77,11 @@ def check_domain_hijack(ns4: NamedInstance) -> None:
"TXT",
'"correct answer from the domain under attack"',
)
def test_bailiwick_sibling_ns_referral(servers: Dict[str, NamedInstance]) -> None:
set_spoofing_mode(ans1="sibling-ns", ans2="none")
ns4 = servers["ns4"]
send_trigger_query(ns4, "trigger.")
check_domain_hijack(ns4)