Communicate with Airoha-based Bluetooth devices using the RACE protocol via Bluetooth and USB.
Python
477
10 commits
updated Jan 15, 2026
RACE Toolkit is the tool released alongside our Airoha research. You can find more about that in our blog post.
This repository contains a Python-based command-line toolkit for interacting with devices that expose the RACE protocol over various transports (BLE GATT, Bluetooth Classic RFCOMM, USB HID). It is primarily intended for further security research into the Airoha ecosystem and for end-users to check whether their devices are affected by the vulnerabilities.
The tool supports RAM/flash dumping, device information queries, and has preliminary support for firmware updates (FOTA). Whether a given feature works with a specific device is largely dependent on the device. For example, RAM dumping only works on devices that (still) have the command exposed. The firmware update process currently only supports headphones, not TWS (true-wireless stereo) earbuds.
RACE toolkit also offers a command to check whether a given device is affected by CVE-2025-20700, CVE-2025-20701, or CVE-2025-20702. However, due to differences in devices, we cannot guarantee the reliability of the check command. If it returns FIXED, there might still be a chance the device is vulnerable. For example, some devices require specific circumstances to bypass the Bluetooth Classic pairing. We also saw a device that was only vulnerable to the Classic pairing issue in one of multiple tries and across reboots. We didn't properly investigate all these devices and all these edge-cases. Nonetheless, the check command is a good starting point, and it will not generate false positives. If it considers a device as vulnerable, the device is vulnerable.
This project supports two installation methods. You can choose either based on your preferred workflow:
pip with requirements.txtuv using pyproject.tomlBoth methods install the same dependencies. Due to the requirements of the Bumble Bluetooth library, Python 3.10 is required.
pippython -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
uvThis repository includes a pyproject.toml that can be used with uv.
uv sync
This will:
uv.lockTo run commands inside the environment:
uv run python race_toolkit.py --help
To use the gatt or rfcomm transports, Bumble requires access to either your built-in Bluetooth controller or an external Bluetooth dongle. On some Linux distributions the Bluetooth daemon needs to be disabled. If you see any HCI-related or libUSB-related issues, try deactivating the service (systemctl stop bluetooth). On macOS, an external dongle is required, as the operating system does not expose the HCI layer.
Usually, the best approach is using an external Bluetooth dongle. This allows you to use the toolkit without interfering with your OS Bluetooth stack. Note that not all Bluetooth dongles are supported by Bumble.
RACE can be communicated via different transports. In this toolkit we implemented a subset of these. Each transport has different capabilities, limitations, and requirements.
--transport gatt)Default transport.
Notes:
--authenticate--transport bleak)Limitations:
--transport rfcomm)Notes:
--transport usb)Notes:
VID:PIDpython race_toolkit.py [global options] <command> [command options]
These options apply to all commands unless stated otherwise.
| Option | Description |
|---|---|
-t, --transport | Transport method. One of gatt, bleak, rfcomm, usb (default: gatt) |
-c, --controller | Bumble Bluetooth controller (default: usb:0) |
--target-address | Target device Bluetooth classic address |
--le-names | One or more BLE device names to scan for if no address is provided |
-d, --device | USB HID device VID:PID (only for usb transport) |
--outfile | Write command output to a file instead of stdout |
--debug | Enable debug logging |
--send-delay | Delay (in seconds) between RACE messages (might be required for old firmware?) |
--authenticate | Attempt pairing/authentication during connection |
checkCheck a device for the RACE vulnerabilities:
python race_toolkit.py check
The command will interactively guide you through the process. It performs the following actions:
If you know your device's Bluetooth Classic address already, you can supply it via the --target-address parameter. RACE toolkit will try to obtain the address during the BLE phase. If this fails, it will interactively ask for the address. If the device is not available via BLE the automatic retrieval of the Classic address will not work.
At the end, a summarized vulnerability status is printed.
ramRead from device RAM.
python race_toolkit.py [global options] ram --address <hex> --size <hex>
Options:
--address (required): Target RAM address (hex)--size (required): Number of bytes to read (hex). Must be multiple of 4 as the command reads 4 bytes only.Behavior:
--outfile is specifiedflashDump flash memory.
python race_toolkit.py [global options] flash --address <hex> --size <hex>
Options:
--address (required): Flash start address (hex, multiple of 0x100)--size (required): Number of bytes to dump (hex, multiple of 0x100)link-keysRetrieve stored Bluetooth BR/EDR link keys.
python race_toolkit.py [global options] link-keys
Notes:
dump-partition).bdaddrQuery the Bluetooth Classic address via RACE.
python race_toolkit.py [global options] bdaddr
Notes:
sdkinfoRetrieve SDK information from the device.
python race_toolkit.py [global options] sdkinfo
Notes:
buildversionRetrieve the firmware build version string.
python race_toolkit.py [global options] buildversion
Notes:
mediainfoDump metadata about the currently playing media. Proof-of-concept command for a live demo targeting the Sony WH-CH720N.
python race_toolkit.py [global options] mediainfo
Important notes:
dump-partitionInteractively dump a flash partition.
python race_toolkit.py [global options] --outfile <file> dump-partition
Workflow:
This is usually used to dump the NVDM partition (most of the time it's partition number 6). This partition contains configuration data.
Notes:
--outfile option is required here. This command will not print to stdout.fotaWARNING: Only use this command if you know what you are doing!
This command perform a FOTA firmware update. We reimplemented the FOTA process as we have observed it during an update of one of our devices. Additional information was retrieved by reverse-engineering the firmware and a mobile app. In the end, this process was confirmed to work with Sony WH-CH720N and Sony WH-1000 XM6. It likely works with other Sony headphone models, however, we have not confirmed this. The same applies to headphones from other vendors. Additionally, the current implementation of the FOTA process does not work with True Wireless Stereo (TWS) earbuds. This would require additional steps that we have not (yet) implemented.
This implementation allows you to flash valid FOTA images. It also allows firmware downgrades. However, we don't recommend downgrading your production device. Sony firmware can be found in the MDR Proxy Repository. Make sure to choose the correct device when downloading the firmware and running the FOTA command.
Again, don't use this if you don't know what you are doing. Due to the integrity checks of the firmware during the FOTA process it should be fine. During our research we bricked two devices after playing around with the firmware image and running the update.
python race_toolkit.py [global options] fota --fota-file <file> [options]
Options:
--fota-file (required): Path to the FOTA image--dont-reflash: Do not erase/reflash the FOTA partition--chunks-per-write: Number of chunks per flash write (default: 3)Notes:
--fota-file is required unless --dont-reflash is setThis tool is intended for research and educational purposes only.
Python
100.0%
Communicate with Airoha-based Bluetooth devices using the RACE protocol via Bluetooth and USB.
Python
477
10 commits
updated Jan 15, 2026
RACE Toolkit is the tool released alongside our Airoha research. You can find more about that in our blog post.
This repository contains a Python-based command-line toolkit for interacting with devices that expose the RACE protocol over various transports (BLE GATT, Bluetooth Classic RFCOMM, USB HID). It is primarily intended for further security research into the Airoha ecosystem and for end-users to check whether their devices are affected by the vulnerabilities.
The tool supports RAM/flash dumping, device information queries, and has preliminary support for firmware updates (FOTA). Whether a given feature works with a specific device is largely dependent on the device. For example, RAM dumping only works on devices that (still) have the command exposed. The firmware update process currently only supports headphones, not TWS (true-wireless stereo) earbuds.
RACE toolkit also offers a command to check whether a given device is affected by CVE-2025-20700, CVE-2025-20701, or CVE-2025-20702. However, due to differences in devices, we cannot guarantee the reliability of the check command. If it returns FIXED, there might still be a chance the device is vulnerable. For example, some devices require specific circumstances to bypass the Bluetooth Classic pairing. We also saw a device that was only vulnerable to the Classic pairing issue in one of multiple tries and across reboots. We didn't properly investigate all these devices and all these edge-cases. Nonetheless, the check command is a good starting point, and it will not generate false positives. If it considers a device as vulnerable, the device is vulnerable.
This project supports two installation methods. You can choose either based on your preferred workflow:
pip with requirements.txtuv using pyproject.tomlBoth methods install the same dependencies. Due to the requirements of the Bumble Bluetooth library, Python 3.10 is required.
pippython -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
uvThis repository includes a pyproject.toml that can be used with uv.
uv sync
This will:
uv.lockTo run commands inside the environment:
uv run python race_toolkit.py --help
To use the gatt or rfcomm transports, Bumble requires access to either your built-in Bluetooth controller or an external Bluetooth dongle. On some Linux distributions the Bluetooth daemon needs to be disabled. If you see any HCI-related or libUSB-related issues, try deactivating the service (systemctl stop bluetooth). On macOS, an external dongle is required, as the operating system does not expose the HCI layer.
Usually, the best approach is using an external Bluetooth dongle. This allows you to use the toolkit without interfering with your OS Bluetooth stack. Note that not all Bluetooth dongles are supported by Bumble.
RACE can be communicated via different transports. In this toolkit we implemented a subset of these. Each transport has different capabilities, limitations, and requirements.
--transport gatt)Default transport.
Notes:
--authenticate--transport bleak)Limitations:
--transport rfcomm)Notes:
--transport usb)Notes:
VID:PIDpython race_toolkit.py [global options] <command> [command options]
These options apply to all commands unless stated otherwise.
| Option | Description |
|---|---|
-t, --transport | Transport method. One of gatt, bleak, rfcomm, usb (default: gatt) |
-c, --controller | Bumble Bluetooth controller (default: usb:0) |
--target-address | Target device Bluetooth classic address |
--le-names | One or more BLE device names to scan for if no address is provided |
-d, --device | USB HID device VID:PID (only for usb transport) |
--outfile | Write command output to a file instead of stdout |
--debug | Enable debug logging |
--send-delay | Delay (in seconds) between RACE messages (might be required for old firmware?) |
--authenticate | Attempt pairing/authentication during connection |
checkCheck a device for the RACE vulnerabilities:
python race_toolkit.py check
The command will interactively guide you through the process. It performs the following actions:
If you know your device's Bluetooth Classic address already, you can supply it via the --target-address parameter. RACE toolkit will try to obtain the address during the BLE phase. If this fails, it will interactively ask for the address. If the device is not available via BLE the automatic retrieval of the Classic address will not work.
At the end, a summarized vulnerability status is printed.
ramRead from device RAM.
python race_toolkit.py [global options] ram --address <hex> --size <hex>
Options:
--address (required): Target RAM address (hex)--size (required): Number of bytes to read (hex). Must be multiple of 4 as the command reads 4 bytes only.Behavior:
--outfile is specifiedflashDump flash memory.
python race_toolkit.py [global options] flash --address <hex> --size <hex>
Options:
--address (required): Flash start address (hex, multiple of 0x100)--size (required): Number of bytes to dump (hex, multiple of 0x100)link-keysRetrieve stored Bluetooth BR/EDR link keys.
python race_toolkit.py [global options] link-keys
Notes:
dump-partition).bdaddrQuery the Bluetooth Classic address via RACE.
python race_toolkit.py [global options] bdaddr
Notes:
sdkinfoRetrieve SDK information from the device.
python race_toolkit.py [global options] sdkinfo
Notes:
buildversionRetrieve the firmware build version string.
python race_toolkit.py [global options] buildversion
Notes:
mediainfoDump metadata about the currently playing media. Proof-of-concept command for a live demo targeting the Sony WH-CH720N.
python race_toolkit.py [global options] mediainfo
Important notes:
dump-partitionInteractively dump a flash partition.
python race_toolkit.py [global options] --outfile <file> dump-partition
Workflow:
This is usually used to dump the NVDM partition (most of the time it's partition number 6). This partition contains configuration data.
Notes:
--outfile option is required here. This command will not print to stdout.fotaWARNING: Only use this command if you know what you are doing!
This command perform a FOTA firmware update. We reimplemented the FOTA process as we have observed it during an update of one of our devices. Additional information was retrieved by reverse-engineering the firmware and a mobile app. In the end, this process was confirmed to work with Sony WH-CH720N and Sony WH-1000 XM6. It likely works with other Sony headphone models, however, we have not confirmed this. The same applies to headphones from other vendors. Additionally, the current implementation of the FOTA process does not work with True Wireless Stereo (TWS) earbuds. This would require additional steps that we have not (yet) implemented.
This implementation allows you to flash valid FOTA images. It also allows firmware downgrades. However, we don't recommend downgrading your production device. Sony firmware can be found in the MDR Proxy Repository. Make sure to choose the correct device when downloading the firmware and running the FOTA command.
Again, don't use this if you don't know what you are doing. Due to the integrity checks of the firmware during the FOTA process it should be fine. During our research we bricked two devices after playing around with the firmware image and running the update.
python race_toolkit.py [global options] fota --fota-file <file> [options]
Options:
--fota-file (required): Path to the FOTA image--dont-reflash: Do not erase/reflash the FOTA partition--chunks-per-write: Number of chunks per flash write (default: 3)Notes:
--fota-file is required unless --dont-reflash is setThis tool is intended for research and educational purposes only.
Python
100.0%