This project revolved around recovering secret key information from a Raspberry Pi Pico by reverse engineering the firmware and rehosting it in an emulated environment using Unicorn. The goal was to brute-force the PIN without damaging the physical hardware, utilizing Ghidra for firmware analysis. Here's a detailed explanation of the reverse engineering process and why specific functions were skipped during the emulation.
The Pico transmitted UART messages when certain menu options were selected. By using Saleae Logic Analyzer and Logic 2 software, I decoded these messages at the correct baud rate (115200) and retrieved my first flag.
For SPI, I identified the MOSI, MISO, and SCLK pins and captured the communication between the Raspberry Pi Pico and another device. After decoding the message, I successfully retrieved the second flag.
Instead of brute-forcing directly on the hardware, I chose to rehost the firmware to avoid potential damage or crashes. The rehosting involved emulating the firmware in Unicorn after reverse engineering the firmware dump using Ghidra.
I started by analyzing the provided ELF binary in Ghidra. This allowed me to identify key memory addresses, the main function, and crucial function calls, including the entry point of the rehosting function.
Using Ghidra, I found the entry point at 0x10000a8e where the function assignment_2B_rehost() was called.
The rehosting function assignment_2B_rehost() is where the firmware processes the PIN input and generates the output flag. The starting address for this function is 0x10000460, and I mapped this address in Unicorn for emulation.
Certain functions like sleep_ms() were non-essential for our brute-force process. Skipping it allowed the brute-force attack to run faster without delays between iterations.
def hook_sleep_ms(uc):
uc.reg_write(UC_ARM_REG_PC, uc.reg_read(UC_ARM_REG_PC) + 4 | 1)
After setting up the memory mappings and skipping non-essential functions, I implemented a brute-force attack by iterating through all possible PINs (0-9999). Each PIN was injected using scanf(), and the result was captured using puts().
def hook_scanf(uc, user_data):
address = uc.reg_read(UC_ARM_REG_R1)
uc.mem_write(address, user_data["pin"].to_bytes(2, "little"))
uc.reg_write(UC_ARM_REG_R0, 1)
uc.reg_write(UC_ARM_REG_PC, uc.reg_read(UC_ARM_REG_PC) + 4 | 1)
def hook_puts(uc):
string_ptr = uc.reg_read(UC_ARM_REG_R0)
flag = read_string(uc, string_ptr)
if "flag{" in flag or "sshs" in flag:
print(f"Flag Found: {flag}")
test_data["flag"] = flag
test_data["found"] = True
Rehosting the firmware in Unicorn was a safer and more efficient method for performing the brute-force attack. By emulating the firmware, I avoided the risk of damaging the physical hardware or crashing the firmware due to excessive incorrect PIN entries. Moreover, by skipping non-essential functions, I optimized the brute-force process to run faster in a controlled environment.
In the end, I successfully retrieved the PIN and flag using the rehosted firmware:
PIN: 4919
Flag: sshs{8ea8549aff0b37a8d1f537c65aebfe55}