CVE-2019-5036
An exploitable denial-of-service vulnerability exists in the Weave error reporting functionality of the Nest Cam IQ Indoor, version 4620002. A specially crafted weave packets can cause an arbitrary Weave Exchange Session to close, resulting in a denial of service. An attacker can send a specially crafted packet to trigger this vulnerability.
Nest Labs Nest Cam IQ Indoor version 4620002
https://store.nest.com/product/nest-cam-iq/NC3200US
7.5 - CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
CWE-284: Improper Access Control
The Nest Cam IQ Indoor is one of Nest Labs’ most expensive and advanced devices. The surveillance camera integrates with Google Assistant, contains facial recognition technology and even has the ability to act as an 6lowpan hub for other less powerful internet-of-things devices. The main protocol that is used for setup and initial communications of Nest devices is Weave, a protocol designed strictly for IoT devices, which can run over TCP, UDP, Bluetooth and 6lowpan.
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.
For the purposes of this advisory, let’s consider the MessageType kMsgType_KeyError
. When performing any sort of authentication to a Weave device, the profileID used is fittingly kWeaveProfile_Security
. All weave messages of this type get passed to the WeaveSecurityManager, which is the sole authentication authority. Interestingly, when looking at messages destined for itself, the first thing WeaveSecurityManager does is checking if it’s received a kMsgType_KeyError
message:
void WeaveSecurityManager::HandleUnsolicitedMessage(ExchangeContext *ec, const IPPacketInfo *pktInfo, const WeaveMessageInfo *msgInfo,
uint32_t profileId, uint8_t msgType, PacketBuffer* msgBuf) {
WEAVE_ERROR err = WEAVE_NO_ERROR;
WeaveSecurityManager *secMgr = (WeaveSecurityManager *)ec->AppState;
// Handle Key Error Messages.
if (profileId == kWeaveProfile_Security && msgType == kMsgType_KeyError)
{
secMgr->HandleKeyErrorMsg(ec, msgBuf); //[6]
msgBuf = NULL;
ec = NULL;
ExitNow();
}
[...]
}
Normally, the kMsgType_KeyError
is sent by a Weave server when a client has failed authentication, at which point, the client will close the connection. Examining the actual KeyErrorMsg handler at [6], we see:
void WeaveSecurityManager::HandleKeyErrorMsg(ExchangeContext *ec, PacketBuffer* msgBuf)
{
WEAVE_ERROR err;
WeaveSessionKey *sessionKey;
uint8_t *p = msgBuf->Start();
uint64_t srcNodeId = ec->PeerNodeId;
uint16_t keyId;
uint8_t encType;
uint16_t keyErrCode;
uint32_t messageId;
WEAVE_ERROR keyErr;
uint64_t endNodeIds[WEAVE_CONFIG_MAX_END_NODES_PER_SHARED_SESSION + 1]; // 10 + 1
uint8_t endNodeIdsCount = 0;
// Verify correct message size.
if (msgBuf->DataLength() != kWeaveKeyErrorMessageSize) // 0x9
ExitNow();
// Read the message fields.
keyId = LittleEndian::Read16(p); //[7]
encType = Read8(p);
messageId = LittleEndian::Read32(p);
keyErrCode = LittleEndian::Read16(p);
//[…]
// If the failed key is a session key...
if (WeaveKeyId::IsSessionKey(keyId)) // ( Key & 0x0FFFF000 == 0x2000)
// Attempt to find the referenced session key. If found...
err = FabricState->FindSessionKey(keyId, srcNodeId, false, sessionKey); //[8]
First, the DataLayer of our packet must be 0x9 bytes long. Second, our packet lets the server know which keyID has failed via the first two bytes ([7]). Third, using the keyId and the srcNodeId that sent the packet, the server tries to find the session key we are referring to at [8]. It’s worth noting that the srcNodeID is the eight-byte little-endian field in the MessageLayer, which typically corresponds to the 802.15.14 mac address for Weave devices. Regardless, we have to know both the keyId and the srcNode that we want to deny service to (these fields can also be brute forced).
Next, lets look at what happens if there is a SessionKey found for the given (keyId, srcNodeId)
pair:
if (err == WEAVE_NO_ERROR) {
if (sessionKey->IsSharedSession()) {
FabricState->GetSharedSessionEndNodeIds(sessionKey, endNodeIds, sizeof(endNodeIds) / sizeof(uint64_t), endNodeIdsCount);
}
// Add the terminating node to the list of peer nodes associated with the key.
endNodeIds[endNodeIdsCount++] = sessionKey->NodeId;
// Discard the failed session key.
FabricState->RemoveSessionKey(keyId, srcNodeId); //[9]
}
} else {
endNodeIds[endNodeIdsCount++] = srcNodeId; //endNodeIds => function array
}
// For each peer node associated with the key, notify the exchange manager that the key has failed
// with respect to that peer.
for (int i = 0; i < endNodeIdsCount; i++)
ExchangeManager->NotifyKeyFailed(endNodeIds[i], keyId, keyErr); //[10]
Of note in the above code is that the SecurityManager will first remove the SessionKey from memory [9], and then subsequently notify the ExchangeManager that the exchange has failed [10], which causes the weave device to perform all the teardowns and cleanups necessary for that given peer.
The main reason why this vulnerability is relevant is that it’s essentially a Weave Deauth-Frame. The function call to FabricState->FindSessionKey(keyId, srcNodeId, false, sessionKey);
does not take into account the source connection of our KeyErrorMsg, which means that we can send packets over Bluetooth to kill a connection that’s occurring over TCP, or send UDP packets to repeatedly kill a 802.15.14 device’s weave connection (rendering the device useless).
It’s worth noting again that the KeyId that is being used must be known, along with the SrcNode that one wishes to disconnect. The key space is relatively small however, the KeyId must be between 0x2000 and 0x3000, and also a Weave Device’s NodeID will generally always start with 18:b4:30:00:00. If an attacker can sniff any packets from a node, it will have the Node’s ID, and it can also be found via a kMsgType_Identify
request sent to the device or as a broadcast.
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.