There’s Another Hole In Your SoC: Unisoc ROM Vulnerabilities

We discussed this vulnerability during Episode 154 on 27 September 2022

Eight vulnerabilities that were discovered by nccgroup in the UNISOC bootROM. One was in the second-stage recovery mode bootloader (FDL1), five were in the bootROM recovery mode, and two were in U-Boot. Exploiting the first vuln allowed them to RE the bootloader and find the other seven. Basically all the vulnerabilities are trivial and are due to lacking validation.

1. Buffer Overflow in FDL1 USB Recovery Mode in usb_get_packet() FDL1 has a custom USB protocol in their recovery mode, which is handled via usb_get_packet(). The bug is it simply doesn’t validate the payload length and copies the packet into a static buffer, which is also in executable memory. By achieving code execution, they were able to dump the bootROM on devices that had secure boot fuses blown.

2. Unchecked write in cmd_start() The cmd_start() command handler would accept a user-provided target address for receiving data, and performed no validation on it. By simply setting an arbitrary address and using cmd_recv_data(), arbitrary write could be achieved.

3. Out-of-bounds access due to unchecked index in USB command dispatcher (recovery_comms()) The command dispatcher would take an index for the command to execute, and would perform a lookup on the dispatcher function table for the handler. This index was not validated.

4. Buffer overflow in receive_payload_usb() The USB data transfer function would not validate payload length before copying the payload into a static buffer. The same issue also exists in receive_payload_uart().

5. Out-of-bounds read in handle_setup_request() When accepting USB setup requests, the request wLength wasn’t validated, allowing out-of-bounds read and information disclosure to occur.

6. Uninitialized read via recovery_comms()+cmd_start() recovery_comms() can receive a payload, but the length and payload are passed along to the handler. cmd_start() doesn’t validate the payload is at least 12 bytes to occupy the header. cmd_recv_data_usb() copies data of sz bytes without validating the amount of data actually received, so uninitialized memory can be read.

7. Lack of proper RSA verification of secondary bootloader Getting into U-boot, the bootROM needs to validate the signature of the second-stage bootloader. Their RSA verification supports two certificate types, contentcert (0) and keycert (1). Typically a keycert is embedded in the second-stage bootloader, and it’s validated against keys in fuses. But if you switch it to a contentcert type, no validation is performed. Unfortunately, this on it’s own wasn’t exploitable though because of other factors, notably that the cert type 0 path doesn’t initialize a valid hash key either.

8. Buffer overflow in RSA do_rsa_powmod() The RSA implementation itself had a vulnerability in the function responsible for modular exponentiation. When performing a byte swap, the length of the key size is not checked, and so by having a key greater than 2048 bits in size, you could overflow global buffers g_n and g_sig. Since these precede the stack in memory, this can be used to smash the stack and get persistent code execution.