Talos Vulnerability Report

TALOS-2019-0797

Nest Labs Nest Cam IQ Indoor Weave Legacy Pairing Information Disclosure Vulnerability

August 19, 2019
CVE Number

CVE-2019-5034

Summary

An exploitable information disclosure vulnerability exists in the Weave Legacy Pairing functionality of Nest Cam IQ Indoor version 4620002. A set of specially crafted weave packets can cause an out of bounds read, resulting in information disclosure. An attacker can send packets to trigger this vulnerability.

Tested Versions

Nest Labs Nest Cam IQ Indoor version 4620002

Product URLs

https://store.nest.com/product/nest-cam-iq/NC3200US

CVSSv3 Score

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

CWE

CWE-125: Out-of-bounds Read

Details

The Nest Cam IQ Indoor is 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 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
step1.p1.gx: \x26\x79\x13\xe9\x91\x07\xd8\x95\xd2\xda\xa5\xb9\xc3\x63\x69\xb3\x63\xfa\x8f\x8a\x38\x96\x44\xd3\x60\x7e\x39\xbc\x3b\xc9\xb0\x1f\x0d\x3a\x56\x0a\x9f\x45\x83\x40\x26\x56\x0a\x8c\x1b\xbf\xba\x0e\xed\x61\x7a\x98\xc1\x60\x68\x22
step1.p2.gx: \x9e\x61\x2e\xc7\x06\x33\x48\xb6\xbd\xfd\xc1\x60\x85\xed\x3f\xa1\x25\xc1\x91\x9c\x6b\xe9\x7e\xd6\x06\x5c\x66\x3b\xe3\x36\x23\x45\xaa\xea\x6e\x12\x87\x68\xa4\x43\x0d\xa5\xd3\x42\x72\x28\x04\x25\x34\x5b\x0a\x30\x41\x00\xa5\x22
step1.p2.zkpx.gr:
\x1e\x22\x9a\x94\x67\x36\x88\xc4\x29\xa3\x57\xd3\xbd\xb7\x6a\xee\xba\x17\xc8\x19\xaf\x11\x89\xa3\xc1\x7d\x08\xff\xe5\x65\xd0\x74\xc3\x26\x8d\x20\x83\xe0\x81\x9a\x69\x7e\x67\xe6\x40\xc1\xcc\x83\xe7\x55\x27\x63\x63\x0f\x71\x7d
step1.p2.zkpx.b: \x38\x07\x74\x4e\x85\x3e\x9a\x2c\x97\x31\x16\xf7\x88\x86\xd8\xa4\x81\x70\xaf\x9b\x72\xf8\x56\x5a\x67\xc4\x15\x7d
step1.p2.zkpx.gr: \xd3\x0d\x93\x9e\x3b\xe1\x2f\x33\xf2\x80\xb9\x27\x78\x74\xf4\xda\xdf\x90\xeb\x5a\xee\x77\x86\x9b\x44\x37\x13\x55\x83\x9d\x35\x01\x49\xb5\xa7\x61\x06\xa8\x9d\x47\x4b\x76\xca\xbc\xf5\x97\xc4\x83\xf2\x93\x93\x7f\xb1\xec\x16\x27
step1.p2.zkpx.b: \xeb\xe9\x8c\x3a\x99\x5a\x61\x5f\xe0\x76\x5c\xae\x06\x55\xda\x5c\x45\xca\xc5\x54\x55\xd3\x22\x74\x85\x76\xdd\x0e

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.

Turning back now to the vulnerability, if we set the ProfileID to 0x235a0010 (kWeaveVendor_NestLabs), and the message type to 0x1 (kMsgType_CamAuthDataReq), we will end up hitting the following code:

DropcamLegacyPairingServer::HandleCameraAuthDataRequest(ExchangeContext *ec, PacketBuffer *(&msgBuf)){
[...]
    // Calculate HMAC of camera secret and auth_data message
    memcpy(authDataMessage, macAddress, EUI48_LEN);
    memcpy(&authDataMessage[EUI48_LEN], noncePtr, CAMERA_NONCE_LEN); //[6]

    hmacObj.Begin(secret, CAMERA_SECRET_LEN);
    hmacObj.AddData(authDataMessage, authDataMessageLen);
    hmacObj.Finish(hmac); // secret + mackAdress + nonce. 
                          //   32   +     6      +    64
    // Re-use request buffer
    PacketBuffer::Free(msgBuf);
    msgBuf = PacketBuffer::New();
    VerifyOrExit(msgBuf != NULL, err = WEAVE_ERROR_NO_MEMORY);

    // Encode response
    writer.Init(msgBuf, msgBuf->MaxDataLength());

    err = writer.PutBytes(AnonymousTag, macAddress, EUI48_LEN);
    SuccessOrExit(err);

    err = writer.PutBytes(AnonymousTag, hmac, HMAC_BUF_LEN);
    SuccessOrExit(err);

    err = writer.Finalize();
    SuccessOrExit(err);

    // Send MAC address and pairing data HMAC to client
    err = ec->SendMessage(kWeaveProfile_DropcamLegacyPairing, kMsgType_CameraAuthDataResponse, msgBuf, 0);
    SuccessOrExit(err);
[...]

In summary, the code takes the last 0x40 bytes from our message, appends that to camera's secret (32 bytes) and mac address (6 bytes) and then calculates a hmac and sends that in its reply to the initial request. Before the len 0x40 memcpy at [6], there is no bounds checking, so there's not actually any thing stopping out of bounds bytes from being included in this hash.

To actually trigger the vulnerability though, one must also know how the camera stores network data, and how the PacketBuffer objects are implemented. One thing of note however, Weave can be configured for either LWiP based packet storage or a PacketBuffer system, the latter of which is what the Nest Cam utilizes, and will be referenced throughout this advisory.

Upon initialization of the nldaemon process (which implements weave for the Nest Cam IQ Indoor), a static packet buffer array is allocated with 0xF elements that are all 0x648 in length. The PacketBuffer structure is defined as such:

struct pbuf{
    struct pbuf* next;        // Points to the next Pbuf in the chain
    void * payload;           // Pointer to Pbuf's payload.  
    uint16_t tot_len;        // Never used.
    uint16_t len;            // Length of the current Pbuf. 
    uint16_t ref;            // Current Reference Count of Pbuf. 
#if WEAVE_SYSTEM_CONFIG_PACKETBUFFER_MAXALLOC == 0 
    uint16_t alloc_size;     // Not defined.
#endif
}
#define WEAVE_SYSTEM_PACKETBUFFER_SIZE 1700

An example of a PacketBuffer in memory:

-------------Start of Pbuf---------------
        pbuf* next(none)                   Payload ptr
0x55617bdb88:   0x0000000000000000      0x00000055617bdbc0
0x55617bdb98:   0x0000000100420042      | end of pbuf struct
0x55617bdba0:                           0x000001e323000060   <---- Weave Headers.
0x55617bdba8:   0x0000000000000003      0x0000000000000001   <---- Weave headers.
0x55617bdbc0:   0x235a00100bb00115      | ---- Start of payload.
0x55617bdbc0:                           0x363534333231010c
0x55617bdbc8:   0x3635343332313837      0x3635343332313837

In order to get the nonce memcpy to occur on valuable data, there are two steps. First, one must have the end of their kMsgType_CamAuthDataReq packet reach the end of a given packet buffer slot. Thankfully this can be done by prepending other weave messages before the kMsgType_CamAuthDataReq, the easiest one being kEchoMessageType_EchoRequest, as it is easy to create and can have variable length.

The second step required to leak useful bytes is that our kMsgType_CamAuthDataReq packet must not occupy the bottom element of the PacketBuffer array. The Camera's PacketBuffer array fills up from bottom to top, so there must be a packet occupying the bottom slot such that our OOB memcpy can occur on the headers of another PacketBuffer, giving us a pointer to valid memory and allowing us to get around memory randomization.

To fill up the bottom PacketBuffer slot, there's two reliable methods, first being repeatedly sending packets (in essentially a heap spray manner), the other being to send an incomplete TCP weave packet. If the Message_Length field of a given packet is greater than the bytes received on that connection, the Weave PacketBuffer will hold the packet indefinitely, until it is completed or the connection errors out. This cannot be done over UDP as there is no length field prepended to a UDP weave packet.

Thus to actually derive and leak the desired memory, one must trigger the OOB read such that the first 0x3F bytes are user controlled, but the last byte is from program memory. This will result in the user getting a hash to save. After this, the user should utilize a normal kMsgType_CamAuthDataReq with all 0x40 bytes controlled, but with the last byte being brute forced. One out of the 0xFF possibilities will match our original hash generated with a leaked byte, thus telling us what the byte is. The process can then be repeated up to 0x40 times to leak data, but in practice 0x10 iterations will allow one to get around memory randomization and tell us where our PacketBuffer is in memory.

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

Credit

Discovered by Lilith Wyatt and Claudio Bozzato of Cisco Talos.