Talos Vulnerability Report


Yi Technology Home Camera 27US QR Code trans_info Code Execution Vulnerability

October 31, 2018
CVE Number

CVE-2018-3898, CVE-2018-3899


An exploitable code execution vulnerability exists in the QR code scanning functionality of Yi Home Camera 27US 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.

Tested Versions

Yi Technology Home Camera 27US

Product URLs


CVSSv3 Score

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&timestamp=1522268058227 HTTP/1.1
Content-Length: 0
Host: api.us.xiaoyi.com
Connection: Keep-Alive
User-Agent: YI Home/ (Alcatel_4060A; Android 10.2; en-US)
Accept-Encoding: gzip
x-xiaoyi-appVersion: android;73;

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:

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

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.

Crash Information

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.