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.
Anker Roav A1 Dashcam RoavA1_SW_V1.9
7.5 - CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
CWE-252: Unchecked Return Value
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_
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
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, ???, ???);
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
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 //  ROM:80224130 jal fixup_path # (srcpath,dstPath) ROM:80224134 move $a1, $s3 //  ROM:80224138 move $a0, $s3 //  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 //  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 , the output of which is thrown into the stack variable
var_AC . 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  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.
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
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
Discovered by Lilith [<_<] of Cisco Talos.