Blog: Hardware Hacking

Fastboot Fuzzing

David Lodge 29 Sep 2023

TL;DR

  • The Fastboot protocol can often have hidden commands
  • Those commands can do interesting things
  • Conventionally they’re found by reverse engineering
  • Can’t find a copy of the firmware? Guess the commands
  • A custom implementation of the protocol enables fuzzing via dictionary or brute force
  • A simple tool was written (fuzz-fastboot), freely available here

What’s this then?

Fastboot is a client-server tool and protocol that was added to the Android bootloader since the early days of Android. It is pretty much always used by the Android bootloader and can be seen on devices running other operating systems, such as Linux.

The daemon can be talked to with a tool that is part of the Google SDK, called Fastboot. The client is good enough for most uses, but it is deliberately restricted to predefined commands.

This means that commands outside the normal can’t easily be referred to, and talking to obscure USB devices IDs becomes complex. I experimented with this when I was doing some work on the Unisoc bootloader and wrote a basic Fastboot client in Python.

For that research we had access to the OTA firmware for the device and could reverse engineer any commands out of the abl partition. Recently we tested a device that had some hidden commands, but we didn’t (yet) have a dump of the abl partition, nor any OTA firmware.

So, could I build on our previous code and build a simple fuzzer?

The Fastboot protocol

The protocol is remarkably simple. It consists of two conversations sent mostly using ASCII text. The protocol uses two USB endpoints to communicate. By default a Fastboot client will have a USB vendor and device ID as 22b8:2e80, although some bootloaders like Unisoc will alter this. It uses endpoints of 0x81 (in) and 0x01 (out). Again, some bootloaders will alter this.

The message is simply the command to be issued, up to 64 octets. Subcommands can be separated by “:” or ” ” and parameters are separated by a space. There is some inconsistency in how this is managed which is very bootloader dependent.

For example, getvar uses a “:” whereas oem uses a ” “. Both of the below commands are valid, but swap the “:” for a ” ” or vice versa and the command will error.

  • getvar:all
  • oem unlock

Depending on the command a status will be returned in a packet that is less than 256 bytes, which can be one of the following responses:

  • OKAY – The command was successful with no response
  • INFO – The command succeeded and returned a message which is in the rest of the message
  • TEXT – A multiple block text response is return, keep reading ASCII data until a NULL is received
  • FAIL – The command failed, an error message may be in the rest of the packet
  • DATA – Enter a data phrase, where subsequent packets will be binary data. The length of the data is passed as part of the DATA response

The client can also choose to enter a data phase with the DATA command.

As this is a relatively simple protocol it can be implemented is less than 50 lines of Python.

Making a fuzzer

With the goal of finding hidden commands, the easiest route is to just find a dictionary of command commands and attempt to send each command through Fastboot and look for a valid response (i.e. not a FAIL status code).

This is really trivial and could be written as a simple bash script: just loop through a dictionary and pass it to Fastboot. The problem with this was when the loop got to the command “oem lock” which requires an onscreen prompt. So, we had to add a “don’t issue these commands list”.

Some tinkering later and we added a traditional pattern based brute force, just using lower case letters, “-” and “_”.

A version of this can be found at our GitHub repository: https://github.com/pentestpartners/fuzz-fastboot.

Conclusions

The fuzzer was effective and identified a number of commands; the standard Fastboot service on all the devices we tested was a lot more stable than expected. Throughput was not quite the best: using the Fastboot service in abl on a Motorola G10 phone returned around 15 commands a second, making it feasible to test a common sized dictionary.

Manipulations of dictionaries to combine different words, separated by “-” or “_” was effective producing a number of hits.

Ultimately it shows that fuzzing can be used to find hidden functionality and to make sure that when production firmware is released it only has the required commands for its purposes. We have in the past found interesting commands that provided extra options and vulnerabilities, such as in the Motorola E20 described here https://www.pentestpartners.com/security-blog/moto-e20-readback-vulnerability/.