Security Audit of Unbound DNS Server in Progress

We at X41 are currently performing an audit on the unbound DNS server thanks to the great folks at OSTIF who kindly sponsor our work.

While X41 is still auditing, first results of are already making their way into the unbound codebase thanks to the diligent developers at NLNetLabs.

IPSECMOD - Command Injection

Today, another issue was resolved in unbound, that could lead to remote code execution in case the ipsecmod module was enabled. Eric Sesterhenn noticed a shell injection vulnerability when the ipsecmod helper tool was executed via system().

for(i=0; i<rrset_data->count; i++) {
        if(i > 0) {
                /* Put space into the buffer. */
                sldns_str_print(&s, &slen, " ");
        }
        /* Ignore the first two bytes, they are the rr_data len. */
        tempdata = rrset_data->rr_data[i] + 2;
        tempdata_len = rrset_data->rr_len[i] - 2;
        /* Save the buffer pointers. */
        tempstring = s; tempstring_len = slen;
        w = sldns_wire2str_ipseckey_scan(&tempdata, &tempdata_len, &s, &slen,  
                NULL, 0);
        /* There was an error when parsing the IPSECKEY; reset the buffer      
         * pointers to their previous values. */
        if(w == -1){
                s = tempstring; slen = tempstring_len;
        }
}
sldns_str_print(&s, &slen, "\"");
verbose(VERB_ALGO, "ipsecmod: hook command: '%s'", str);
/* ipsecmod-hook should return 0 on success. */
if(system(str) != 0)
        return 0;

This shell injection can be easily triggered by a malicious DNS response
packet. The unbound project already released an update for this issue and all users are encouraged to update to version 1.9.5. This issue is tracked via CVE-2019-18934

EDNS PARSING - Use After Free

The first issue that triggered a patch was a vulnerability in parsing NOTIFY queries found by Luis Merino. This issue is tracked by CVE-2019-16866 and is about the re-use of unitialized heap memory.

The function worker_handle_request() in file ‘daemon/worker.c’ does the high level parsing of incoming DNS requests. While extracting EDNS information from the incoming packet, it will call parse_edns_from_pkt() with a pointer to the stack allocated struct edns where EDNS data will be stored, if present.

The variable edns is not initialized after declaration in worker_handle_request().

When the input packet has no valid EDNS data, some error paths in parse_edns_from_pkt(), still do not initialize edns, and let worker_handle_request() continue processing the request.

Just after calling parse_edns_from_pkt(), EDNS processing branches will be executed if edns.edns\_present is true} but edns is still not initialized and contains whatever data was present in
the stack when it was declared. This effectively means edns.edns_present can be true even when the request contained no valid EDNS data.

If you are interested in working with us on such projects in the future, ping us!

Author: Eric Sesterhenn
Date: November 19, 2019