### Summary
An exploitable buffer overflow vulnerability exists in the remote
video-host communication of `video-core`'s HTTP server of Samsung
SmartThings Hub. The `video-core` process insecurely parses the AWSELB
cookie while communicating with remote video-host servers, leading to a
buffer overflow on the heap. An attacker able to impersonate the remote
HTTP servers could 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](https://www.smartthings.com/products/smartthings-hub)
### CVSSv3 Score
8.5 - CVSS:3.0/AV:N/AC:H/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 PUT request for the
`/cameras/<camera-id>/streams/<stream-type>` path, it's possible to make
the hub to start streaming the camera's video (specified by the
"camera-id") towards the remote servers. The "stream-type" can have
multiple values and represent the kind of stream requested, for example
it could be set to "hls1080p". The following is an example of such
request:
$ curl "http://127.0.0.1:3000/cameras/<camera-id>/streams/hls1080p" -d '{"streamClass":"hls2segs","oauthToken":"Bearer <oauth-token>"}'
Note that the "oauth-token" is an OAuth 2.0 Bearer Token (canonical
UUID), provided by auth-global.api.smartthings.com. Such a request is
handled by function `sub_4B078`, which in turn calls `sub_4CDC4` for
setting up the communication with a remote video-host server and
providing the remote streaming feature:
.text:0004CDC4 sub_4CDC4
.text:0004CDC4
.text:0004CDC4 var_228= -0x228
.text:0004CDC4 var_224= -0x224
.text:0004CDC4 var_220= -0x220
.text:0004CDC4 var_21C= -0x21C
.text:0004CDC4 var_218= -0x218
.text:0004CDC4 var_214= -0x214
.text:0004CDC4 var_210= -0x210
.text:0004CDC4 ptr = -0x20C
.text:0004CDC4 s = -0x208
.text:0004CDC4
.text:0004CDC4 000 STMFD SP!, {R4-R11,LR}
.text:0004CDC8 024 MOV R3, #0
.text:0004CDCC 024 SUB SP, SP, #0x204
.text:0004CDD0 228 MOV R7, R1
.text:0004CDD4 228 MOV R4, R0
.text:0004CDD8 228 STR R2, [SP,#0x228+var_220]
.text:0004CDDC 228 STR R3, [SP,#0x228+var_210]
.text:0004CDE0 228 STR R3, [SP,#0x228+ptr]
.text:0004CDE4 228 BL http_resp_packer__buffer_init
.text:0004CDE8 228 MOV R1, #:lower16:aStreamclassHls ; "{\"streamClass\": \"hls2segs\"}"
.text:0004CDEC 228 MOV R9, R0
.text:0004CDF0 228 MOVT R1, #:upper16:aStreamclassHls ; "{\"streamClass\": \"hls2segs\"}"
.text:0004CDF4 228 BL http_build_answer ; [1]
...
.text:0004CE98 228 BL http_stream_curl_request ; [2]
...
.text:0004CEDC 228 ADD R1, R7, #0x1200
.text:0004CEE0 228 STR R3, [R4,#0x8F4]
.text:0004CEE4 228 ADD R2, R7, #0xC
.text:0004CEE8 228 ADD R1, R1, #0x10
.text:0004CEEC 228 LDR R0, [SP,#0x228+var_218]
.text:0004CEF0 228 BL sub_4C570 ; [3]
At [1], the function starts to build an answer for the request that is
being handled. The answer is composed by a JSON string, which will be
filled by the answer from the request at [2]. The request [2] is a
remote request toward a video-host server: the server's address is
stored in `video-core`'s database, and in the hub that we tested this
address is "https://vh-na04-useast2.connect.smartthings.com:8300".
The communication generated at [2] is the following:
- video-core --> https://vh-na04-useast2.connect.smartthings.com:8300
PUT /cameras/<camera-id>/streams/hls1080p HTTP/1.1
Host: vh-na04-useast2.connect.smartthings.com:8300
Accept: */*
Content-Type: application/json
Authorization: Bearer <oauth-token>
Content-Length: 27
Connection: close
{"streamClass": "hls2segs"}
- https://vh-na04-useast2.connect.smartthings.com:8300 --> video-core
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, OPTIONS, DELETE
Access-Control-Allow-Origin: *
Cache-control: no-cache="set-cookie"
Content-Type: text/html; charset=utf-8
ETag: W/"5T7hZ36v7ulr9v0HTY4E2g=="
Server: nginx/1.12.0
Set-Cookie: AWSELB=4D83E80E23DA7677A73134EC01A12610C0D45FFDF9389B9D15CD82A43087013F829D09859E3CA0909290FDFFE8AEEB654FC7CEE34F9A088ABB9A8D69753700706BCB37AE10;PATH=/;MAX-AGE=1800
X-Powered-By: Express
Content-Length: 1288
Connection: Close
<json-with-streaming-information>
The HTTP body in the response contains a series of parameters that allow
for pushing the camera stream remotely. Moreover, we can see that a
cookie with name "AWSELB" is set. At [3], function `sub_4C570` parses
the cookies, extracting the value of "AWSELB" if present.
.text:0004C570 sub_4C570
.text:0004C570
.text:0004C570 var_210= -0x210
.text:0004C570 var_20C= -0x20C
.text:0004C570 var_208= -0x208
.text:0004C570 save_ptr= -0x204
.text:0004C570 s = -0x200
.text:0004C570
.text:0004C570 000 STMFD SP!, {R4-R9,LR}
.text:0004C574 01C MOV R8, #debug_log
.text:0004C57C 01C MOV R9, R2
.text:0004C580 01C MOV R2, #0
.text:0004C584 01C SUB SP, SP, #0x1F4
.text:0004C588 210 LDR R3, [R8]
.text:0004C58C 210 MOV R4, R0
.text:0004C590 210 MOV R6, R1
.text:0004C594 210 STR R2, [R9]
.text:0004C598 210 CMP R3, #2
.text:0004C59C 210 BHI loc_4C6CC
.text:0004C5A0
.text:0004C5A0 loc_4C5A0
.text:0004C5A0 210 MOV R1, #:lower16:0x40001C
.text:0004C5A4 210 MOV R0, R4
.text:0004C5A8 210 MOVT R1, #:upper16:0x40001C ; CURLINFO_COOKIELIST
.text:0004C5AC 210 ADD R2, SP, #0x210+var_208
.text:0004C5B0 210 BL curl_easy_getinfo ; [4]
.text:0004C5B4 210 CMP R0, #0
.text:0004C5B8 210 BNE loc_4C71C
.text:0004C5BC 210 LDR R7, [SP,#0x210+var_208]
.text:0004C5C0 210 CMP R7, #0
.text:0004C5C4 210 BEQ loc_4C6A4
.text:0004C5C8
.text:0004C5C8 loc_4C5C8
.text:0004C5C8 210 MOV R1, #:lower16:asc_C6C8C ; " \t"
.text:0004C5CC 210 LDR R0, [R7]
.text:0004C5D0 210 MOVT R1, #:upper16:asc_C6C8C ; " \t"
.text:0004C5D4 210 ADD R2, SP, #0x210+save_ptr
.text:0004C5D8 210 BL __strtok_r ; [5]
.text:0004C5DC 210 SUBS R4, R0, #0
.text:0004C5E0 210 BEQ loc_4C698
.text:0004C5E4
.text:0004C5E4 loc_4C5E4
.text:0004C5E4 210 MOV R0, R4
.text:0004C5E8 210 BL strlen
.text:0004C5EC 210 CMP R0, #6
.text:0004C5F0 210 MOV R1, R4
.text:0004C5F4 210 MOVCC R2, R0
.text:0004C5F8 210 MOV R0, #:lower16:aAwselb ; "AWSELB"
.text:0004C5FC 210 MOVCS R2, #6
.text:0004C600 210 MOVT R0, #:upper16:aAwselb ; "AWSELB"
.text:0004C604 210 BL strncmp ; [6]
.text:0004C608 210 MOV R1, #:lower16:asc_C6C8C ; " \t"
.text:0004C60C 210 MOV R5, R0
.text:0004C610 210 ADD R2, SP, #0x210+save_ptr
.text:0004C614 210 MOV R0, #0
.text:0004C618 210 MOVT R1, #:upper16:asc_C6C8C ; " \t"
.text:0004C61C 210 BL __strtok_r
.text:0004C620 210 SUBS R4, R0, #0
.text:0004C624 210 BEQ loc_4C698
.text:0004C628 210 CMP R5, #0
.text:0004C62C 210 BNE loc_4C5E4
.text:0004C630 210 MOV R1, R4
.text:0004C634 210 MOV R0, R6
.text:0004C638 210 BL strcpy ; [7]
.text:0004C63C 210 MOV R0, R6
.text:0004C640 210 BL strlen
.text:0004C644 210 LDR R3, [R8]
.text:0004C648 210 STR R0, [R9]
.text:0004C64C 210 CMP R3, #2
.text:0004C650 210 BLS loc_4C5E4
At [4] the function `curl_easy_getinfo` is called for extracting the
list of cookies, which will be stored in `var_208`. Then, by using
`strtok` [5], the function loops over all the cookies' values in the
response, and if any one of them starts with the string "AWSELB" [6],
its value is copied using `strcpy` [7] into the heap buffer passed as
parameter, which has a size of 512 bytes.
Since the cookies are controlled by the remote server and there is no
restriction on the length of the copy operation, anyone able to
impersonate a video-host server could be able to overflow the heap
buffer and execute arbitrary code.
### Timeline
* 2018-05-07 - Vendor Disclosure
* 2018-05-23 - Discussion with vendor/review of timeline for disclosure
* 2018-07-17 - Vendor patched
* 2018-07-26 - Public Release
暂无评论