Swiss Hacking Challenge 2023 - ezrev
Information
Challenge category: re
Challenge Description
Since we’re having so much fun together, have some low level microcontroller stuff. Maybe have a look at the ATmega328P Datasheet. (PS: yeah the eeprom.txt dump is kinda fake but be happy it is)
Files
We are given an ezrev.zip
archive. It contains the following files:
eeprom.txt
: An EEPROM dump of the first 64 valuesEZRev.hex
: An atmega328p firmware dump
Exploitation
It turns out that there are actually two ways of solving this challenge. One is by just flashing the firmware and EEPROM correctly and then reading the flag via UART with the right baudrate. The other one involves just reading the disassembly.
The hardware way
I had an atmega328p
board (Arduino Nano) laying arround and tried this way first. We need to do several steps before being able to read the flag:
Flashing the EEPROM
I’ve crated the following arduino program to write the values from eeprom.txt
into the EEPROM and read them again to verify that they’re correct:
#include <EEPROM.h>
void setup() {
// Serial output
Serial.begin(9600);
// eeprom.txt
int eepromValues[] = {84, 115, 125, 85, 104, 123, 61, 39, 91, 69, 88, 48, 37, 70, 79, 77, 47, 122, 107, 83, 41, 82, 51, 82, 36, 93, 122, 107, 75, 110, 62, 124, 50, 99, 59, 68, 78, 78, 73, 37, 38, 115, 80, 125, 80, 81, 73, 102, 38, 104, 39, 74, 50, 66, 105, 77, 85, 56, 68, 40, 51, 85, 62, 49};
// Loop through every value and set it, log the set value to serial
for (int i = 0; i < 64; i++) {
EEPROM.write(i, eepromValues[i]);
Serial.print(eepromValues[i]);
Serial.print(" ");
}
Serial.println(" ");
// Read the EEPROM and dump it to serial
for (int i = 0; i < 64; i++) {
int value = EEPROM.read(i);
Serial.print(value);
Serial.print(" ");
}
}
void loop() {}
The serial console should print the exact contents of eeprom.txt
twice.
Flashing the EZRev.hex
file
In a video from LiveOverflow I learned how to flash the binary onto the device:
avrdude -c arduino -p atmega328p -P /dev/ttyUSB0 -b115200 -u -V -U flash:w:EZRev.hex
Note: Chinese Arduino Nano clones use an older bootloader with a lower baudrate and thus need -b57600
Calculating the baudrate
I hooked up a logic analyzer to the TX
pin of the Arduino and got the following output:
We can see that the smallest signal gap is 3.652 ms
. To calculate the baudrate we do the following:
>>> math.floor(1000 / 3.652)
273
Reading the flag
I then just used the async serial analyzer in Logic and used the calculated baudrate:
The assembly way
Afterwards I looked at the file in ghidra again. I won’t go into how everything works but below is how the flag is hidden:
- The entire flag is in the
eeprom.txt
, even in plaintext. - However every number is a letter and the numbers are randomly scrambled.
- In the assembly code we can identify the code where the EEPROM reads happen to reverse engineer the flag.
The following part of the assembly is relevant:
LAB_code_0002fb
code:0002fb 81 e0 ldi R24,0x1
code:0002fc 90 e0 ldi R25,0x0
code:0002fd 0e 94 04 02 call read_eeprom
code:0002ff 0e 94 ba 01 call serial_print
code:000301 84 e0 ldi R24,0x4
code:000302 90 e0 ldi R25,0x0
code:000303 0e 94 04 02 call read_eeprom
code:000305 0e 94 ba 01 call serial_print
code:000307 81 e2 ldi R24,0x21
code:000308 90 e0 ldi R25,0x0
code:000309 0e 94 04 02 call read_eeprom
code:00030b 0e 94 ba 01 call serial_print
code:00030d 84 e3 ldi R24,0x34
code:00030e 90 e0 ldi R25,0x0
code:00030f 0e 94 04 02 call read_eeprom
code:000311 0e 94 ba 01 call serial_print
code:000313 8b e0 ldi R24,0xb
code:000314 90 e0 ldi R25,0x0
code:000315 0e 94 04 02 call read_eeprom
code:000317 0e 94 ba 01 call serial_print
code:000319 80 e2 ldi R24,0x20
code:00031a 90 e0 ldi R25,0x0
code:00031b 0e 94 04 02 call read_eeprom
code:00031d 0e 94 ba 01 call serial_print
code:00031f 86 e1 ldi R24,0x16
code:000320 90 e0 ldi R25,0x0
code:000321 0e 94 04 02 call read_eeprom
code:000323 0e 94 ba 01 call serial_print
code:000325 85 e0 ldi R24,0x5
code:000326 90 e0 ldi R25,0x0
; This goes on for some time
The first ldi
instruction for R25
specifies the EEPROM address to load. That means we can just read the right elements from the eeprom.txt
and convert the integers to letters. I did this in the following example for the first 7 characters (shc2023
):
data = open("eeprom.txt", "r").read().split(" ")
key_order = [0x1, 0x4, 0x21, 0x34, 0xb, 0x20, 0x16]
for key in key_order:
print(chr(int(data[key])), end="")
This successfully prints shc2023
and you could do this for the rest of the flag as well. As it’s less than 24h before SHC ends at the time of writing I’ll be skipping this.
Flag
The flag is shc2023{EEPROMFUUN}
Conclusion
I spent a lot of time with the assembly code in advance without knowing how it worked. After finally figuring out that the serial console does work but just at a very low baud rate the challenge got a lot easier.