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.

Xiaomi AI Speaker Authenticated RCE I: Firmware  Analysis

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)

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

mico_upgrade_api

Figure 1. REQ/RSP of upgrade API

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.

mico_firmware_structure

Figure 2. MICO firmware structure

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.


  1. 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) ↩︎

  2. This API does not require user token. ↩︎

  3. https://gist.github.com/csftech/c11b817bd76cf1443fbe39355e21bdf9 ↩︎

  4. https://github.com/csftech/Xiaomi-OpenWrt-firmware-toolkit ↩︎