Talos Vulnerability Report

TALOS-2018-0698

Novatek NT9665X XML_GetScreen Strncmp denial-of-service vulnerability

May 13, 2019
CVE Number

CVE-2018-4026

Summary

An exploitable denial-of-service vulnerability exists in the XML_GetScreen Wi-Fi command of the NT9665X Chipset firmware, running on the Anker Roav A1 Dashcam, version “RoavA1_SW_V1.9.” A specially crafted set of packets can cause an invalid memory dereference, resulting in a device reboot.

Tested Versions

Anker Roav A1 Dashcam RoavA1_SW_V1.9

Product URLs

https://goroav.com/products/roav-dash-cam-a1

CVSSv3 Score

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

CWE

CWE-252: Unchecked Return Value

Details

The Novatek NT9665X SOC is a chipset used in an large number of consumer camera devices, particularly in dashboard cameras. The chip provides default firmware that is a fork of the Embedded Configurable Operating System (eCOS) project, which is found within the Roav A1 Dashcam,the product we are focusing on in this advisory.

The Roav A1 Dashcam by Anker is a dashboard camera that allows users to connect using the Roav app for Android and iOS so that they can control the camera remotely. In order to do this, users must first enable the “Wi-Fi AP” setting manually on the dashcam, and then connect to the “Roav_A1_” SSID, with the default password of “goroavcam”.

From here, the app interacts mainly with the dashboard camera via an eCOS webserver running on port 80 that requires no authentication. The standard HTTP POST, GET, and DELETE requests can be used to upload, download or delete videos and pictures from the dashcam, but there’s also a separate interface used for configuration. When requesting any url, a set of commands is accessed by providing the following http query string: ?custom=1&cmd=<0000-9999>. It should be noted that only a firmware-specific subset of commands are implemented on any given device, the list of which can be found by accessing http://192.168.1.254/?custom=1&cmd=3012.

For the following vulnerability, the XML_GetScreen command (4001) will be discussed. Like all the other WiFi commands, the prototype for this function is:

XML_GetScreen(char *URLPath,
char *QueryStr,
char *POSTData,
size_t PostDataLen,
???,
???);

The XML_GetScreen command is used to get a screenshot from a given file. For the default “.mov” files, it will read in the videos to produce this, and for any other type of file the program will search for and parse out a screenshot from the EXIF data, if any.

To start, the function first searches to see if there’s an available buffer to read the data into, and if not, the default XML response is sent and the function returns. This buffer address is gotten via a small stub that calls a function pointer that’s been dubbed get_movie_temp_buff as it heuristically has led to the Movie_GetTempBuffer function.

The Wi-Fi command 3001 must first be called to make sure that the camera has an available “temp” movie buffer allocated, which can be done with a cURL command or just browsing to 192.168.1.254/?custom=1&cmd=3001. The rest of the code flow becomes available after the buffer is created, starting with the following:

ROM:8022411C   la      $a1, aImageJpeg  # "image/jpeg"
ROM:80224120   move    $a0, $s5
ROM:80224124   jal     strcpy
ROM:80224128   addiu   $s3, $fp, 0x158+var_AC
ROM:8022412C   move    $a0, $s0     // [0]    
ROM:80224130   jal     fixup_path   # (srcpath,dstPath)
ROM:80224134   move    $a1, $s3	// [1]
ROM:80224138   move    $a0, $s3	// [2]
ROM:8022413C   jal     fancy_strchr
ROM:80224140   li      $a1, "."         
ROM:80224144   addiu   $s0, $v0, 1   
ROM:80224148   la      $a1, aMov_1      # "mov"
ROM:80224150   move    $a0, $s0     // [3]     
ROM:80224154   jal     strncmp
ROM:80224158   li      $a2, 3
ROM:8022415C   bnez    $v0, loc_80224278
ROM:80224160   lui     $a1, 0x8047   

The path of the HTTP request is moved to $a0 of the fixup_path function at [0], the output of which is thrown into the stack variable var_AC [1]. This fixed path is then passed to a function that resembles a strchr call, looking for the ‘.’ character, after which it looks for ‘mov.’

The issue here lies solely at [3] though, as there’s no check on the return value of this strchr call. In the event that the path of the HTTP request does not contain a ‘.’, if it only contains a ‘.’, or if it only contains ‘..’, the strchr will return NULL, which implies that $s0 will contain 0x1, causing a bad memory read within the call to strncmp(0x1,”mov”,3), resulting in a device crash.

Crash Output

WifiCmd_Lock(): Lock
WifiCmd_DispatchCmd(): cmd:4002 evt:0 par:-2134199650 CB:802240c0 wait:0
WifiCmd_DispatchCmd(): ret 0
*** CPU Exception!!! cause 0x02: TLB exception (load or instruction fetch)
epc  - 0x800af980
$ra  - 0x8022415c
$sp  - 0x80caaca0
$fp  - 0x80caaca0
general registers:
     $zero : 0x80cbf620       $at : 0x80caac40       $v0 : 0x00000001       $v1 : 0x00000000
       $a0 : 0x00000001       $a1 : 0x804698f4       $a2 : 0x00000003       $a3 : 0x01010101
       $t0 : 0x7fffc5be       $t1 : 0x01010101       $t2 : 0x80ca57d4       $t3 : 0x00000012
       $t4 : 0x00000008       $t5 : 0x807d2bb4       $t6 : 0x00000015       $t7 : 0x80aed924
       $s0 : 0x00000001       $s1 : 0x80caaeb0       $s2 : 0x80cab8e8       $s3 : 0x80caad4c
       $s4 : 0x00010400       $s5 : 0x80caaee4       $s6 : 0x80cab8e8       $s7 : 0x00000000
       $t8 : 0x807d2bb4       $t9 : 0x00002000      null : 0x00000008      null : 0x80caac88
        gp : 0x8060f540        sp : 0x80caaca0        fp : 0x80caaca0        ra : 0x8022415c
co-processor registers:
   entrylo : 0x00000002    status : 0x00000008    vector : 0x0100c403       epc : 0x800af980
     cause : 0x00000000  badvaddr : 0x00800008    hwrena : 0x00000400      prid : 0x00019655
   entrylo : 0x01215792
Thread(id) :

  Hfs Session(260)
stack      :
    range(0x80ca57b4 - 0x80cab7b4)
call stack :
  0 frame(0x80caaca0 - 0x80caacb8) ............................ $pc : 0x800af980
     + 0x80caaca0 : 0xfffffffe 0x00000001 0x00000000 0x000001ff
     + 0x80caacb0 : 0xdeadbeef 0xdeadbeef
  end
*** CPU Exception in Task[]! cause=0x00000002, addr=0x800af980

Timeline

2018-10-29 - Talos contacts vendor
2018-11-02 - Report disclosed to vendor
2018-12-04 - 30 day follow up
2019-01-18 - 60 day follow up - Talos reaches out to TWNCERT for assistance reaching vendor (Novatek)>br> 2019-01-22 - TWNCERT contacted Novatek and advised Novatek will check emails for reports
2019-03-06 - 90+ day follow up - Talos asks TWNCERT for direct point of contact for Novatek
2019-03-27 - Talos sends follow up to TWNCERT
2019-04-02 - Talos sends copies of email correspondence and reports to TWNCERT
2019-04-18 - Suggested pubic disclosure date of 2019-05-13 (171 days after initial disclosure)
2019-04-19 - Vendor fixed issue and provided patch to their IDH
2019-05-13 - Public disclosure

Credit

Discovered by Lilith [<_<] of Cisco Talos.