CVE-2018-4024
An exploitable denial-of-service vulnerability exists in the thumbnail display functionality of the NT9665X Chipset firmware, running on the Anker Roav A1 Dashcam, version “RoavA1_SW_V1.9”. A specially crafted packet can cause a null pointer dereference, resulting in a device reboot.
Anker Roav A1 Dashcam RoavA1_SW_V1.9
https://goroav.com/products/roav-dash-cam-a1
7.5 - CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
CWE-476: NULL Pointer Dereference
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 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 with the dashcam mainly via an eCOS web server 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 here: http://192.168.1.254/?custom=1&cmd=3012
.
This specific vulnerability covers the XML_GetThumbNail
command. This particular command is not just found in the Roav line of dashcams, but seems to be a default command in the Novatek NT966XX firmware. Note, however, that while this command shows up in all the NT966XX devices, the device in question might not be vulnerable, as certain devices (e.g. The SJ6 Camera w/ NT96660 chipset) could not be tested.
When the Roav A1 asks for a given file’s thumbnail, it parses the HTTP paths for the following file extensions first: “.MOV”,”.mov”,”.MP4”,”.mp4.” In the case that the file requested ends with one of these strings, the code path handles the situation correctly, and the given crash does not occur. Also, if the HTTP path does not contain a ‘.’ character, the code will return a default response, avoiding the vulnerability.
For all other files of various extensions, if the file exists, it reaches the following code block:
ROM:80224BD4 lui $s6, 1
ROM:80224BD8 lui $a0, 1
ROM:80224BDC sw $zero, 0x1C0+toc_8($fp)
ROM:80224BE0 sw $zero, 0x1C0+var_1A0($fp)
ROM:80224BE4 jal get_movie_temp_buff //[0]
// => Movie_GetTempBuffer
ROM:80224BE8 sw $s6, 0x1C0+toc_4($fp)
ROM:80224BEC move $a1, $v0
ROM:80224BF0 addiu $a2, $fp, 0x1C0+toc_4
ROM:80224BF4 move $a3, $zero
ROM:80224BF8 move $a0, $s0
ROM:80224BFC sw $zero, 0x1C0+toc($sp)
ROM:80224C00 jal FileSys_ReadFile //[1]
# (fd,dst,size,NULL,NULL)
ROM:80224C04 move $s7, $v0
ROM:80224C08 jal FileSys_CloseFile //[2]
ROM:80224C0C move $a0, $s0
ROM:80224C10 move $a0, $zero
ROM:80224C14 addiu $a1, $fp, 0x1C0+var_198
ROM:80224C18 sw $s6, 0x1C0+var_194($fp)
ROM:80224C1C jal Read_EXIF //[3]
ROM:80224C20 sw $s7, 0x1C0+var_198($fp)
ROM:80224C24 bnez $v0, loc_80225004
In summary, we grab a temporary buffer using a function pointer inside of [0] that calls the Movie_GetTempBuffer
function, and then at [1] we read the open file descriptor (of the file found in the HTTP request) and put the bytes into our Movie_TempBuffer
. The open file is then closed at [2] and parsed for EXIF data at [3], which tries to look for a thumbnail.
In our case, the call to Read_EXIF
will fail if there’s no EXIF data, causing a branch to a new case, where it repeats the above disassembly (Movie_GetTempBuffer
=> FileSys_OpenFile
=> FileSys_ReadFile
=> FileSys_CloseFile
), except instead of looking for EXIF data, the server will try to parse out thumbnail information using the GxImgFile_ParseHeader
function instead.
GxImgFile_ParseHeader
takes two parameters, the first of which is a pointer to a struct on the stack of the parent function, defined as:
struct {
0x0: char *file_buff;
0x4: unsigned int file_size;
}
Assuming that the address of the file_buff
plus the file_size
does not cause an overflow/wrap-around (which would cause an error case), the following function will be called with our file_buff
as the only parameter:
sub_80085508
ROM:80085508 var_4 = -4
ROM:80085508
ROM:80085508 addiu $sp, -8
ROM:8008550C lw $v0, 0($a0) //[4]
ROM:80085510 sw $fp, 8+var_4($sp)
ROM:80085514 move $fp, $sp
ROM:80085518 move $sp, $fp
ROM:8008551C addiu $v1, $v0, 1
ROM:80085520 lw $fp, 8+var_4($sp)
ROM:80085524 lbu $v0, 0($v0) //[5]
ROM:80085528 sw $v1, 0($a0)
ROM:8008552C jr $ra
ROM:80085530 addiu $sp, 8
At [4], the first dereference is performed on the stack address pointing to the GXImgFile
structure, and then at [5], a second dereference is performed, which will provide the buffer from Movie_GetTempBuffer
.
As we can see, there’s never any error checking on this particular code flow, and if the device is not in “Movie mode” (which can be toggled by sending wificmd
3001), Movie_GetTempBuffer
will actually return a null buffer, causing the instruction at [5] to dereference a null pointer, crashing the process and device.
It should be noted that while the null buffer is actually checked for on multiple occasions (as noted by the “Crash Output” log messages), none of the checks stop or redirect the code flow.
WifiCmd_GetData(): Data2 path = asdf.JPG, argument -> custom=1&cmd=4001, mimeType= text/xml, bufsize= 66560, segmentCount= 0
WifiCmd_Lock(): Lock
WifiCmd_DispatchCmd(): cmd:4001 evt:0 par:0 CB:802249fc wait:0
WifiCmd_DispatchCmd(): ret 0
ERR:Movie_GetTempBuffer() get buffer fail
ERR:FileSys_ReadFile() ReadFile to NULL buffer
ERR:graph_flushAopCache() Invalid image A addr: 0x0
ERR:graph_flushAopCache() Invalid image A addr: 0xf000
WRN:ExifR_ReadExif() Appx marker not found! No APP0 or APP1
ERR:Movie_GetTempBuffer() get buffer fail
ERR:FileSys_ReadFile() ReadFile to NULL buffer
*** CPU Exception!!! cause 0x02: TLB exception (load or instruction fetch)
epc - 0x80085524
$ra - 0x80327b7c
$sp - 0x80d42358
$fp - 0x80d42360
general registers:
$zero : 0x807d0000 $at : 0xb0290000 $v0 : 0x00000000 $v1 : 0x00000001
$a0 : 0x80d42370 $a1 : 0x80d42458 $a2 : 0x80d42458 $a3 : 0x00000008
$t0 : 0x00000008 $t1 : 0x01010101 $t2 : 0x80d3cf74 $t3 : 0x8060f5b0
$t4 : 0x80d422c8 $t5 : 0x80d422c8 $t6 : 0x8001652c $t7 : 0x0000cccc
$s0 : 0x80d42428 $s1 : 0x000000ff $s2 : 0x80d42370 $s3 : 0x00000000
$s4 : 0x80d42458 $s5 : 0x00000000 $s6 : 0x00000000 $s7 : 0x00000000
$t8 : 0x0100c403 $t9 : 0xdeadbeef null : 0xdeadbeef null : 0xdad94150
gp : 0x8060f5b0 sp : 0x80d42358 fp : 0x80d42360 ra : 0x80327b7c
co-processor registers:
entrylo : 0xcccd0000 status : 0x00000008 vector : 0x0100c403 epc : 0x80085524
cause : 0x00000000 badvaddr : 0x00800008 hwrena : 0x00000400 prid : 0x00019655
entrylo : 0x001de850
Thread(id) :
Hfs Session(458)
stack :
range(0x80d3cf54 - 0x80d42f54)
call stack :
0 frame(0x80d42358 - 0x80d42360) ............................ $pc : 0x80085524
+ 0x80d42350 : 0x00000039 0x80d42360
1 frame(0x80d42368 - 0x80d423e0) ............................ $pc : 0x80327b74
+ 0x80d42360 : 0x0000005c 0x800f3840
+ 0x80d42370 : 0x00000000 0x80d43088 0x80d42684 0x80d42480
+ 0x80d42380 : 0x00000000 0x80000000 0x806b58e0 0x802ffc1c
+ 0x80d42390 : 0x00000000 0x00000000 0x00000000 0x80d423b8
+ 0x80d423a0 : 0x80d423a8 0x8030057c 0x80d423b0 0x801e538c
+ 0x80d423b0 : 0x80d42428 0x80d42650 0x80d424e4 0x00010200
+ 0x80d423c0 : 0x80d43088 0x80d42684 0x80d42480 0x00000000
+ 0x80d423d0 : 0x80d423d8 0x80225094 0xb0290000 0x80677548
^[[31m :
abort ($pc 00007520 is invalid address!)
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.