This will contain spoilers for the Level 1 challenge board for use with the curious bolt. you have been warned. So firstly what is the “Level 1 Challenge Board”? It is a STM32 based set of 4 challenges to familiarize yourself with glitching attacks, specifically for using the “curious bolt” to perform these attacks.
Scripts that I created or used will be available in the following git repo: https://rossmarks.uk/git/0xRoM/Hardware
Specifically in the following folder: “/FaultInjection/examples/CuriousBolt/Level-1/”.
The CTF board looks like:
My setup for completing this (curious bolt on the left):
Challenge 1
In this challenge, the flag gets loaded into RAM in plaintext. There should be no way to extract it, as JTAG and SWD have been disabled.
Setup
Notes
STM32F1 series have a design vulnerability, where the debugging peripheral can only be disabled in software, and not through hardware fuse bits. This means that RAM is still accessible when an attacker can force booting from bootrom or SRAM.
Because RAM is not cleared upon reset, the flag is still there, and it can be obtained by dumping RAM.
Solution
First access screen:
screen /dev/ttyUSB0 115200
Press “challenge 1 button” to load values into memory.
Press and hold “boot” button, then press “reset” button.
Dump the memory with the following:
$> pyocd commander 0004169:WARNING:board:Generic 'cortex_m' target type is selected; is this intentional? You will be able to debug but not program flash. To set the target type use the '--target' argument or 'target_override' option. Use 'pyocd list --targets' to see available targets types. Connected to CoreSightTarget [Halted]: 39002200172D343632525544 >>> savemem 0x20000000 20480 sram.dump Saved 20480 bytes to sram.dump >>> $> strings sram.dump | grep 'ctf' ctf{1139BE4A97D2172E6E39B71EFAD4B30648}
Challenge 2
In this chall, the flag is protected by a check that is never false:
volatile bool check = true; uint32_t cnt = 0; int i = 0; int j; while (true) { cnt = 0; for (i = 0; i < 1000; i++) { for (j = 0; j < 1000; j++) { cnt++; if (!check) { // Flag gets printed } } } uart_printf("%u %u %u\r\n", i, j, cnt); }
Setup
Notes
This seems to be the “hello world” of hardware glitching. Having done this a few times now I decided to write a small script to automate it. I called it “glitch-o-matic” (it can be found in the github repo) however when running it it couldn’t get a working glitch! Advice in the “curious supplies” discord was to bend the wires or try different wires, perhaps hold the button as that causes a little power draw. That was the solution for me. Wanting to understand what was going on out came the scope:
(Top) Not holding the button, (Bottom) Holding the button.
The difference is minimal, but when holding the button the power is off for slightly less time.
Now when holding the button and starting the glitch-o-matic:
___ _ _ _ _ _ _ / __| (_) |_ __| |_ ___ ___ ___ _ __ __ _| |_(_)__ | (_ | | | _/ _| ' \___/ _ \___| ' \/ _` | _| / _| \___|_|_|\__\__|_||_| \___/ |_|_|_\__,_|\__|_\__| ╒══════════════╤═══════════╤═════════╤════════════════╕ │ Glitch Len │ Repeats │ Delay │ Elapsed Time │ ╞══════════════╪═══════════╪═════════╪════════════════╡ │ 41 / 43 │ 9 / 30 │ 0 / 30 │ 0d 0h 2m 21s │ ╘══════════════╧═══════════╧═════════╧════════════════╛ [INFO] Received data: 1000 1000 1000000 [INFO] Received data: 1000 1000 1000000 [INFO] Received data: 1000 1000 1000000 [INFO] Received data: 1000 1000 1000000 [INFO] Received data: 1000 1000 1000000 [INFO] Received data: Unreachable point reached, proceeding to print flag: [INFO] Incrementing trigger repeats: 9 [INFO] Received data: ctf{6F48C055674E751B4A444A3328C00C93DB} [INFO] Flag: ctf{6F48C055674E751B4A444A3328C00C93DB} [WARNING] Received 'ctf', exiting...
Interlude
The script worked so well it was at this point I decided I would write another script to make using the curious bolt and performing glitching attacks in the future easier, so I came up with a quick design doc:
I know, that looks crazy useful! so I made it! you can get it here: https://rossmarks.uk/git/0xRoM/glitch-o-bolt. the main idea being you create a config file to define how you want the program to run, the offests for values, the trigger settings, what words to watch for and actions to perform when those words are seen.
Once I had created this it was time for the next challenge, using the next couple of challenges to help refine the app.
Challenge 3
In this challenge, the flag gets printed only when certain bits in external EEPROM memory are cleared (equal to 0).
Simply changing the bits on the EEPROM chip does not work, as an integrity check ensures that data on the chip has not been modified.
Setup
A quick overview of this: The green wire is using the RX line (which is always high) to push the challenge button so even if the device is glitched to reset it auto starts the correct challenge.
The orange line Also goes from the RX to the bolt’s glitching pin, the purple wire also on that line goes to an input pin on the bolt to monitor it.
The SDA pins on the challenge board are also wired up to the input pins on the bolt for monitoring.
Notes
It is now possible to use pulseview to see the SDA data and the glitching line. (This will not work on a raspberry pi which at time of writing has an older version of pulseview that doesnt have the correct “SUMP compatible” driver). By setting the SDA line as a falling edge trigger, when the line drops at X time offset perform a glitch for Y duration. This can be monitored in pulseview. The following screenshot demonstrates that with pulsview on top and glitch-o-bolt underneath reading the UART output.
I Used the following to work out the timings and offsets:
Once the offset and duration have been identified rewire it to drop the SDA line instead of the power, which was only in use for something visual to use.
Then run the glitch as demonstrated below.
The top pulseview was from when the power was still wired to glitch, the glitch-o-bolt was run after re-wiring the glitch pin to SDA on the challenge device.
Challenge 4
In this challenge, the flag is printed by a function that never gets called. Readout protection (RDP) is active, meaning the flash content can’t be dumped, even from JTAG/SWD, the bootloader, or when booted from SRAM.
Setup
Part 1 – Pulling the firmware
To pull the firmware we have to use a recently published attack that uses a vulnerability to enable flash access from SRAM-booted code, and then jump into the flag printing function.
This sounds more complicated than it is!
Download the PoC from: https://github.com/JohannesObermaier/f103-analysis/tree/master/h3 This contains the precompiled exploit we want to put into SRAM (rootshell/shellcode.bin)
Use the trick from challenge 1 to upload the PoC shellcode into RAM using pyocd. (>>>load shellcode.bin 0x20000000) Then do a single power glitch to reset, whilst holding BOOT0 and BOOT1 to boot from SRAM.
Connect to the device’s UART port with BAUD 9600. Once connected push “d” to dump the flash contents. I used the logging feature of glitch-o-bolt to to write this to a file. (Chall-4_UART.log in the git repo)
Part 2 – Reversing the firmware
I wrote a small script to tidy this log file (tidy_log.py). Open the output of this in ghidra, find all of the strings to work out the function we want to call, take note of it’s memory address.
ARM CPUs can operate in two instruction sets:
- ARM mode: 32-bit instructions
- Thumb mode: 16-bit instructions (smaller, more efficient)
The Cortex-M3 in STM32F103 only supports Thumb mode, not full ARM mode.
Ghidra reverses in ARM mode, So the address needs to be converted into thumb mode which the device runs.
To get the actual thumb address, you simply clear bit 0.
If:
– ARM address = 0x08000BA8
Then:
– Thumb address = 0x08000BA9 (bit 0 cleared)
Now simply patch in some code to the previously used exploit to jump to this memory address. The device still runs on baud rate 115200 so I would recommend adding a delay to give yourself time to switch to the different speed.
The code I used to patch was:
void readCmd( uint8_t const * const cmd ) { void (*chall4)(void) = (void (*)(void))0x08000ba9; switch (cmd[0]) { case 0: return; break; /* chall4 function */ case 'p': case 'P': writeStr("time to change baudrate to 115200\r\n"); for (volatile uint32_t i = 0; i < 5000000; i++); // Crude delay chall4(); break;
My firmware with patches is in the git in the “Chall-4-Firmware” folder. It also has the compiled version “Shellcode-0xRoM.bin” if you just want to use that.
Part 3 – Upload and run
With the new firmware compiled, follow the same steps as “Part 1” to get it running on the device and press the “p” key to pwn. Naturally I created a config for glitch-o-bolt to make this simpler, with steps to follow and guide the process. It ended up looking like:
Extras
Wanting people to use glitch-o-bolt I created some more configs to demonstrate it’s use. The first one I made was one to solve “Challenge 2” to bruteforce the offsets:
The next one was to brute force the baud rate for unknown UART terminals:
Conclusion
The curious bolt is an incredibly useful tool, and I’ve certainly got the glitching bug. It’s so much fun! and the challenge board to learn how to use it was a great tool to learn just that. I would 100% recommend this kit to anyone interested in hardware hacking that wants to take the next step into voltage glitching.
Massive thank you to Aleph, for motivating me and helping. Also thanks to Tom for creating this and being patient answering my dumb questions, if anyone else does this I recommend joining the “curious supplies” discord as theres lots of help and tips there.