An exploitable code execution vulnerability exists in the UDP network functionality of Yi Home Camera 27US 18.104.22.168D. A specially crafted set of UDP packets can allocate unlimited memory, resulting in denial of service. An attacker can send a set of packets to trigger this vulnerability.
Yi Technology Home Camera 27US 22.214.171.124D
7.5 - CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
CWE-400: Uncontrolled Resource Consumption
Yi Home Camera is an internet-of-things 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 such as the ability to view the video from anywhere, offline and subscription-based cloud storage, and ease of setup.
When the Yi Camera communicates with the ‘Yi Home’ phone app, in order to provide video feeds and status updates, the network daemon in charge is the
p2p_tnp binary. This daemon provides network discovery, authorization, and also settings manipulation functionality, all over UDP. The custom protocol handling this is rather simple to start, and a basic overview is given below:
| Magic Byte | Opcode Byte | MsgLen Short | Message (max 0x500) | \xF1 | \x00-\xFF | <len of msg> | <msg>
If the camera and the phone running the ‘Yi Home’ app are on the same subnet, a handshake occurs with the custom TNP protocol before any other operations can happen. The phone sends a
MSG_LAN_SEARCH (\x30) packet to UDP port 31208, to which the phone will respond with an
MSG_LAN_NOTIFY (\x31) packet from a new source port. It should be noted that this
MSG_LAN_SEARCH can be sent to a broadcast address or a single IP address, and also that the
MSG_LAN_SEARCH response contains the Device ID (DID) of the camera, which is needed to pass certain checks. A sample DID consists of a prefix, serial and suffix, for example:
After the initial packet exchange, the thread controlling this socket enters the
PPPP_thread_recv_Proto_device function, which contains a jumptable for dealing with a subset of the possible
TNP opcodes. Of interest for this bug is the
MSG_DEV_LGN_ACK_CRC opcode, which seems to validate a response from remote servers by utilizing a complex CRC algorithm. Throughout testing, this message has not been seen naturally on the wire, so the theory is that this is artifact code that has not been removed. The relevant disassembly follows below:
LDR R3, =0xFFFFF6C0 ADD R2, SP, #0x998+memcpy_base_ptr LDRH R1, [R2,R3] // STR R1, [SP,#0x998+size_of_inp] ADD R11, R1, #4 MOV R0, R11 BL malloc LDR R1, [SP,#0x998+size_of_inp] MOV R3, R11 MOV R9, R0 MOV R2, R9 // MOV R0, R10 BL tnp::PPPP_CRCDec //
At , the size of the input buffer data is taken and added to 0x4 before using that as the size of the input malloc buffer, which is stored into R2 eventually . At 3, a call to
tnp::PPPP_CRCDec is made, with the following approximation of parameters:
tnp::PPPP_CRCDec(udpbuff, inp_malloc, inp_malloc_size, udpbuff_size, crc_IV) // udpbuff => data of our packet // udpbuff_size = inp_malloc_size + 0x4. // crc_IV => Empty.
Once inside of
CRCDec, a quick stub is called to load the
CRC_IV, which doesn’t actually load anything, and then we step into the main body. To summarize the main functionality, it allocates a new buffer of size
size_of_inp - 0x4, does a bunch of CRC operations on our input buffer, and throws that into the buffer that was just allocated. Interestingly, approximately only the first 0xD0 bytes are actually CRC’d, the rest are just copied over from the UDP buffer to the malloc’d buffer.
After this, the code walks backward from the end of the CRC’d bytes and replaces all “\x43”/”C” bytes with null bytes, until it hits the end of the buffer-4. If the function finds a byte that is not a “C”, the function returns an error, after freeing the inner buffer. If the function only finds “CCCC” however, the CRC’d data will be memcpy’d from the buffer malloc’d inside the CRCDec function to the buffer that was malloc’d and then passed in as the
inp_malloc parameter. After this, the program will free the inner malloc buffer.
CRCDec function returns, if the CRC checksum “CCCC” was not found and CRCDec returned an error,
PPPP_thread_recv_Proto_device will just free the buffer allocated before
CRCDec, and return to processing new packets:
.text:00026EC4 MOV R0, R9 ; ptr .text:00026EC0 BL free .text:00026EC4 B loc_25B0C ; jumptable 00025BFC default case
Unfortunately, if there was no error, the login status is updated, and nothing else really occurs. The buffer malloc’d before
CRCDec is never freed, allowing an arbitrary amount of memory to be allocated by the `p2p_tnp process, causing quite a few unintended consequences.
Depending on the amount of these packets sent, and also the size of the data allocated, different outcomes can be achieved by an unauthenticated attacker with network connectivity. For testing purposes, the following buffer was used: `”A”*0x4A0+”CCCC”.
If one sends approximately 0xC00 packets, the device’s LED will eventually start blinking and the camera’s owners will no longer be able to reach the device from their Yi Home phone apps, even though it maintains network connectivity. The camera must then be physically rebooted in order to regain functionality. It is theorized that only the
tnp_p2p service is crashing at this point, rendering the UDP network comms disabled.
If one sends approximately 0x1000 packets, then the device LED also starts blinking, but this time, the
udhcp services all crash, causing a complete network disconnect. After a certain amount of time, the
dispatch binary will realize that
wpa_supplicant has crashed, and will promptly reboot the device. Thus, depending on the goals of the attacker, a camera reboot can also be propagated with this vulnerability.
2018-06-13 - 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 (>_>) of Cisco Talos.