Write-up: Badge HITB attendee

Filter by category:

July 8, 2019 by Damien Cauquil
I attended Hack In The Box 2019 Asmterdam as a speaker, and it was pretty awesome ! I met awesome people, old friends, had fun with old games, made some jewelry, and drank a lot of Club Mate. All of this during two amazing days =). I also solved the attendee badge challenges, because you know, I'm not fond of coffee shops and Amsterdam's Red District.



HITB badge PCB reverse-engineering

When I packed for HITB, I put some tools in my luggage: a basic multimeter, a portable USB soldering iron, some wires and clips, a spool of solder and a FTDI/RS232 adapter. I wanted to travel light, so I took what I thought was a minimal toolbox.

So here I was at HITB Amsterdam, with a friend of mine asking if we could have alook at the attendee's badge. It is all red and shiny, have a couple of buttons and LEDs, and is powered by 2 AAA batteries. I quickly noticed that this badge was built upon a very common microcontroller from ST Microelectronics: a STM32F103.

First things first, I used my multimeter to figure out the schematic. It took me some time to determine where all these LEDs and buttons are connected on the MCU, but also what the two footprints placed on each side of the badge were for. The result is the schematic below:

Extracting the firmware

STM32F103 provides a Single Wire Debug (SWD) interface that can be used to extract its firmware. The problem is I did not bring an ST-Link v2, a specific adapter that I use to get the firmware from compatible SoCs and MCUs. I went all around the HAXPO first floor asking people if they did not have by chance an adapter to lend me. No luck, no adapter.

We went to the Yes We Hack booth (ran by some of old pals of mine) and decided to use their Raspberry Pi as a cheap ST-Link v2 adapter, without disconnecting it from its attached screen as it was used to show a video at this booth. We managed to get it download and compile a special version of OpenOCD, but it took ages. The documentation is not that clear and we spent a lot of times trying to get an SSH access to it. But in the end, I got OpenOCD working and connected to the badge. And this is at this exact moment I did something wrong. I don't know really why, but I persuaded myself that this MCU' SWD interface was disabled and I needed to bypass it. I recalled @LargeCardinal STM32F103 hack and tried it, and it worked ! Weird fact: I was only able to extract a few kilobytes of memory instead of the whole 256 kB. Anyway, this did not bother me that much at this time, and I returned happy at the hotel after a quick diner with some friends, ready to reverse-engineer the firmware I just extracted.

I spent some time looking for GPIO references, but nothing matches the one I figured out previously. Only PORT A was used, whereas this badge definitely uses ports A and B. Something was wrong. I continued to analyze this firmware, and at a certain point it was clear to me that it was only Flash-related. This whole firmware only manipulates Flash memory ! And then it was obvious: I dumped the bootloader instead of the actual firmware :/. But that's cool, that means this bootloader can be used to access the firmware. Still no ST-Link v2 adapter, but luckily it happened that this badge has a stock bootloader able to use UART for firmware upload/download ! I connected my FTDI adapter to the badge's UART port, downloaded the stm32flash tool and voilà. In a few command lines, the actual firmware was extracted. It was Friday 0030 AM, and I spent way too much time doing what is normally a routine task. Shame on me.

Firmware reverse-engineering


Finding how the firmware checks the buttons state

STM32F103 is built upon an ARM Cortex M3 CPU, that uses the ARMv7-M instruction set. ARM is quite common when you are used to embedded device reverse-engineering, so I had to choose my weapon: a well-known disassembler like IDA Pro or a new-comer like Ghidra ? I decided to use Ghidra in order to test it on a real embedded firmware.

I loaded the firmware with Ghidra, added the GPIO ports A and B memory mapping, the same for RAM, and started looking at the disassembled code. I searched for instructions reading or writing to GPIO ports A and B (at addresses 0x40010808, 0x4001080C, 0x40010C08 and 0x40010C0C). I quickly noticed many functions that perform read operations on some of these addresses, and decided to use Ghidra's decompiler to see what it's worth. Jeez, it was pretty clear code that decompiler produced !

The image below shows the decompiled code of function at address 0x080005d8:

DAT_08000d6c is the address of a DWORD containing 0x40010C08 which is the GPIOB_IDR register. Each bit of this register represents a bit of PORTB. This above routine checks if button connected to PB9 (BTN_O) is pressed, and returns true if it is, false otherwise. Let's look for cross-references:

Function at address 0x08001c7c stores a pointer to this button checking function at address 0x200000bc + 0x10, along with 6 other similar functions (evaluating states respectively for buttons X, O, square, triangle, OK, and combinations of triangle+X and square+O).
This array of pointers (starting at 0x200000bc) is used by function at address 0x08001a74 to debounce a specific button, while function at address 0x08001e76 loops on every possible buttons and debounce them one after another. This routine is called by a wrapper function at 0x080021dc, which is called by a function at 0x08001358 called by another wrapper at 0x08001350. This latter is referenced in the vector table at offset 0x3C, defined as the SysTick vector.

Basically, the badge uses a system timer to regularly check the state of every buttons, which is then stored in an array at 0x2000012c (this operation is performed by a function at 0x08001ef8).


Locating the main()

The main routine is generally called by the Reset vector, so it could be a good idea to start from there. Reset vector is stored at offset +0x4 (i.e. at address 0x08000004), so let's have a look at it:

Reset vector is a function located at address 0x08000199. This routine calls two other functions, pointed by two dwords stored respectively at address 0x080001c0 and 0x080001c4. The first one seems to perform some initialization stuff, while the second one seems more interesting. It calls a function at address 0x0800321c that contains a series of loops, which is somehow what we are expecting for a main function: it loops over and over, dealing with inputs and outputs. Moreover, there is a call to function 0x08001b40 at address 0x0800330c: this function checks the array at address 0x2000012c and therefore is used to retrieve the state of every buttons !

Function at address 0x080019dc is a delay function, looping until a specific condition is met. Function at address 0x08002474 is more cryptic, but a quick look at the possible second parameter values reveals its role: this function switches off and on the series of 8 LEDs on the badge. When the badge is powered on, these LEDs blink. This effect is indeed produced by the loop at address 0x0800323a, that calls successively this function with the first parameter to 1 and then 0, while the second one is 0xff. Again, a different LEDs combination is shown when a keypress is detected (returned by function at 0x08001b40): 0x11 when button triangle is pressed, 0x44 when button O is pressed, 0x22 when button square is pressed and 0x88 when button X is pressed. These various combinations are used to switch on the LEDs corresponding to the column of the pressed button.

Last but not least, function at address 0x08000288 takes a string as parameter and seems to send it to a debug interface, such as the UART interface we previously identified.


Finding out what the badge is expecting from us

This main function waits for a button to be pressed, and reacts accordingly. Buttons are marked as X, O, square and triangle, but as explained above these are named internally as buttons A, B, C and D. We will use the internal naming in the following explanation in order to make things easier.

When button A is pressed, value 1 is stored at address 0x20000000. When button B is pressed, value 2 is stored at the same exact address. Values 3 and 4 are respectively stored at the same address when buttons C or D are pressed. It is then clear that the badge stores the last button pressed at address 0x20000000. When the OK button is pressed, the badge checks the value at address 0x20000001: if it equals 0x00 then it calls a function at address 0x08003178 (renamed set_mode1_x in my above screenshot), or a function at address 0x080031ac otherwise. After a bit of investigation (including strings xrefs analsysis), it appears a mode value is stored at address 0x20000001: if it equals 0x00 then badge is in mode 1 and a specific function is called with the last button pressed as its only parameter, otherwise another function is called with the current mode as its parameter (so mode accepts values from 0 to 4).

Mode 1 is the mode dedicated to challenges, and function at address 0x08003178 is a simple dispatch based on the
button pressed. If we press the A button and then the OK button, we will enter the first challenge. The second one is accessed when B and then OK buttons are pressed, and so on. 4 challenges are available, each of them can be selected with the corresponding button (A,B,C or D) and a press on the OK button.


First challenge

The first challenge first displays the content of a memory area (5 bytes stored at address 0x2000003c) using the LEDs. top LEDs are used to show the 4 LSB while the bottom 4 LEDs show the MSB. It then switches off the LEDs and enters a loop. This loop expects a button to be pressed (function 0x08001b40 getting the state of every buttons pressed), and stores the corresponding value (1 for A, 2 for B, etc.) in a local array (local_30). The exit condition of this loops is the following: we need to enter 20 values and these values must match a specific sequence, the exact same sequence displayed at the beginning of this challenge with the badge's LEDs. When this sequence matches, the badge will make all the LEDs blink with a funky pattern (0x69 (01101001) and 0x96 (10010110)).

To be more specific, a series of 4 button presses define a byte as each button value is coded on 2 bits. This explains
the values are left-shifted by 2, 4 and 6. All we have to do to complete this level is to extract the bytes displayed by
the LEDs at the start of this challenge, and then enter the corresponding button presses, each button press coding
2 bits of this 5-byte sequence.
I shot a video of the start sequence in order to slow it down and analyze it with VLC, and it produced the following 5-byte sequence: 0x23 0x48 0x49 0x54 0x42. This byte sequence corresponds to the text "#HITB". We convert it into binary, and then in the corresponding buttons sequence:

Original sequence:                23       48       49       54       42
Corresponding binary sequence: 00100011 01001000 01001001 01010100 01000010
Encoded sequence:                0203     1020     1021     1110     1002

So if we press successively the following buttons, it will make the LEDs blink as we solved this first task:


second challenge

The second challenge is pretty similar to the first one: we need to press buttons to match an existing byte sequence, coded in a specific way. This time, the expected sequence is stored at address 0x20000051, and this sequence is not displayed. I decided to do it with the help of OpenOCD, with a ST-Link v2 adapter. The problem is I did not have this adapter, so I went to Haxpo's armory and met people from ExplIoT who lent me a similar hardware so I was able to debug this badge (thanks to the SWD interface I previously spotted).

By issuing the correct commands (mdw 0x20000051 and mdw 0x20000055) I was able to extract the expected sequence:

0x02 0x00 0x01 0x09 0x00 0x05 0x01 0x00 (this value corresponds to the 10th of May 2019)

This time each byte is computed from two button presses, the first one representing bits 3 and 4 while the second one represents bits 1 and 2. It is then quite straightforward to deduce the correct key presses:

bytes: 02 00 01 09 00 05 01 00
key presses: 02 00 01 21 00 11 01 00

This corresponds to the following button sequence: ACAAABCBAABBABAA

Third challenge

The third challenge is very similar to the first one, relying on the same code. Again, I used OpenOCD
to extract the expected byte sequence:

32 30 31 39 41 4D 53 ("2019AMS" in ASCII)

Each button press codes 2 bits, from MSB to LSB:

Original sequence:                32       30       31       39       41       4D       53
Corresponding binary sequence: 00110010 00110000 00110001 00111001 01000001 01001101 01010011
Encoded sequence:                0302     0300     0301     0321     1001     1031     1103

The corresponding button sequence is: ADACADAAADABADCBBAABBADBBBAD


Fourth challenge

This last challenge was a bit tricky, as it relies on system memory and especially the STM32F103 unique ID.
This ID is unique per device, so the combination of buttons I'm going to describe below only works for my
badge, and yours will be different.

Device_Serial0 = *(__IO uint32_t*)(0x1FFFF7E8);
Device_Serial1 = *(__IO uint32_t*)(0x1FFFF7EC);
Device_Serial2 = *(__IO uint32_t*)(0x1FFFF7F0);

This challenge is the same as the first one, with a 4-byte sequence corresponding to the third part of the device serial instead of a 5-byte sequence. For my badge, this value is 0x81202755 (obtained again through debugging). This value must be coded the same way, i.e. :

Original sequence:                81        20      27       55
Corresponding binary sequence: 10000001 00100000 00100111 01010101
Encoded sequence:                2001      0200    0213     1111

Pressing the following buttons make the LEDs blink: CAABACAAACBDBBBB

What about the other modes?

There are two more button combinations that can be used to modify the current mode: A+D and B+C. Pressing A+D when mode is 0 set this latter to 1, while pressing A+D when mode is 2 sets it to 3. There is the same type of trickery with B+C: if the current mode is 1 then it will set it to 2, and if it equals 3 then it will
set it to 4.

So by pressing successfully A+D, B+C, A+D, B+C, the badge enters mode 4. Modes 4 animates the LEDs and allows the user to press any button to change the animation, while mode 3 allows to change the RGB LED animation pattern by entering 12 button presses.



Well, this challenge was quite interesting even if not that difficult to break. Shame on me not to have brought the usual stuff I'm carrying when I go to Infosec conferences, I spent too much time trying to extract this damned firmware...
Anyway, it was pretty cool to test Ghidra with a real STM32F103 firmware. I have to admit that Ghidra's decompiler is pretty amazing, and helped a lot in the understanding of this embedded software !