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.
Hint 1
The RDP setting is saved to non-volatile memory, unlike the earlier JTAG/SWD disable.
Hint 2
RDP is honored even in bootrom, and when booted from RAM.
Hint 3
RDP on STM32F103 specifically has been proven to be broken semi-recently.
Hint 4
The solution involves voltage glitching, along with another approach.
Solution
You need to use a recently published attack that uses a vulnerability in the ARM Flash Patch and Breakpoint peripheral to enable flash access from SRAM-booted code, and then jump into the flag printing function.
- Checkout the vuln author's PoC code.
- This attack uses the FPB to patch the flash address space to SRAM, followed by a glitch-induced power cycle that resets the flash protection bit, without clearing SRAM. We are then booted from our own SRAM code, whilst the hardware thinks it's running from flash (thus enabling flash access).
- Following the PoC's instructions, use the SWD-enabling trick from chall2 to upload the PoC shellcode into RAM using pyocd. Then do a single power glitch to reset, whilst holding BOOT0 and BOOT1 to boot from SRAM.
- Set up your serial terminal to 9600 baud (as that's the configured baud of the PoC code)
- If you reset the board using the RST button whilst holding down both BOOT pins, you'll see a simple prompt:
- Press RST once without the boot pins to enter stage 2 of the PoC shellcode.
- If the glitched power cycle was successful, you can now dump the freshly-unprotected flash contents using the `d` command:
- Save the contents into a binary file, and open it in e.g. Ghidra (ARM Cortex, 32 bit, little-endian, load adddress 0x08000000).
- (NB: at this point you can reverse, patch, and flash the firmware to the target board. Because STM32F103 does not have permanent RDP/write protection, the device manufacturer can't prevent you from flashing your own firmware. This approach is allowed. The intended flow is described below.)
- Search for all strings, finding 'chall4 flag print function starts here', and go to the function that references it. You'll find the function at `0x08000BA8`, at the time of writing. Be sure to set bit 0 of the found address to 1 to have it executed in Thumb mode (so `0x08000BA9`).
- Modify the PoC to add a new command that calls this function:
- Compile and upload the new shellcode using `pyocd` again, and execute your new command.
- Note that the flag will be printed at `115200` baud, whilst your terminal is set to `9600`. You can e.g. use the logic analyzer to view the flag contents, or write a script that quickly changes the terminal's baud after sending the new command.