Talos Vulnerability Report

TALOS-2018-0557

Samsung SmartThings Hub video-core Database find-by-cameraId Code Execution Vulnerability

July 26, 2018
CVE Number

CVE-2018-3880

Summary

An exploitable stack-based buffer overflow vulnerability exists in the database "find-by-cameraId" functionality of video-core's HTTP server of Samsung SmartThings Hub. The video-core process incorrectly handles existing records inside its SQLite database, leading to a buffer overflow on the stack. An attacker can send an HTTP request to trigger this vulnerability.

Tested Versions

Samsung SmartThings Hub STH-ETH-250 - Firmware version 0.20.17

Product URLs

https://www.smartthings.com/products/smartthings-hub

CVSSv3 Score

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

CWE

CWE-120: Buffer Copy without Checking Size of Input ('Classic Buffer Overflow')

Details

Samsung produces a series of devices aimed at controlling and monitoring a home, such as wall switches, LED bulbs, thermostats and cameras. One of those is the Samsung SmartThings Hub, a central controller which allows an end user to use their smartphone to connect to their house remotely and operate other devices through it. The hub board utilizes several systems on chips. The firmware in question is executed by an i.MX 6 SoloLite processor (Cortex-A9), which has an ARMv7-A architecture.

The firmware is Linux-based, and runs a series of daemons that interface with devices nearby via ethernet, ZigBee, Z-Wave and Bluetooth protocols. Additionally, the hubCore process is responsible for communicating with the remote SmartThings servers via a persistent TLS connection. These servers act as a bridge that allows for secure communication between the smartphone application and the hub. End users can simply install the SmartThings mobile application on their smartphone to control the hub remotely.

One of the features of the hub is that it connects to smart cameras, configures them and looks at their livestreams. For testing, we set up the Samsung SmartCam SNH-V6414BN on the hub. Once done, the livestream can be displayed by the smartphone application by connecting either to the remote SmartThings servers, or directly to the camera, if they're both in the same subnetwork.

Inside the hub, the livestream is handled by the video-core process, which uses ffmpeg to connect via RTSP to the smart camera in its same local network, and at the same time, provides a streamable link for the smartphone application.

The remote SmartThings servers have the possibility to communicate with the video-core process by sending messages in the persistent TLS connection, established by the hubCore process. These messages can encapsulate an HTTP request, which hubCore would relay directly to the HTTP server exposed by video-core. The HTTP server listens on port 3000, bound to the localhost address, so a local connection is needed to perform this request.

We identified a vulnerable request that can be exploited to achieve code execution on the video-core process, which is running as root. By sending an HTTP DELETE request for the /cameras/<camera-id> path it's possible to delete an existing smart camera which has the specified "camera-id".

This request is handled by function sub_4A6C8:

.text:0004A6C8     sub_4A6C8
.text:0004A6C8
.text:0004A6C8     dest   = -0x2CDC
.text:0004A6C8     var_2CC0= -0x2CC0
.text:0004A6C8     var_2AC0= -0x2AC0
.text:0004A6C8     var_2040= -0x2040
.text:0004A6C8     var_2000= -0x2000
.text:0004A6C8     arg_0  =  4
.text:0004A6C8     arg_4  =  8
.text:0004A6C8
.text:0004A6C8 000        STMFD           SP!, {R4-R6,R11,LR}
.text:0004A6CC 014        ADD             R11, SP, #0x10
...
.text:0004A768 2CE8       SUB             R2, R11, #-var_2040
.text:0004A76C 2CE8       MOV             R0, R6
.text:0004A770 2CE8       MOV             R1, R4
.text:0004A774 2CE8       SUB             R2, R2, #0x18        ; [1]
.text:0004A778 2CE8       BL              db_camera_by_id      ; [2]

Soon after initialization, the function db_camera_by_id is called [2], passing as parameters the "camera-id" specified in the request, the "camera-id" string length, and a buffer allocated on the stack [1] where all the information about the camera is expected to be stored.

.text:0001458C     db_camera_by_id
.text:0001458C
...
.text:000145A4 000        STMFD           SP!, {R4-R11,LR}
.text:000145A8 024        ADD             R11, SP, #0x24+var_4
.text:000145AC 024        MOV             R5, R1
.text:000145B0 024        SUB             SP, SP, #0x14
.text:000145B4 038        MOV             R4, R2
.text:000145B8 038        MOV             R1, #0
.text:000145BC 038        STR             R0, [R11,#-0x34]
.text:000145C0 038        MOV             R0, R2
.text:000145C4 038        MOV             R2, #0x2044
.text:000145C8 038        STR             R1, [R11,#-0x30]
.text:000145CC 038        ADD             R8, R4, #0x2040
.text:000145D0 038        BL              memset
.text:000145D4 038        ADD             R2, R5, #8
.text:000145D8 038        LDR             R3, [R11,#-0x34]
.text:000145DC 038        LDR             R12, [R11,#-0x30]
.text:000145E0 038        BIC             R2, R2, #7
.text:000145E4 038        SUB             SP, SP, R2
.text:000145E8 038        MOV             R2, R5
.text:000145EC 038        MOV             R1, R3
.text:000145F0 038        MOV             R0, SP
.text:000145F4 038        LDR             R6, =columnArr    ; [3]
.text:000145F8 038        MOV             R7, R12
.text:000145FC 038        MOV             R10, SP
.text:00014600 038        MOV             R9, R12
.text:00014604 038        ADD             R8, R8, #8
.text:00014608 038        ADD             R4, R4, #8
.text:0001460C 038        STRB            R12, [SP,R5]
.text:00014610 038        BL              memcpy            ; [4]
.text:00014614
.text:00014614     loc_14614                                ; [5] loop
.text:00014614 038        MOV             R2, R6            ; [7]
.text:00014618 038        MOV             R0, #0
.text:0001461C 038        MOV             R1, R10
.text:00014620 038        SUB             R3, R11, #0x28    ; [8]
.text:00014624 038        STR             R9, [R11,#-0x28]
.text:00014628 038        ADD             R6, R6, #0x84
.text:0001462C 038        BL              db_find           ; [6]
.text:00014630 038        CMN             R0, #6
.text:00014634 038        BEQ             loc_14674
.text:00014638 038        CMP             R0, #0
.text:0001463C 038        BLT             loc_14680
.text:00014640 038        LDR             R5, [R11,#-0x28]
.text:00014644 038        ADD             R7, R7, #1
.text:00014648 038        MOV             R0, R5
.text:0001464C 038        BL              strlen
.text:00014650 038        STR             R0, [R4,#-4]
.text:00014654 038        MOV             R0, R5            ; [8]
.text:00014658 038        BL              strlen            ; [9]
.text:0001465C 038        MOV             R1, R5
.text:00014660 038        MOV             R2, R0
.text:00014664 038        MOV             R0, R4
.text:00014668 038        BL              memcpy            ; [10]
.text:0001466C 038        MOV             R0, R5
.text:00014670 038        BL              free
.text:00014674
.text:00014674     loc_14674
.text:00014674 038        ADD             R4, R4, #0x204
.text:00014678 038        CMP             R4, R8
.text:0001467C 038        BNE             loc_14614         ; [5] loop
.text:00014680
.text:00014680     loc_14680
.text:00014680 038        CMP             R7, #0
.text:00014684 038        BEQ             loc_146A4
.text:00014688 038        CMP             R7, #0xF
.text:0001468C 038        MOVGT           R0, #0
.text:00014690 038        MOVLE           R0, #1
.text:00014694
.text:00014694     loc_14694
.text:00014694 038        SUB             SP, R11, #0x20
.text:00014698 024        LDMFD           SP!, {R4-R11,PC}

The function searches for the camera with the given id in the database and saves each column in the buffer passed as third argument. At [4] the first argument of the function ("camera-id") is copied on the stack and is used by the db_find function [6] as a filter on the database. We can see that the function loops [5], and for each column of the table [7], pointed by the array at [3], a query is performed. Depending on [7], db_find will execute the following query:

SELECT <third-argument> FROM camera WHERE cameraId='666f8370-05b7-424d-a5e2-28a2dc8477de'

The pointer to the string resulting from the query will be saved in the fourth argument [8]. Finally, if the query is successful, the result is saved using memcpy [10] on the caller's buffer [2]. Moreover, the length parameter of the memcpy is set from the strlen [9] output of the source string itself. At high level this would be:

memcpy(stack_buffer, query_result, strlen(query_result));

Since the query_result is directly fetched from the database, there is no restriction on the length of the copy operation, which allows for overflowing the stack buffer of the parent function and execute arbitrary code.

To get this data into the database, the vulnerability described in TALOS-2018-0556 could be used. Note that while we scored this vulnerability CVSS 8.2 on its own, it would constitute a CVSS 9.9 (CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H) when combined with TALOS-2018-0556. This is demonstrated in the proof of concept below.

Exploit Proof of Concept

The following proof of concept shows how to crash the video-core process:

1- Add a new camera with id $sCameraId in the "camera table" of the video-core database, using an overlong value in any other column. This is possible, for example, using using TALOS-2018-0556.

2- Send the DELETE request, using curl from inside the hub, but the same request could be sent using a SmartApp.
$ curl -X DELETE "http://127.0.0.1:3000/cameras/${sCameraId}"

Timeline

2018-04-09 - Vendor Disclosure
2018-05-23 - Discussion with vendor/review of timeline for disclosure
2018-07-17 - Vendor patched
2018-07-26 - Public Release

Credit

Discovered by Claudio Bozzato of Cisco Talos.