Xiaomi AI Speaker Authenticated RCE I: Firmware Analysis
This three-part writeup details the journey of finding and exploiting a vulnerability in Xiaomi AI Speaker (MICO S12A) without a physical peripheral (UART). Part I focuses on the approach of obtaining the firmware and introduces the firmware structure in detail.
Author: Otis Chang
This three-part writeup details the journey of finding and exploiting a vulnerability in Xiaomi AI Speaker (MICO S12A) without a physical peripheral (UART). The vulnerability, CVE-2020-14096, was fixed in 1.59.6[1].
Introduction
Part I focuses on the approach of obtaining the firmware and introduces the firmware structure in detail.
Before telling the story, I suggest the reader first read the research published by Tencent Blade Team in DEF CON 26. It is very helpful for understanding the OTA update mechanism of Xiaomi Speaker.
Also, there are some publications and CVEs for further reading. (Updated on Sep. 15 2020)
- Dennis Giese, “Having fun with IoT: Reverse Engineering and Hacking of Xiaomi IoT Devices,” DEF CON 26, 2018
- CVE-2020-8994 (UART access)
- CVE-2020-10262 (UART access)
- CVE-2020-10263 (UART access)
Obtaining The Firmware
If your device firmware is not up to date, you can get the firmware URL directly from Xiaomi's upgrade API[2].
GET /upgrade/mico?checkUpgrade=1&countryCode=CN&deviceIdList=<DEVICE_ID>&filterID=&locale=zh_CN&platform=micoiOS&version=<IOS_APP_VERSION> HTTP/1.1
Host: tw.api.miwifi.com
User-Agent: MISoundBox/2.1.25 (com.xiaomi.mico; build:2.1.56; iOS 13.7.0) Alamofire/4.9.1 MICO/iOSApp/appStore/2.1.25
Q: What if my device has been updated to the latest version?
The upgrade API will not return any URL if your device is at the latest version. However, you can find the firmware name format from Figure 1., which is mico_{all || firmware}_{MD5_LAST_5_CHARS}_{VERSION}.bin
. If the prefix of your firmware name is mico_all, it means that the firmware image also contains u-boot firmware and dtb.img.
In addition, the firmware URL also tells us that Xiaomi AI Speaker's internal product id is mico and the model id is s12a. BTW, Xiaomi Router's product id is miwifi.
Assume that the version 1.34.36 exists on the server. The firmware CDN host is fixed. To get the firmware file, you only need to guess the hex string 2x16⁵ times. That is pretty easy. Here is the script I used to brute-force the firmware URL[3].
Firmware Structure
After obtaining the firmware image, you can learn the firmware structure by reversing the OTA update binary /bin/miso
. Xiaomi AI Speaker's firmware is not encrypted. At first, you can use binwalk to get the /bin/miso
. The reverse engineering of the binary will not be described here. In the rest of the post, I'll just introduce the structure.
The firmware structure is shown in Figure 2. The firmware contains three blocks: (1) firmware image header, (2) segments, and (3) signature.
ImageHeader
ImageHeader describes the location of segments and signature. It also has a checksum field for verifying the integrity of segments.
struct ImageHeader
{
unsigned int magic;
unsigned int signature_offset;
unsigned int crc32_checksum;
unsigned short file_type;
unsigned short model;
unsigned int segment_offsets[8];
};
Segments
Each segment has its own header. flash_address
and partition
are not used in Xiaomi AI Speaker. They have fixed value 0xFFFFFFFF
and 0x0000FFFF
respectively.
struct SegmentHeader
{
unsigned int magic;
unsigned int flash_address;
unsigned int segment_length;
unsigned int partition;
char segment_name[32];
};
- mico_version
Usually, it is the first segment, which contains the metadata of the firmware. - root.squashfs
This segment contains a read-only SquashFS filesystem based on OpenWrt. You can use unsquashfs to extract its contents. - boot.img
- dtb.img
- u-boot.bin.usb.bl2
- u-boot.bin.usb.tpl
Signature
In addition to the checksum, Xiaomi also applies a RSA signature verification.
struct SignatureHeader
{
unsigned int signature_length;
unsigned int padding[3];
};
Firmware Toolkit
Based on the above structures, I wrote a simple script called Xiaomi-OpenWrt-firmware-toolkit[4] to show firmware information and extract firmware segments.
$ python3 ./toolkit.py -s ./mico_all_0f70e_1.34.36.bin
2020-09-17 12:04:14,412 - [MSG] Input file: /Users/<redacted>/mico_all_0f70e_1.34.36.bin
2020-09-17 12:04:14,412 - [Jobs] Verifying firmware image...
2020-09-17 12:04:14,412 - firmware magic: 0x31524448
2020-09-17 12:04:14,413 - firmware signature_offset: 0x2409b70
2020-09-17 12:04:14,413 - firmware signature_length: 256
2020-09-17 12:04:14,413 - firmware signature: 81947990e457ef31a0a297441297e194e65ff9f6f05534d1e905b6c644e15349425d017567a89e06076a915802ae02c4c9bde15289f6b2b65ef90d71ab3c6509036623d2a7e14a37d674be7f3403bfb90f28a0ad77af46087a6ccc985f3f865a426ab5b057644057bc7159c0479b4ac4748fe58f97c1ca7b0c57282e81b5732216b681748c23bda79aa05d53913426b448b5f8850c3ba30ce7c4a60fbd64cf76251f5e889d8c39dd1dc1554d292d4b702328a93ff19072c9028222fb3dea61996b162a9722776cea3a7e263b3b1b82e31ca58f4c4239c43c76f15bbf833a5696e53b051e6a62a8805c0c93f30ce238f65a705522e5a7196a6d9d48e9f7340578
2020-09-17 12:04:14,413 - firmware crc32_checksum: 0xf1d55a5b
2020-09-17 12:04:14,436 - computed crc32_checksum: 0xf1d55a5b
2020-09-17 12:04:14,492 - computed md5 hash: 4d2033e9b6c99178f8eccc1a3580f70e
2020-09-17 12:04:14,492 - [Jobs] Verification success: it's a genuine firmware from Xiaomi.
Conclusion
Xiaomi AI Speaker's firmware includes a CRC32 checksum and a RSA signature. In general, signature verification ensures data integrity. Howerver, Xiaomi miso's implementation is not secure. Next part will describe the OTA update process of Xiaomi Speaker.
Xiaomi Speaker has many models and different update channels. Xiaomi does not push update to all channels. For example, model id: s12a's latest firmware is 1.34.39 (Taiwan channel). (Updated on Sep. 15 2020) ↩︎
This API does not require user token. ↩︎
https://gist.github.com/csftech/c11b817bd76cf1443fbe39355e21bdf9 ↩︎
https://github.com/csftech/Xiaomi-OpenWrt-firmware-toolkit ↩︎