Investigate how CVE-2015-5477 & CloudFlare Virtual DNS protects its users

Source: Internet
Author: User
Tags cloudflare

Investigate how CVE-2015-5477 & CloudFlare Virtual DNS protects its users

Last week, ISC released a patch to fix a remote vulnerability in the BIND9 DNS server. This vulnerability causes the server to crash when processing a certain data packet.


The announcement indicates that an error occurs when the server processes a TKEY-type query. This error causes assertion fail, which causes the server to crash. Because assertion occurs in the query parsing process, this problem cannot be avoided: when the server receives data packets, the first thing to do is to parse the query, then, make a decision as needed.

TSIG is a protocol used by DNS servers to verify each other. TKEY query is used in the context of this Protocol. Different from conventional DNS queries, the TKEY query information contains an EXTRA/ADDITIONAL section, which contains a "meta" record about the TKEY type.

Because the exploitation of data packets has been made public, I think we can study this vulnerability code. Let's take a look at the output results of this crashed instance:

03-Aug-2015 16:38:55.509 message.c:2352: REQUIRE(*name == ((void*)0)) failed, back trace 03-Aug-2015 16:38:55.510 #0 0x10001510d in assertion_failed()+0x5d 03-Aug-2015 16:38:55.510 #1 0x1001ee56a in isc_assertion_failed()+0xa 03-Aug-2015 16:38:55.510 #2 0x1000bc31d in dns_message_findname()+0x1ad 03-Aug-2015 16:38:55.510 #3 0x10017279c in dns_tkey_processquery()+0xfc 03-Aug-2015 16:38:55.510 #4 0x100016945 in ns_query_start()+0x695 03-Aug-2015 16:38:55.510 #5 0x100008673 in client_request()+0x18d3 03-Aug-2015 16:38:55.510 #6 0x1002125fe in run()+0x3ce 03-Aug-2015 16:38:55.510 exiting (due to assertion failure) [1]    37363 abort (core dumped)  ./bin/named/named -f -c named.conf


The crash code above has a lot of inspiration for us. It tells us that this is a crash caused by assertion failure, and tells us where the problem occurs in message. c: 2352. the following is a summary of the vulnerability code:

// https://source.isc.org/git/bind9.git -- faa3b61 -- lib/dns/message.c       isc_result_t    dns_message_findname(dns_message_t *msg, dns_section_t section,                 dns_name_t *target, dns_rdatatype_t type,                 dns_rdatatype_t covers, dns_name_t **name,                 dns_rdataset_t **rdataset)    {        dns_name_t *foundname;        isc_result_t result;           /*         * XXX These requirements are probably too intensive, especially         * where things can be NULL, but as they are they ensure that if         * something is NON-NULL, indicating that the caller expects it         * to be filled in, that we can in fact fill it in.         */        REQUIRE(msg != NULL);        REQUIRE(VALID_SECTION(section));        REQUIRE(target != NULL);        if (name != NULL)==>         REQUIRE(*name == NULL);       [...]


Here, we find a function "dns_message_findname", which is used to find RRset with the same name and type based on the name and type given in the message section. This function applies a common c api: to obtain the result, and fills the pointer passed by caller in the result (dns_name_t ** name, dns_rdataset_t ** rdataset ).


It is ironic that the verification process for these pointers is really strict: if these pointers do not point to (dns_name_t *) NULL, REQUIRE assertion will fail and the server will crash, and will not try to recover. The code that calls this function must be very careful to pass the pointer to NULL dns_name_t *, and the function will be filled with the name found in the Code.

In non-Memory Security languages, crashes often occur when assertion is ineffective. When an exception occurs, the program may not be able to clean up its memory.

Therefore, in the investigation, we use stacks to find illegal calls. The following is dns_tkey_processquery. The following is a simplified summary:

// https://source.isc.org/git/bind9.git -- faa3b61 -- lib/dns/tkey.c   isc_result_t dns_tkey_processquery(dns_message_t *msg, dns_tkeyctx_t *tctx,               dns_tsig_keyring_t *ring){    isc_result_t result = ISC_R_SUCCESS;    dns_name_t *qname, *name;    dns_rdataset_t *tkeyset;       /*     * Interpret the question section.     */    result = dns_message_firstname(msg, DNS_SECTION_QUESTION);    if (result != ISC_R_SUCCESS)        return (DNS_R_FORMERR);       qname = NULL;    dns_message_currentname(msg, DNS_SECTION_QUESTION, &qname);       /*     * Look for a TKEY record that matches the question.     */    tkeyset = NULL;    name = NULL;    result = dns_message_findname(msg, DNS_SECTION_ADDITIONAL, qname,                      dns_rdatatype_tkey, 0, &name, &tkeyset);    if (result != ISC_R_SUCCESS) {        /*         * Try the answer section, since that's where Win2000         * puts it.         */        if (dns_message_findname(msg, DNS_SECTION_ANSWER, qname,                     dns_rdatatype_tkey, 0, &name,                     &tkeyset) != ISC_R_SUCCESS) {            result = DNS_R_FORMERR;            tkey_log("dns_tkey_processquery: couldn't find a TKEY "                 "matching the question");            goto failure;        }    }   [...]
Here there are two dns_message_findname calls, because we are looking for a call to pass a malicious name, so we can ignore the first call, because name = NULL;

The second call is interesting. After dns_message_findname is called, the same dns_name_t * name is used again, and it is not set to NULL. This may be where the bug occurs.


The question is, when will dns_message_findname set name without returning ISC_R_SUCCESS (so that the if condition can be met )? Now let's take a look at the complete function body.
// https://source.isc.org/git/bind9.git -- faa3b61 -- lib/dns/message.c   isc_result_t dns_message_findname(dns_message_t *msg, dns_section_t section,              dns_name_t *target, dns_rdatatype_t type,             dns_rdatatype_t covers, dns_name_t **name,             dns_rdataset_t **rdataset){    dns_name_t *foundname;    isc_result_t result;       /*     * XXX These requirements are probably too intensive, especially     * where things can be NULL, but as they are they ensure that if     * something is NON-NULL, indicating that the caller expects it     * to be filled in, that we can in fact fill it in.     */    REQUIRE(msg != NULL);    REQUIRE(VALID_SECTION(section));    REQUIRE(target != NULL);    if (name != NULL)        REQUIRE(*name == NULL);    if (type == dns_rdatatype_any) {        REQUIRE(rdataset == NULL);    } else {        if (rdataset != NULL)            REQUIRE(*rdataset == NULL);    }       result = findname(&foundname, target,              &msg->sections[section]);       if (result == ISC_R_NOTFOUND)        return (DNS_R_NXDOMAIN);    else if (result != ISC_R_SUCCESS)        return (result);       if (name != NULL)        *name = foundname;       /*     * And now look for the type.     */    if (type == dns_rdatatype_any)        return (ISC_R_SUCCESS);       result = dns_message_findtype(foundname, type, covers, rdataset);    if (result == ISC_R_NOTFOUND)        return (DNS_R_NXRRSET);       return (result);}

You can find that dns_message_findname first uses findnamet to match the record with the same target name, and then uses dns_message_findtype to match the target type. Between these two calls... * name = foundname! Even if dns_message_findname finds a record with name = qname in DNS_SECTION_ADDITIONAL, the name of the type not dns_rdatatype_tkey will be filled and an error will be returned. The second dns_message_findname call will trigger a malicious name, and then it will be out of control.

Indeed, the patch only adds name = NULL before the second call (no, our starting point is not the patch program, or what else will it mean)
diff --git a/lib/dns/tkey.c b/lib/dns/tkey.c index 66210d5..34ad90b 100644 --- a/lib/dns/tkey.c+++ b/lib/dns/tkey.c@@ -654,6 +654,7 @@ dns_tkey_processquery(dns_message_t *msg, dns_tkeyctx_t *tctx,          * Try the answer section, since that's where Win2000          * puts it.          */+        name = NULL;         if (dns_message_findname(msg, DNS_SECTION_ANSWER, qname,                      dns_rdatatype_tkey, 0, &name,                      &tkeyset) != ISC_R_SUCCESS) {

Let's take a look at the bug triggering process:

After receiving a TKEY-type query, call dns_tkey_processquery to parse the query.

In the EXTRA section, a record with the same name as the query is found, resulting in name filling. However, this record is not a TKEY record, resulting in result! = ISC_R_SUCCESS

Call dns_message_findname again to find it in ANS section. Now it is based on Malicious name Reference

Assertion * name! = NULL fail, BIND crash

@ Jfoote _ found this bug through the american fuzzy lop fuzzy test tool. The Fuzzy testing tool is an automatic tool that automatically submits Abnormal Input to the target program until the program crashes. You can use TKEY query + a combination of non-tkey extra rr to check how the server crashes and find the bug.

Virtual DNS users are secure

Good news! The BIND server of CloudFlare Virtual DNS users will not be affected by this attack. If necessary, our custom Go DNS server-PRDNS will first parse all the queries and "disinfect" them before forwarding the queries back to the original server.

Because Virtual DNS does not support TSIG and TKEY (used to authenticate the traffic between the server and the server, rather than recursive queries), there is no need to forward EXTRA Section records in the query, virtual DNS does not. In this way, the attack risk is much lower, and Virtual DNS cannot be used to exploit this vulnerability.

There is no way to defend against this vulnerability: PRDNS always verifies that the incoming data packet is benign, ensures that the query is normal, and simplifies it into the simplest form before forwarding.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.