Headline
CVE-2019-5040: TALOS-2019-0803 || Cisco Talos Intelligence Group
An exploitable information disclosure vulnerability exists in the Weave MessageLayer parsing of Openweave-core version 4.0.2 and Nest Cam IQ Indoor version 4620002. A specially crafted weave packet can cause an integer overflow to occur, resulting in PacketBuffer data reuse. An attacker can send a packet to trigger this vulnerability.
Summary
An exploitable information disclosure vulnerability exists in the Weave MessageLayer parsing of Openweave-core version 4.0.2 and Nest Cam IQ Indoor version 4620002. A specially crafted weave packet can cause an integer overflow to occur, resulting in PacketBuffer data reuse. An attacker can send a packet to trigger this vulnerability.
Tested Versions
Nest Labs Openweave-core 4.0.2 Nest Labs Nest Cam IQ Indoor version 4620002
Product URLs
https://openweave.io/ https://store.nest.com/product/nest-cam-iq/NC3200US
CVSSv3 Score
8.2 - CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:L
CWE
CWE-190: Integer Overflow Or Wraparound
Details
Openweave is the opensource implementation of the Weave protocol, which was initially developed by Nest Labs in order to provide a Session-Layer agnostic method of communication between their IoT devices. This protocol is used by Nest devices such as the Nest Cam IQ Indoor, one of Nest Labs’ most expensive and advanced devices, integrating Google Assistant, facial recognition and even the capability to act as an 6lowpan hub for other less powerful IoT devices. The issue described in this advisory is thus also present in the Nest Cam IQ Indoor.
Before going into the details of the bug itself, a quick overview of the Weave protocol and terminology is needed, so for brevity, here’s a packet dissection of a sample weave packet:
--------TCP Message Layer----------- //[1]
Mesage_Length : 0x0146
Message Version : 0x1
Message_Id : 0x00000004
Message_Flags : 0x0300 : [ Dstnode|Srcnode ]
Src_Node : 0000000000000002
Dst_Node : 0000000000000001
Encryption : None
--------Exchange Layer----------- //[2]
Exchange Ver|Flag : 0x11 : [ Initiator ]
Exchange MsgType : 0x1 : kMsgType_PASEInitiatorStep1 //[3]
Exchange ExchangeID : 0x70e3
Exchange ProfileID : 0x00000004 : kWeaveProfile_Security //[4]
--------Data Layer----------- //[5]
kMsgType_PASEInitiatorStep1
controlHeader: 0x8011213f
sizeHeader: 0x01070e0e
ProtocolConfig: 0x235a0004
AltConfig[0]: 0x235a0001
gx: 0xe, zkpxgr: 0xe, zkpxb: 0x7
[...]
Weave messages consist of three layers: Message, Exchange, and Data. The Message Layer [1] and Exchange Layer [2] are both variable sized and consist of little-endian fields as listed above. The Data Layer [5] is strictly dependent on the MessageType [3] and ProfileId [4], and every combination thereof generally has a unique message structure, which can be seen from all the parsed fields below [5]. The ProfileID is fittingly used to determine which Weave “Profile” to talk with, and likewise, the MessageType is essentially the opcode for the given Profile.
When examining the Message Layer parsing of a given packet buffer, we must fittingly examine /src/lib/core/WeaveMessageLayer.cpp, and more specifically the WeaveMessageLayer::DecodeMessageWithLength function. It should be noted that UDP packets will not hit this code path and will instead be directed straight to WeaveMessageLayer::DecodeMessage, as a UDP packet’s length is determined by the return value of recvfrom. Moving on, let us examine the relevant code:
WEAVE_ERROR WeaveMessageLayer::DecodeMessageWithLength(PacketBuffer *msgBuf, uint64_t sourceNodeId, WeaveConnection *con, WeaveMessageInfo *msgInfo, uint8_t **rPayload, uint16_t *rPayloadLen, uint16_t *rFrameLen) {
uint8_t *dataStart = msgBuf->Start();
uint16_t dataLen = msgBuf->DataLength();
// Error if the message buffer doesn't contain the entire message length field.
if (dataLen < 2) {
*rFrameLen = 8; // Assume absolute minimum frame length.
return WEAVE_ERROR_MESSAGE_INCOMPLETE;
}
// Read the message length.
uint16_t msgLen = LittleEndian::Get16(dataStart); //[6]
// The frame length is the length of the message plus the length of the length field.
*rFrameLen = msgLen + 2; //[7]
// Error if the message buffer doesn't contain the entire message, or is too
// long to ever fit in the buffer.
if (dataLen < *rFrameLen){ //[8]
if (*rFrameLen > msgBuf->MaxDataLength() + msgBuf->ReservedSize())
return WEAVE_ERROR_MESSAGE_TOO_LONG;
return WEAVE_ERROR_MESSAGE_INCOMPLETE;
}
// Adjust the message buffer to point at the message, not including the message length field that precedes it,
// and not including any data that may follow it.
msgBuf->SetStart(dataStart + 2);
msgBuf->SetDataLength(msgLen); //[9]
// Decode the message.
WEAVE_ERROR err = DecodeMessage(msgBuf, sourceNodeId, con, msgInfo, rPayload, rPayloadLen);
At [6] we read in the bytes from the packet that let Weave know how long it is. Due the possibility of TCP reassembly, the msgLen that we give might not be the length of our actual buffer (dataLen). Moving on, we add 2 to our given msgLen at [7] and store it in the rFrameLen variable, which from the prototype, one can see is a uint16_t, just like our msgLen. As one might guess, we can overflow this variable by setting our msgLen to 0xFFFF or 0xFFFE, which allows us to bypass the check at [8], causing us to hit [9] with a msgLen that is much much greater than the intended maximum size of a PacketBuffer at ~0x62F. Weave will actually see that the msgLen is greater than 0x62F at the msgBuf->SetDataLength(msgLen) line, and will knock the message length down to 0x62F regardless. More accurately, it will cause our PacketBuffer’s length to be set to the maximum size left. Since there can be more than one message in a PacketBuffer at a time, if there’s 0x300 bytes left, SetDataLength(0xFFFF) will result in the PacketBuffer’s length now being 0x300.
This vulnerability allows us to send a short packet and have whatever data was already there to be included into our packet, as the allocation and freeing of a PacketBuffer do not clear the data inside, which provides an attacker something to work with. The quickest and most useful packet to send is the kMsgType_EchoRequest, as one can read out the last sent packet by any other party over any other connection (as TCP, UDP, Bluetooth and 6lowpan all share the same PacketBufferPool). Even more interesting is that, when dealing with encrypted communications (e.g. a PASE or CASE session), encrypted messages are actually encrypted and decrypted in the PacketBuffer itself, which would allow us to read data sent encrypted over the wire to a device (such as Fabric Configurations, Network Configurations, or other sensitive information).
There might be more uses for this vulnerability, so the following will be a description of the constraints thereof. One can replay as much data as one wants from a previous packet, but it should be noted that this cannot be done with packets that are encrypted. However this might be useful, as one could potentially end encrypted sessions to a third party by replaying their previous encrypted packet and changing the Message_ID field.
Timeline
2019-04-18 - Vendor Disclosure
2019-05-20 - Vendor completed analysis
2019-06-18 - Follow up with vendor
2019-07-02 - 90 day notice; Vendor advised updates scheduled for release mid-July
2019-07-18 - Vendor advised fix will release end of July and be tested in the field
2019-07-26 - Extended disclosure date to 2019-08-15
2019-08-19 - Public Release
Discovered by Lilith Wyatt and Claudio Bozzato of Cisco Talos.