Flawed logic in resume_dslookup() may trigger assertion failures
Commit 7b2ea97e (part of !6188 (merged))
introduced a logic bug in resume_dslookup()
: that function now only
performs a "resolution boundary check"1 conditionally, specifically
only when the previous resume_dslookup()
call invoked
dns_resolver_createfetch()
with the nameservers
argument set to
something else than NULL
, which may not always be the case. Skipping
the "resolution boundary check" causes resume_dslookup()
to chop a
label off from the root name and then try to find name servers for such
an invalid name; this triggers assertion failures in various locations,
depending on the version:
-
main
:ht.c:366: REQUIRE(key != ((void *)0) && keysize > 0) failed, back trace
-
v9_18
:rbt.c:807: REQUIRE((((name)->attributes & 0x00000001) != 0 ? 1 : 0)) failed, back trace
Consider the following scenario:
-
A validating resolver is configured to forward queries to another resolver that returns broken DS responses that trigger DS chasing.
-
rctx_chaseds()
callsdns_resolver_createfetch()
with thenameservers
argument set toNULL
(note that a prerequisite for this is thatforward only;
has to be in effect for the part of the tree that the QNAME, i.e. the owner name of the DS record being chased with one label chopped off, belongs to). -
The fetch fails, so
resume_dslookup()
is called. Due tofevent->result
being set to e.g.DNS_R_SERVFAIL
, thedefault
branch is taken in theswitch
statement. -
Since
nameservers
was set toNULL
for the fetch which caused theresume_dslookup()
callback to be invoked (fctx->nsfetch->private->nameservers
),resume_dslookup()
chops one label offfctx->nsname
and callsdns_resolver_createfetch()
again, for a name containing one label less. -
Steps 3-4 are repeated (i.e. all attempts to find the name servers authoritative for the DS RRset being chased fail) until
fctx->nsname
becomes stripped down the the root name (.
). -
Since the
dns_name_equal(fctx->nsname, domain)
check is not performed,resume_dslookup()
strips a label off the root name and continues its attempts at finding the name servers authoritative for the DS RRset being chased, passing an invalid name todns_resolver_createfetch()
.💥
The scenario above is possible to reproduce reliably, though writing a test for it would be very cumbersome because it would involve implementing a DNS server that returns broken DS responses for some zones while also returning correct responses to other queries, plus some of the data would have to be properly DNSSEC-signed.
Here is the simplest way I could find to reproduce this issue on a Linux system:
-
Configure
named
to forward queries to a resolver returning broken DS responses for RFC 6303 zones:options { forward only; forwarders { 208.67.222.222; }; };
-
Block outgoing NS queries for
in-addr.arpa.
,arpa.
, and.
sent to the configured forwarder2:iptables -I OUTPUT 1 -p udp --dport 53 -d 208.67.222.222 -m u32 --u32 '0>>22&0x3C@32&0xFFFF=0x0002' -j DROP iptables -I OUTPUT 1 -p udp --dport 53 -d 208.67.222.222 -m u32 --u32 '0>>22&0x3C@24&0xFFFF=0x0002' -j DROP iptables -I OUTPUT 1 -p udp --dport 53 -d 208.67.222.222 -m u32 --u32 '0>>22&0x3C@19&0xFFFF=0x0002' -j DROP
-
Start
named
and query it for a PTR record for some IP address in RFC 1918 space:dig @localhost 1.0.0.10.in-addr.arpa. PTR
I do not think this is exploitable, but I am marking this issue as confidential out of abundance of caution since a crash is involved. The bug was introduced in BIND 9.19.1 and BIND 9.18.3.
See also Support RT #20920