Talos Vulnerability Report

TALOS-2017-0416

Cesanta Mongoose DNS Query Compressed Name Pointer Denial Of Service

October 31, 2017
CVE Number

CVE-2017-2909

Summary

An infinite loop programming error exists in the DNS server functionality of Cesanta Mongoose 6.8 library. A specially crafted DNS request can cause an infinite loop resulting in high CPU usage and Denial Of Service. An attacker can send a packet over network to trigger this vulnerability.

Tested Versions

Cesanta Mongoose 6.8

Product URLs

https://cesanta.com/

CVSSv3 Score

7.5 - CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

CWE

CWE-835: Loop with Unreachable Exit Condition (‘Infinite Loop’)

Details

Mongoose is a monolithic library implementing a number of networking protocols, including HTTP, MQTT, DNS and others. It is designed with embedded devices in mind and as such is used in many IoT devices and runs on virtually all platforms.

In a DNS request packet, a name can be compressed to save space. The compression in question relies on pointers to parts of the domain name already present in the packet. When decompressing the names, the DNS server software must look up those pointers and substitute them accordingly.

In the Mongoose library, the function mg_dns_uncompress_name is responsible for decompressing names and a special case for pointers is handled:

while ((chunk_len = *data++)) {                                        [3]
 int leeway = dst_len - (dst - old_dst);
 if (data >= end) 
   return 0;
 

 if (chunk_len & 0xc0) {                                        [1]
   uint16_t off = (data[-1] & (~0xc0)) << 8 | data[0];
   if (off >= msg->pkt.len) {
     return 0;
   
   data = (unsigned char *) msg->pkt.p + off;                        [2]
   continue;

The name chunk pointer is encoded in a single byte having 2 most significant bits set (0xc0) and rest are an offset to the actual name. In the above code, at [1], a check is performed to see if the current chunk is actually a pointer, and if so, the offset is extracted instead. At [2], this offset is used to advance the parser by adding it to start of the packet. The loop then continues at [3].

In the above code, no check is performed to see if the calculated offset refers to the same position, or if the pointer points to another pointer. No valid DNS query should have a pointer pointing to another pointer and precisely that kind packet will cause an infinite loop in the above code. An example packet:

00000000: 0020 a577 0120 0001 0000 0000 0001 c00c  . .w. ..........
00000010: 0000 0000 0100 0100 0029 1000 0000 0000  .........)......
00000020: 0000 0a                                  …

At offset 14 above, we have a start of name chunk, which specifies a pointer (0xc0), followed by 0x0c which represents it’s offset from the start of the packet, past the 2 ID bytes. When parsing this packet, function mg_dns_uncompress_name will enter an infinite loop, because the first chunk points to itself.

This causes 100% CPU usage and Denial Of Service.

Timeline

2017-08-30 - Vendor Disclosure
2017-10-31 - Public Release

Credit

Discovered by Aleksandar Nikolic of Cisco Talos.