Blog: Hardware Hacking
Turning an OBD-II reader into a USB / NFC attack tool
Everyone has their own way of going about things. Different methodologies, habits, and skill sets mean that approaches will be diverse.
This is how I work through things, just as an example of how it can be done. The process, not the product.
TL;DR / Index
- Building firmware from scratch
- Reverse engineering the screen
- USB connectivity
- Cracking firmware encryption
- Dumping the bootloader
- Modifying the hardware
This is a project that I originally put together for 44CON last year. You can find the video is at the end of this post.
For this project, I decided on a list of requirements for the device I wanted to modify:
- LCD screen
- Menu buttons
- USB device communication
- Onboard storage
Together, these features could make an effective USB emulation and exploitation device.
A colleague had shown me some OBD-II readers that he had been reverse-engineering in order to ascertain their capabilities, and I felt that this would be a perfect target, if I could find one with the right hardware.
After having looked around, I found a device which looked to have the desired capabilities for this project, the Nexpeak NX301.
- Utilises an STM32F103 MCU, with 64KB of Flash, 20KB of RAM
- 72MHz maximum clock speed
- Has an LCD screen, USB Interface, CAN interface and menu buttons
- Contains 8MB SPI flash memory
This device was found to be available under a number of different names, however they all seemed to have the same capabilities.
The USB interface on the device was found to be used for firmware updates, using a tool which could be downloaded online.
A quick look through the program files showed that it used a standard STM32 USB CDC interface for uploading of firmware, meaning that standard system libraries were in use, and that no additional hardware was used for USB communication on the board.
The program was found to download firmware files off the internet, using HTTP, meaning that the firmware file could be found and downloaded quickly, once the URL was found using Wireshark.
Quick analysis of this firmware payload found that it was heavily encrypted, so a quick win on breaking firmware updates wasn’t possible.
In addition to this, connecting to the STM32 chip on the board found that it was locked, meaning that firmware couldn’t be read from the device, but could be overwritten.
At this stage, with both firmware updates being encrypted and readout protection being enabled, I had two options in front of me:
Building firmware from scratch
- Wouldn’t require software reverse engineering
- Would require hardware reverse engineering
- Would allow for access to the entire 64KB of flash memory
- Wouldn’t allow for usage of the device’s firmware update functionality
- Would allow for modification of original application
- Would require finding an exploitable weakness in the device
- Would allow for usage of the built-in firmware updates
- Would not require any hardware modification
In the end, I decided to take both approaches.
Building firmware from scratch
I started with soldering an SWD header to the board, this was conveniently placed, allowing for easy programming of the device, even with the case shut.
Logic analysis of all peripherals prior to deletion of code from the chip is recommended, however in this case I did not do this, and decided to assess their functionality from scratch. This was simple for the SPI Flash and CAN interfaces on the board, as these had known datasheets. For the LCD screen however, there were complications.
A key element of assessment of the board is also tracing all of the standard peripherals to their pin on the microcontroller, for instance the menu buttons and LEDs. This was a simple task, as this hardware was directly connected to the pins.
From here, I set up a project using the STM32Cube design software, labelled each pin, and configured the SPI, USB and CAN interfaces.
In addition, STM32Cube has built-in USB libraries which can be modified to emulate almost any USB device, meaning that it could be configured to act as any hardware we wanted to spoof.
Reverse engineering the screen
Unfortunately, the LCD screen had a part number (DJM12864G13) but no publicly available datasheets. However, pinouts were available online on sales pages, allowing for easier assessment of the purposes of each pin.
Tracing each pin and assessment of this pinout showed that the device was in parallel mode, as outlined above. Sending random packets based on the command/data and read/write signal information outlined in the pinout allowed for further identification of the protocol. This concluded with showing that it utilised a standard protocol, also used by LCD controllers developed by other manufacturers who did have their datasheets available. Usage of these alternate datasheets allowed for appropriate communication with the LCD controller.
This culminated in the ability to draw ASCII bitmaps to the screen.
From here, it was time to look at the USB interface of the chip. My intention was to create a device capable of displaying itself as a USB disk, in order to write text files to it, and a USB keyboard in order to type pre-programmed commands to a standard PC, using the LCD screen and menu buttons to select the file to process.
Luckily for me, the STM32Cube provides libraries for standard device types, including the HID and Mass Storage libraries which could be used to emulate a USB drive and keyboard. The Mass Storage libraries were paired with the SPI flash on the board, allowing for storage of 8MB of data to the device.
I designed my software to boot up into two modes. Firstly, it was configured to boot as a USB disk when a menu button was held down on start-up. The SPI Flash on the board could be leveraged so that it stored a FAT32 filesystem.
Read and Write commands abstracted from the USB libraries could be mapped to the SPI Flash read and write commands, with the key difference being that the USB disk was configured to read and write 4096-byte blocks at a time, and the flash was designed for 256-byte blocks. All this meant was that all reads and writes via USB translated to 16 reads and writes for the flash.
I programmed the device so that when no buttons were held down on it, it would present itself as a USB keyboard to the host computer. In order to allow for menu selection from the files, I needed to be able to read the FAT32 file system.
To do this, I leveraged the FATFS library, which allows for mounting, reading, and writing of FAT32 filesystems, providing callback functions for read and writes in order allow developers to provide their own hardware integration. Using this, it was possible to read files, and print them to the screen.
In order to translate the contents of these text files to the USB keyboard interface, they needed to be translated to the scancodes used standard USB HID interfaces. This could simply be performed by generating a lookup table.
In order to bring this project together, I used a few development shortcuts. Due to the limitations of the hardware, some tricks had to be used. The most key of these was USB on board would only enumerate on initial connection to a host computer. Due to this, Mass Storage or Keyboard modes were selected by holding down buttons on start-up, and to change mode the whole device had to be disconnected from the PC.
This constituted a fully working device which fulfilled the goals I had for the device, but it would not be possible to upload this to a completely new Nexpeak without hardware modifications, as the firmware protections had not been broken yet.
Cracking firmware encryption
Just to reiterate, the restrictions in place were as follows:
- Firmware update payload is encrypted
- Code readout protection is enabled
- No known generic attacks for the chip were found to be viable
The STM32F103 is known to be vulnerable to glitching attacks, however this would require modification of the board, and other options were available.
The biggest weakness, which would cause the firmware protection in place to be broken, was that SWD was enabled in Firmware Upgrade mode. Despite readout protection being enabled on the STM32F103, RAM can still be read at any time, even if Flash can’t, helping to reveal key information that could be stored there.
In addition, due to firmware updates being use via standard USB CDC communication, it would be easy emulate these updates if a man-in-the-middle attack could be performed.
Using my BeagleBone Black and the USBProxy USB analysis tool, it was possible to view these updates as they were being performed. This approach can be performed using USBProxy and any embedded Linux device which has USB device capabilities, such as a Raspberry Pi Zero, or NanoPi.
A quick glance at the communication showed that it could be easily replayed via LibUSB, however CRCs were in use at the end of commands, which would require changing when the payload was modified.
From here, a tool was written to emulate and replay the firmware updates. Each individual packet could be replayed and verified base on their responses. The CRC in use was found to be the standard X.25 CRC-16, which could be easily changed in order to modify elements of the update. This analysis also showed that the update was sent in 512-byte blocks.
Cursory analysis of the firmware update file found that it matched the potential memory space of the chip, heavily implying that no conventional integrity checking was in place. This was corroborated by the fact that the device would not boot if a corrupt firmware payload was sent to it. The ability to upload older firmware files also demonstrated that there was no rollback protection.
It was possible to stop the firmware update process after each individual 512-byte block was sent. Doing this, it was possible to identify where in RAM the firmware payload is decrypted and stored, as the first two 32-bit blocks used by Cortex-M chips such as the STM32F103 in use always end with an 0x20 and 0x08 respectively.
This allowed for manual extraction of the decrypted firmware in RAM, and allowed for it to be stitched together. Even though it was possible to decrypt the firmware, it was not possible to write new firmware, as the encryption algorithm and keys in use were still unknown and the chip crashed after each RAM read.
While it was not possible to ascertain the encryption algorithm in use from a RAM dump, it was highly likely that any encryption keys were stored there. As such, if the encryption algorithm could be identified, these could be used together in order to encrypt and decrypt the firmware.
Due to this, steps were taken to guess the encryption algorithm. Subtle modifications were made to the first 512-byte block of firmware in order to see their effect on the decrypted firmware in RAM. This process could allow for narrowing down the encryption algorithm in use. The following approach was taken, and while this did not explore all possibilities, it does show how this could be narrowed down.
Starting simply, one byte was changed in the encrypted firmware. If only one byte changed in the decrypted firmware, this was likely to be a stream cipher.
If a byte was modified and only bytes in the first handful of bytes in the decrypted firmware was changed, it would denote an ECB-mode block cipher, most likely AES.
If a byte was modified in a later section of the firmware, and only all subsequent blocks of data changed, it would denote a CBC-mode block cipher.
In the end, however, modification of any byte of the payload changed the entirety of the decrypted firmware, meaning that the most likely algorithm was going to be Block TEA or XXTEA.
With this assumption, the first 512-byte block and the RAM dump were processed together, cycling through each block of the RAM.
- key = 0x00001494;
- key = 0x0000d65b;
- key = 0x05e2e03f;
- key = 0x00005426;
Dumping the bootloader
It would now be possible to deploy our custom firmware to the chip, however I wanted to take it a step further. I wanted to swiftly dump the bootloader which handled secure firmware updates from the chip.
By modifying the original firmware payload, now that we had the encryption keys, we could override the firmware to dump the entire contents of the bootloader into RAM.
This allowed it to be read out and then redeployed to the device if it was erased. I wrote a small ARM payload to protect this persistence.
In sequence, this firmware loaded a small magic number to the start of RAM (0xBBAA), followed by looping through the Flash memory and writing it to the remainder of RAM, ending with an infinite loop so no further processing was performed. This could be compiled quickly in any assembler, however I used the online assembler provided by shell-storm.org for this purpose. With the code compiled, it could be written into the update payload.
In order to do this quickly, I wanted to paste it directly into the update data. By identifying the reset vector of the microcontroller at address 0x04 in the binary, I could ascertain that it needed to be written at offset 0x100 in the firmware. This can be calculated based on some assumptions.
The reset vector was found to be 0x08004101. Firstly, all flash starts at 0x08000000, meaning that this could be removed from address. Next, we knew that the bootloader was 0x4000 in size, so this could be removed. Lastly, we know that all Thumb code has the lowest bit of the address set, in order to differentiate it from ARM code, and thus could be removed. This left us with the value 0x100, which is where I wrote the assembled payload.
Dumping the RAM after running this code showed our magic number, followed by the bootloader.
This allowed for full knowledge of the inner workings of the device as it would be traditionally used.
Knowledge of the firmware update procedures means firmware can be deployed without hardware modification.
Sensitive information in the firmware can be exfiltrated.
Modification of the existing firmware can be performed.
Together, this access could allow us to use this device for any purpose without hardware modification. Due to this, I decided to also make the device have a purpose which did require hardware modification.
Modifying the hardware
Simple hardware modifications can add extra features to devices, and removal of ancillary components can allow new components to be attached in their place.
The CAN interface on the device was something that I didn’t want to work with, as it was the intended original purpose of the device.
Due to this, I removed it from my pin design, and replaced it with timer and interrupt pins.
These would be sufficient to allow me to replicate some previous work of mine: creation of NFC tag emulation devices.
The original components for the CAN communication on the board could be desoldered, and wire could be added in their place in order to connect a new daughterboard.
Using the same standard design I had used previously, a small daughterboard could be made and connected to these wires.
An antenna could then be added to this board and threaded through to the back of the device’s case.
Using the software developed for the USB emulation device, it was possible to use the USB Mass Storage functionality in order to store binary NFC tag data on the device, which could then be used for emulation.
The 72MHz clock speed available on the chip meant that all software could be ported almost directly to the new device, with the LCD screen being used to select tag data deployed via USB.
A Lithium-Ion battery I had available was taped to the back of the device, which could be connected directly to the SWD pins in order to power it.
Modifying existing software and hardware for an embedded device can be a complex, but rewarding, experience. It can teach valuable development and reverse engineering skills which can be transferred to other aspects of IoT security, and can allow you to breath new life into your junk.
I highly recommend breaking your devices before you throw them away, to see if you can remake them into something more interesting.