CVE-2018-4027
An exploitable denial-of-service vulnerability exists in the XML_UploadFile
Wi-Fi command of the NT9665X Chipset firmware, running on the Anker Roav A1 Dashcam, version “RoavA1_SW_V1.9.” A specially crafted packet can cause a semaphore deadlock, which prevents the device from receiving any physical or network inputs. An attacker can send a specially crafted packet to trigger this vulnerability.
Anker Roav A1 Dashcam RoavA1_SW_V1.9
https://goroav.com/products/roav-dash-cam-a1
5.3 - CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L
CWE-833: Deadlock
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 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 by accessing http://192.168.1.254/?custom=1&cmd=3012
.
For the following vulnerability, the XML_UploadFile
command (5001) will be discussed. Like all the other Wi-Fi commands, the prototype for this function is:
XML_UploadFile(char *URLPath,
char *QueryStr,
char *POSTData,
size_t PostDataLen,
u_int32 SegmentCount,
u_int32 putStatus);
Stepping back before the call to XML_UploadFile
, there’s two functions that handle the setup and calling of a given Wi-Fi opcode: WifiCmd_PutData
and WifiCmd_DispatchCmd
, the former of which calls the latter, the latter of which sometimes waits for the WifiCmd to finish before returning.
This particular vulnerability focuses on the WifiCmd_PutData
in combination with XML_UploadFile
, as this particular command seems to be the only WifiCmd
that causes a specific code path inside of WifiCmd_PutData
to occur.
The following function is called inside of WifiCmd_PutData
before any processing is done on the HTTP request:
ROM:80356E10 loc_80356E10: # CODE XREF: WifiCmd_PutData+AC↑j
ROM:80356E10 jal WifiCmd_Lock
ROM:80356E14 nop
ROM:80356E18 j loc_80356C94
ROM:80356E1C nop
The WifiCmd_Lock
, aptly named, locks the global WIFICMD_SEM_ID
semaphore, such that only one thread can access the WiFi commands at any given time.
As such, when calling the XML_UploadFile
function via a POST command to http://192.168.1.254/?custom=1&cmd=5001
, the following log messages are displayed over UART:
WifiCmd_PutData(): path =/sdcard, argument = custom=1&cmd=5001, bufAddr = 0x80d430cc, bufSize =0x1 , segmentCount =0 , putStatus = 0
WifiCmd_Lock(): Lock //[0]
WifiCmd_DispatchCmd(): cmd:5001 evt:0 par:0 CB:80221be8 wait:0
WifiCmd_DispatchCmd(): ret 0 // [1]
USB 1.5A
CONNECT to CHARGER
uiUsage=1
CHK: 2306, XML_UploadFile
CHK: 2314, XML_UploadFile
CHK: 2330, XML_UploadFile //[2]
At [0] we can see that the WifiCmd_Lock
is correctly locked, and at [1] that the program did in fact spawn a new thread for the XML_UploadFile
function. At [2], we hit a log message that indicates a file was opened for writing:
lw $a0, 0x80658D28
move $a1, $s3
addiu $a2, $fp, 0xE8+tos_4
move $a3, $zero
jal FileSys_WriteFile
sw $zero, 0xE8+tos($sp)
lui $a0, 0x8046
lui $a2, 0x8047
la $a0, a37mchkDS0m # "\x1B[37mCHK: %d, %s\x1B[0m\r\n"
la $a2, aXmlUploadfile # "XML_UploadFile"
jal Do_Logging
li $a1, 2330 //[3]
j loc_80221CE4
li $v0, 1
At [3], we see the 2330 for the CHK
value printed above, assuring that this is indeed the right code path. From here, if the provided request does not provide a “par=%d” HTTP query parameter, then the function returns 0x0 back to WifiCmd_PutData()
at the following disassembly:
ROM:80356D34 sw $s6, 0x68+tos($sp)
ROM:80356D38 sw $s7, 0x68+tos_4($sp)
ROM:80356D3C move $a0, $s3
ROM:80356D40 move $a2, $s4
ROM:80356D44 jalr $v0 //[4] #WifiCmd_Funcptr
ROM:80356D48 move $a3, $s5
ROM:80356D4C li $v1, 1
ROM:80356D50 beq $s7, $v1, loc_80356E20 //[5]
ROM:80356D54 nop
The call at [4] is the WifiCmd
that was just returned from, and the $s7
value that the code flow depends on at [5] is equal to 0x0, which causes the comparison to fail, and we reach the last basic block of the function which returns. To finally get around to where the vulnerability lies, let us examine the branch we did not take:
ROM:80356E20 loc_80356E20: # CODE XREF: WifiCmd_PutData+170↑j
ROM:80356E20 jal WifiCmd_Unlock //[6]
The path we take does not reach [6], and as such the WIFICMD_SEM_ID
is never unlocked, thus all other Wi-Fi commands are disabled at this point until the device reboots. Note, however, that the Wi-Fi can still be reset via the buttons on the device, allowing normal operations to resume.
More severely, if five more Wi-Fi requests of any type are sent to the device, the following can be seen via the ker dump
command over UART:
Semaphore[125, WIFICMD_SEM_ID] -> * (Max: 01, Cur: 00)
Waiting Task Queue:
eCos Thread[261, Hfs Session]
eCos Thread[262, Hfs Session]
eCos Thread[264, Hfs Session]
eCos Thread[266, Hfs Session]
eCos Thread[268, Hfs Session]
eCos Thread[270, Hfs Session]
eCos Thread[261, Hfs Session] -> Priority: 20
State: SLEEP, Stack Base: 0x80D3CE94 (Stack Size: 24576, Stack Used: 3200)
eCos Thread[260, hfs data thread] -> Priority: 20
State: SLEEP, Stack Base: 0x80D56D20 (Stack Size: 8192, Stack Used: 380)
[…]
The device seems to lock up immediately as six new threads are contending for the WIFICMD_SEM_ID
, which will never unlock. At this point, any subsequent HTTP communication results in the error message ERR: Exceed max clients 6
, moreover, any and all button presses are ignored, even the power button.
Additionally, while the device still does record valid video to the SD card, it is possible to completely disable the device until the battery runs out.
WifiCmd_PutData(): path =/sdcard, argument = custom=1&cmd=5001, bufAddr = 0x80d430cc, bufSize =0x1 , segmentCount =0 , putStatus = 0
WifiCmd_Lock(): Lock
WifiCmd_DispatchCmd(): cmd:5001 evt:0 par:0 CB:80221be8 wait:0
WifiCmd_DispatchCmd(): ret 0
USB 1.5A
CONNECT to CHARGER
uiUsage=1
CHK: 2306, XML_UploadFile
CHK: 2314, XML_UploadFile
CHK: 2330, XML_UploadFile
UINet_DhcpCb(): REQUEST_IP 128487 ms
WifiCmd_PutData(): path =/sdcard, argument = custom=1&cmd=5001, bufAddr = 0x80d4306c, bufSize =0x1 , segmentCount =0 , putStatus = 0
WifiCmd_Lock(): Lock
WifiCmd_PutData(): path =/sdcard, argument = custom=1&cmd=5001, bufAddr = 0x80d5f0cc, bufSize =0x1 , segmentCount =0 , putStatus = 0
WifiCmd_Lock(): Lock
WifiCmd_PutData(): path =/sdcard, argument = custom=1&cmd=5001, bufAddr = 0x80d7b0cc, bufSize =0x1 , segmentCount =0 , putStatus = 0
WifiCmd_Lock(): Lock
UINet_DhcpCb(): REQUEST_IP 143546 ms
WifiCmd_PutData(): path =/sdcard, argument = custom=1&cmd=5001, bufAddr = 0x80d970cc, bufSize =0x1 , segmentCount =0 , putStatus = 0
WifiCmd_Lock(): Lock
WifiCmd_PutData(): path =/sdcard, argument = custom=1&cmd=5001, bufAddr = 0x80db30cc, bufSize =0x1 , segmentCount =0 , putStatus = 0
WifiCmd_Lock(): Lock
WifiCmd_PutData(): path =/sdcard, argument = custom=1&cmd=5001, bufAddr = 0x80dcf0cc, bufSize =0x1 , segmentCount =0 , putStatus = 0
WifiCmd_Lock(): Lock
ERR: Exceed max clients 6
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.