CyStack Advisory ID CSA-2019-04
CVE IDs CVE-2019-19307
Severity Critical
CVSS v3 Base 9.8

Synopsis

CyStack Security discovered an integer overflow vulnerability in the implementation of MQTT protocol in the Cesanta Mongoose Library version 6.16. By exploiting the vulnerability, a remote, unauthenticated attacker can perform a DoS attack to broker server with an infinite loop or a potential remote code execution due to its out of bound access ability.

Details

MQTT is a machine-to-machine (M2M)/"Internet of Things" connectivity protocol. It was designed as an extremely lightweight publish/subscribe messaging transport. It is useful for connections with remote locations where a small code footprint is required and/or network bandwidth is at a premium. For example, it has been used in sensors communicating to a broker via satellite link, over occasional dial-up connections with healthcare providers, and in a range of home automation and small device scenarios. It is also ideal for mobile applications because of its small size, low power usage, minimized data packets, and efficient distribution of information to one or many receivers.

As usual, we move from outside to inside of library and the first feature we meet is about handling a new connection:

When client sends a message, broker server treats it as MG_EV_RECV and starts parsing the packet. Due to network stack mechanism, server may not receive and process a full packet, so it uses a while loop here and after each iteration, it removes the old input. Before moving in deeper, we should take a brief look about the format of MQTT packet. According to this article, a payload sent from client will have the following structure:

The 8 bit control field is the first byte of the 2 byte fixed header. It is divided into two 4 bit fields and contains all of the protocol commands and responses. The first 4 Most significant bits are the command or message type field and the other 4 bits are used as control flags.

Packet length is of variable length between 1 and 4 bytes. Each byte uses 7 bits for the length with the MSB used as a continuation flag. The remaining length is the number of bytes following the length field, includes variable length header and payload as illustrated below:

Now let's see how Cesanta Mongoose Library parses a MQTT packet:

First it checks if size of buffer is less than 2 or not, then it retrieves header and command by shift operation. Next, the library decode variable length by doing a while loop, which is stopped when there is no continuous flag or the number of byte is greater than 4. In each iteration, the len will be increased by a value equals to 7 bits of current byte. Data type of len is size_t and the range of this kind of data type is from 0 to 0xffffffff = 4294967296, meanwhile the maximum possible value len can have is (0x7f << (7 * 4)) & 0xffffffff = 4026531840. It seems to be fine ...

Surprisingly, when we use IDA to open the binary, which was compiled in Ubuntu x64 with default gcc, we realize that something is wrong:

len is signed integer and len_len is unsigned integer!!! What happens now? Let's create a piece of code C to test this edge case:

#include <stdio.h>

int main(void) {
	unsigned char lc = '\x7f';
	size_t len = 4;
	
	printf("%zu\n", (lc & 0x7f) << 7 * len);
	printf("%zu\n", (size_t)((lc & 0x7f) << 7 * len));
	
	return 0;
}

and the result is ...

4026531840
18446744073441116160

len has been overflowed and end is definitely decreased rather than grown up to eop. This result is also returned to function mqtt_handle in order to remove the old data.

MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) {
	...
	mm->len = end - io->buf;
	return mm->len;
}

Unfortunately the handler function has a validation about value and if it is negative, the connection will be closed. Therefore, we can only force parse_mqtt to return 0 so handler won't remove old data, which leads to an infinite loop for processing one crafted payload.

Timeline

  • 2/10/2019: CyStack reported to Cesanta Mongoose team
  • 18/10/2019: CyStack requested a CVE to MITRE
  • 26/11/2019: CVE-2019-19307 is assigned for the vulnerability