This text is on http://www.nlnetlabs.nl/downloads/CVE-2012-29789.txt Subject: NSD denial of service vulnerability from DNS packet when using --enable-zone-stats (default off). [VU#517036 CVE-2012-2979 ] == Summary When requesting non authoritative data, and you use the new, experimental per zone statistics feature introduced in NSD 3.2.11, NSD wants to log the query statistics to a zone reference that is not set. == Description It is possible to crash (SIGSEGV) a NSD child server process by sending it a query for non authoritative data. A crashed child process will automatically be restarted by the parent process, but an attacker may keep the NSD server occupied restarting child processes by sending it a stream of such packets effectively preventing the NSD server to serve. NSD 3.2.11 and NSD 3.2.12 are vulnerable to this attack, and only if you have enabled the experimental per zone statistics (--enable-zone-stats). This is by default disabled. == Remote Exploit. The problem packet causes NSD to dereference a null pointer. Most operating systems map the null pointer's address such that accessing it causes a segmentation fault, ruling out the possibility for remote exploit. == Work around Disable the per zone statistics: $ ./configure --disable-zone-stats && make && make install == Solution Apply the attached patch in the NSD source directory with 'patch -p0 qname); - ZTATUP2(q->zone, opcode, q->opcode); - ZTATUP2(q->zone, qtype, q->qtype); - ZTATUP2(q->zone, opcode, q->qclass); + if (q->zone) { + ZTATUP2(q->zone, opcode, q->opcode); + ZTATUP2(q->zone, qtype, q->qtype); + ZTATUP2(q->zone, opcode, q->qclass); + } offset = dname_label_offsets(q->qname)[domain_dname(closest_encloser)->label_count - 1] + QHEADERSZ; query_add_compression_domain(q, closest_encloser, offset); @@ -1403,7 +1405,9 @@ } ARCOUNT_SET(q->packet, ARCOUNT(q->packet) + 1); STATUP(nsd, edns); - ZTATUP(q->zone, edns); + if (q->zone) { + ZTATUP(q->zone, edns); + } break; case EDNS_ERROR: if (q->edns.dnssec_ok) edns->error[7] = 0x80; @@ -1412,7 +1416,9 @@ buffer_write(q->packet, edns->rdata_none, OPT_RDATA); ARCOUNT_SET(q->packet, ARCOUNT(q->packet) + 1); STATUP(nsd, ednserr); - ZTATUP(q->zone, ednserr); + if (q->zone) { + ZTATUP(q->zone, ednserr); + } break; } Index: server.c =================================================================== --- server.c (revision 3615) +++ server.c (working copy) @@ -1417,15 +1417,20 @@ #ifdef BIND8_STATS if (RCODE(q->packet) == RCODE_OK && !AA(q->packet)) { STATUP(data->nsd, nona); - ZTATUP(q->zone, nona); +# ifdef USE_ZONE_STATS + if (q->zone) + ZTATUP(q->zone, nona); +# endif } # ifdef USE_ZONE_STATS + if (q->zone) { if (data->socket->addr->ai_family == AF_INET) { ZTATUP(q->zone, qudp); } else if (data->socket->addr->ai_family == AF_INET6) { ZTATUP(q->zone, qudp6); } + } # endif #endif @@ -1443,17 +1448,27 @@ if (sent == -1) { log_msg(LOG_ERR, "sendto failed: %s", strerror(errno)); STATUP(data->nsd, txerr); - ZTATUP(q->zone, txerr); + +#ifdef USE_ZONE_STATS + if (q->zone) + ZTATUP(q->zone, txerr); +#endif } else if ((size_t) sent != buffer_remaining(q->packet)) { log_msg(LOG_ERR, "sent %d in place of %d bytes", sent, (int) buffer_remaining(q->packet)); #ifdef BIND8_STATS } else { /* Account the rcode & TC... */ STATUP2(data->nsd, rcode, RCODE(q->packet)); - ZTATUP2(q->zone, rcode, RCODE(q->packet)); +# ifdef USE_ZONE_STATS + if (q->zone) + ZTATUP2(q->zone, rcode, RCODE(q->packet)); +# endif if (TC(q->packet)) { STATUP(data->nsd, truncated); - ZTATUP(q->zone, truncated); +# ifdef USE_ZONE_STATS + if (q->zone) + ZTATUP(q->zone, truncated); +# endif } #endif /* BIND8_STATS */ } @@ -1665,12 +1680,16 @@ && !AA(data->query->packet)) { STATUP(data->nsd, nona); - ZTATUP(data->query->zone, nona); +# ifdef USE_ZONE_STATS + if (data->query->zone) + ZTATUP(data->query->zone, nona); +# endif } # ifdef USE_ZONE_STATS + if (data->query->zone) { # ifndef INET6 - ZTATUP(data->query->zone, ctcp); + ZTATUP(data->query->zone, ctcp); # else if (data->query->addr.ss_family == AF_INET) { ZTATUP(data->query->zone, ctcp); @@ -1678,6 +1697,7 @@ ZTATUP(data->query->zone, ctcp6); } # endif + } # endif /* USE_ZONE_STATS */ #endif /* BIND8_STATS */