CVE-2021-41036
An out-of-bounds write vulnerability exists in the readPacket functionality of Eclipse Foundation Embedded Paho MQTTClient-C library v1.0.0. A specially-crafted MQTT payload can lead to an out-of-bounds write. An attacker can send a malicious MQTT message to trigger this vulnerability.
Eclipse Foundation Embedded Paho MQTTClient-C library v1.0.0
Sealevel Systems, Inc. SeaConnect 370W v1.3.34
Embedded Paho MQTTClient-C library - https://www.eclipse.org/paho/index.php?page=clients/c/embedded/index.php SeaConnect 370W - https://www.sealevel.com/product/370w-a-wifi-to-form-c-relays-digital-inputs-a-d-inputs-and-1-wire-bus-seaconnect-multifunction-io-edge-module-powered-by-seacloud/
9.8 - CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
CWE-120 - Buffer Copy without Checking Size of Input (‘Classic Buffer Overflow’)
The Embedded Paho MQTTClient-C library is a MQTT client library for the language C. This library is primarily designed for embedded devices.
This vulnerability was discovered while looking at the SeaConnect 370W. The mentioned device uses the Paho MQTTClient-C library version 1.0.0. In this version is present a buffer overflow vulnerability in the function readPacket
. This was corrected in July 2017. This correction was treated as a security improvement, and no CVE was assigned to it. The consequence was that the SeaConnect 370W did not update its firmware with the correction. This, in turn, leads to a remote command execution vulnerability in the SeaConnect 370W due to the buffer overflow caused in the Paho MQTTClient-C library version 1.0.0.
The readPacket
function in the SeaConnect 370W is:
uint readPacket(MQTTClient *c,undefined4 param_2)
{
uint rc;
undefined4 timer_;
int iVar1;
dword dVar2;
undefined *ipstack;
dword rem_len;
read_volatile(PTR_DWORD_20012de8._0_1_);
ipstack = c->ipstack;
rem_len = 0;
timer_ = TimerLeftMS(param_2);
rc = (**(code **)(ipstack + 0x30))(ipstack,c->readbuf,1,timer_); [1]
/* calling mqttread ^ */
if (rc == 1) {
timer_ = TimerLeftMS(param_2);
decodePacket(c,&rem_len,timer_); [2]
iVar1 = MQTTPacket_encode(c->readbuf + 1,rem_len);
if (0 < (int)rem_len) {
ipstack = c->ipstack;
timer_ = TimerLeftMS(param_2);
dVar2 = (**(code **)(ipstack + 0x30))(ipstack,c->readbuf + iVar1 + 1,rem_len,timer_); [3]
/* calling mqttread ^ */
if (rem_len != dVar2) {
return 1;
}
}
rc = ((uint)(byte)*c->readbuf << 0x18) >> 0x1c;
}
return rc;
}
This is a Paho MQTTClient-C’s function that handles a new MQTT mesage. It reads at [1]
the heder byte and at [2]
reads, from the packet, the remaining size of the packet. This size is then used at [3]
to read into a heap buffer the remaining message.
The buffer used at [3]
, for the SeaConnect 370W, is allocated in SeaConnectMqtt_Init
:
void SeaConnectMqtt_Init(void)
{
[...]
receiveBuffer_ptr = (int *)read_volatile_4(receiveBuffer);
puVar1 = read_volatile_4(PTR_aSeaconnectmqtt_3_20010be8);
if (*receiveBuffer_ptr == 0) {
malloc_res = malloc(0x201); [4]
*receiveBuffer_ptr = malloc_res;
[...]
}
At [4]
0x201 bytes are allocated to manage a MQTT mesage, but the function readPacket
does not control the size of the variable rem_len
before reading the message content at [3]
. This can lead to a buffer overflow.
The corrected Paho MQTTClient-C’s readPacket
:
static int readPacket(MQTTClient* c, Timer* timer)
{
MQTTHeader header = {0};
int len = 0;
int rem_len = 0;
/* 1. read the header byte. This has the packet type in it */
int rc = c->ipstack->mqttread(c->ipstack, c->readbuf, 1, TimerLeftMS(timer));
if (rc != 1)
goto exit;
len = 1;
/* 2. read the remaining length. This is variable in itself */
decodePacket(c, &rem_len, TimerLeftMS(timer));
len += MQTTPacket_encode(c->readbuf + 1, rem_len); /* put the original remaining length back into the buffer */
if (rem_len > (c->readbuf_size - len)) [5]
{
rc = BUFFER_OVERFLOW;
goto exit;
}
/* 3. read the rest of the buffer using a callback to supply the rest of the data */
if (rem_len > 0 && (rc = c->ipstack->mqttread(c->ipstack, c->readbuf + len, rem_len, TimerLeftMS(timer)) != rem_len)) {
rc = 0;
goto exit;
}
header.byte = c->readbuf[0];
rc = header.bits.type;
exit:
return rc;
}
A size check was introduced at [5]
to avoid the buffer overflow.
The nature of the buffer overflow depends upon the library user. Indeed, the buffer used in readPacket
is provided by the library utilizer and not allocated by the Paho MQTTClient-C library.
2021-10-27 - Initial vendor contact
2021-11-03 - Vendor disclosure
2022-02-01 - Public Release
Discovered by Francesco Benvenuto and Matt Wiseman of Cisco Talos.