InCTF 18 Write-Ups
Reversing
Debug (993 pts, 5 solves)
Chall Description:
Difficulty Level : Easy
One of our friends found this old hex file in his Flash Folder, do you mind telling him about what the file does?
Let’s take a look at the file.
file debug.ino.hex
Output
debug.ino.hex: ASCII text, with CRLF line terminators
And taking a careful look at the file name, gives us the clue, ‘ino’, the file format for the Arduino programs. So, we now know that we’re looking at a Arduino flash file. So we have here a hex file, file to reverse. Pretty simple eh? A simple hex file reversing.
But how to get the binary out of the hex file? We first need to convert the hex file into a binary file, inorder to dissassemble it.
Since we know that most of the Arduino’s use AVR arch, let’s use it’s objcopy to convert our hex files.
avr-objcopy -v -I ihex -O binary debug.ino.hex test.bin
We now have a file test.bin.
file test.bin
test.bin: ELF 32-bit LSB executable, Atmel AVR 8-bit, version 1 (SYSV), statically linked, stripped
Stripped binary, that means no function names, which only makes the RE a tad more difficult.
strings test.bin
Pin mismatch
An interesting output, could give us a clue about the program is about.
Again to reverse this file, we can have 2 approaches :
- Statically disassemble the whole file
- Dynamically load the hex/ELF into a simulator and debug simultaneously.
Static Analysis
Let’s start looking at the static disassembly of the program. There’s good support for AVR with radare2, so let’s use radare2 to load our program.
r2 -A test.bin
Taking a look at all the functions using afl
.
0x00000000 13 3530 -> 88 entry0
0x00000118 9 70 fcn.00000118
0x0000015e 6 86 fcn.0000015e
0x0000021e 1 24 fcn.0000021e
0x00000236 3 20 fcn.00000236
0x0000024a 4 66 fcn.0000024a
0x0000035e 4 20 fcn.0000035e
0x00000372 18 82 fcn.00000372
0x000003c4 7 82 fcn.000003c4
0x00000416 9 96 fcn.00000416
0x00000476 9 120 fcn.00000476
0x000004ee 4 74 -> 64 fcn.000004ee
0x00000538 1 24 fcn.00000538
0x00000550 16 188 fcn.00000550
0x00000626 70 938 fcn.00000626
0x00000c32 3 384 fcn.00000c32
0x00000db2 1 12 fcn.00000db2
0x00000dbe 3 12 -> 14 fcn.00000dbe
We need to look for points that emulate the functions that show the printf or in our case digitalWrite, this is to ensure that the we are on the right track!
Looking at the entry point’s disassembly:
/ (fcn) entry0 88
| entry0 ();
| ; CODE XREF from fcn.0000035e (0x36c)
| 0x00000000 0c946200 jmp 0xc4 ; [06] ---- section size 62 named .shstrtab
..
||||||| ;-- aav.0x0000008a:
| ||||||| ; CODE XREF from entry0 ()
| ||||||| 0x000000c4 1124 clr r1
| ||||||| 0x000000c6 1fbe out 0x3f, r1 ; '?' ; IO SREG: flags
| ||||||| 0x000000c8 cfef ser r28
| ||||||| 0x000000ca d8e0 ldi r29, 0x08
| ||||||| 0x000000cc debf out 0x3e, r29 ; '>' ; IO SPH: Stack higher bits SP8-SP10
| ||||||| 0x000000ce cdbf out 0x3d, r28 ; '=' ; IO SPL: Stack lower bits SP0-SP7
| ||||||| 0x000000d0 11e0 ldi r17, 0x01
| ||||||| 0x000000d2 a0e0 ldi r26, 0x00
| ||||||| 0x000000d4 b1e0 ldi r27, 0x01
| ||||||| 0x000000d6 ecec ldi r30, 0xcc
| ||||||| 0x000000d8 fde0 ldi r31, 0x0d
| ========< 0x000000da 02c0 rjmp 0xe0
| ||||||| ; CODE XREF from entry0 (0xe4)
| --------> 0x000000dc 0590 lpm r0, z+
| ||||||| 0x000000de 0d92 st x+, r0
| ||||||| ; CODE XREF from entry0 (0xda)
| --------> 0x000000e0 aa34 cpi r26, 0x4a
| ||||||| 0x000000e2 b107 cpc r27, r17
| ========< 0x000000e4 d9f7 brne 0xdc
| ||||||| 0x000000e6 22e0 ldi r18, 0x02
| ||||||| 0x000000e8 aae4 ldi r26, 0x4a ; 'J'
| ||||||| 0x000000ea b1e0 ldi r27, 0x01
| ========< 0x000000ec 01c0 rjmp 0xf0
| ||||||| ; CODE XREF from entry0 (0xf4)
| --------> 0x000000ee 1d92 st x+, r1
| ||||||| ; CODE XREF from entry0 (0xec)
| --------> 0x000000f0 af38 cpi r26, 0x8f
| ||||||| 0x000000f2 b207 cpc r27, r18
| ========< 0x000000f4 e1f7 brne 0xee
| ||||||| 0x000000f6 10e0 ldi r17, 0x00
| ||||||| 0x000000f8 c2e6 ldi r28, 0x62 ; 'b'
| ||||||| 0x000000fa d0e0 ldi r29, 0x00
| ========< 0x000000fc 04c0 rjmp 0x106
| ||||||| ; CODE XREF from entry0 (0x10a)
| --------> 0x000000fe 2197 sbiw r28, 0x01
| ||||||| 0x00000100 fe01 movw r30, r28
| ||||||| 0x00000102 0e94d906 call fcn.00000db2
| ||||||| ; CODE XREF from entry0 (0xfc)
| --------> 0x00000106 c136 cpi r28, 0x61
| ||||||| 0x00000108 d107 cpc r29, r17
| ========< 0x0000010a c9f7 brne 0xfe
| ||||||| 0x0000010c 0e941906 call fcn.00000c32
| ========< 0x00000110 0c94e406 jmp 0xdc8
..
| | |:: ;-- aav.0x00000704:
| | ; CODE XREF from entry0 (0x110)
| | ; CODE XREF from fcn.00000dbe (0xdc4)
\ ------`-> 0x00000dc8 f894 cli
We can here notice that the loop function is fcn.0000c32 .
Let’s take a look at the fcn.0000c32 function.
| .-> 0x00000d9a 8191 ld r24, z+
| : 0x00000d9c 8927 eor r24, r25
| : 0x00000d9e 8d93 st x+, r24
| : 0x00000da0 81e0 ldi r24, 0x01
| : 0x00000da2 e338 cpi r30, 0x83
| : 0x00000da4 f807 cpc r31, r24
| `=< 0x00000da6 c9f7 brne 0xd9a
| ; CODE XREF from fcn.00000c32 (0xdb0)
| .-> 0x00000da8 0e941303 call fcn.00000626
| : 0x00000dac 0e94af01 call fcn.0000035e
\ `=< 0x00000db0 fbcf rjmp 0xda8
Again giving us 2 different functions to look at.
The fcn.00000626 seems like a pretty big function to reverse, let’s take the look at fcn.0000035e.
/ (fcn) fcn.0000035e 20
| fcn.0000035e ();
| ; CALL XREF from fcn.00000c32 (0xdac)
| 0x0000035e 80e0 ldi r24, 0x00
| 0x00000360 90e0 ldi r25, 0x00
| 0x00000362 892b or r24, r25
| ,=< 0x00000364 29f0 breq 0x370
| | 0x00000366 0e941b01 call fcn.00000236
| ,==< 0x0000036a 8111 cpse r24, r1
| || 0x0000036c 0c940000 jmp 0x0 ; entry0
| || ; CODE XREFS from fcn.0000035e (0x364, 0x36a)
\ ``-> 0x00000370 0895 ret
/ (fcn) fcn.00000236 20
| fcn.00000236 ();
| ; CALL XREF from fcn.0000035e (0x366)
| 0x00000236 83e8 ldi r24, 0x83
| 0x00000238 91e0 ldi r25, 0x01
| 0x0000023a 0e940f01 call fcn.0000021e
| 0x0000023e 21e0 ldi r18, 0x01
| ; DATA XREF from fcn.00000626 (0x894)
| 0x00000240 892b or r24, r25
| ,=< 0x00000242 09f4 brne 0x246
| | 0x00000244 20e0 ldi r18, 0x00
| | ; CODE XREF from fcn.00000236 (0x242)
| `-> 0x00000246 822f mov r24, r18
\ 0x00000248 0895 ret
(fcn) fcn.0000021e 24
| fcn.0000021e ();
| ; CALL XREF from fcn.00000236 (0x23a)
| 0x0000021e fc01 movw r30, r24
| ; DATA XREF from aav.0x00000704 (+0x4ac)
| 0x00000220 918d ldd r25, z+25
| ; DATA XREF from aav.0x00000704 (+0x494)
| 0x00000222 228d ldd r18, z+26
| 0x00000224 892f mov r24, r25
| ; DATA XREF from aav.0x00000704 (+0x498)
| 0x00000226 90e0 ldi r25, 0x00
| 0x00000228 805c subi r24, 0xc0
| 0x0000022a 9f4f sbci r25, 0xff
| 0x0000022c 821b sub r24, r18
| 0x0000022e 9109 sbc r25, r1
| 0x00000230 8f73 andi r24, 0x3f ; '?'
| 0x00000232 9927 clr r25
\ 0x00000234 0895 ret
Which seems to load some values and call other functions, possibly a part of serial_write function.
Let’s take a look at the fcn.00000626 , which seems like a probable canditate for the major loop function.
The function is very big, therefore I would only be putting the CFG ;)
---------------------------------------------------------------------------.
|
.--------------------.
| fcn.00000626 |
`--------------------'
v
|
.--------------------------------------------------------------------|
| .------------------------------------------|
| .------------------------------------------|
| | .----------------|
| | | |--------.
| | .----------------| |
| .------------------------------------------| |
| | | |----------------------------------.
.--------------------------------------------------------------------| | |
| | | |------------------------------------------------------------.
| | | |--------------------------------------------------------------------------------------.
| | | |--------------------------------------------------------------------------------------.
---------------------------------------------------------------------------' | | | |
| | | '------------------------------------------------------------. |
| | | | | | |
.--------------------. .--------------------. .--------------------. .--------------------. .--------------------. .--------------------. .--------------------.
| fcn.00000538 | | fcn.00000476 | | fcn.00000416 | | fcn.000003c4 | | fcn.00000550 | | fcn.0000015e | | fcn.000004ee |
`--------------------' `--------------------' `--------------------' `--------------------' `--------------------' `--------------------' `--------------------'
As I mentioned eariler will have to make sure that we find out the function responsible for digital writing data. And since this seems the function running in the loop, we must be able to find the write function amongnst the above functions. fcn.000003c4 and fcn.00000416 seems like possible candidates for the write function. But fcn.00000416 seems to be the function as it has lot of instructions loading memories and writing it to various modes.
There for we need to make sure that we see that the conditions that are compared.
Taking a look at the whole function, we can arrive at the following psuedo-code.
int n=0;
for(n=0;n<4;)
{
char input = Key.getKey();
if(input)
{
Serial.print(input);
if(input!=in[n])
{
Serial.print(??);
delay(1200);
fucn(); //The func we are reversing
}
inputer[n]=input;
n++;
}
for (int i = 0; i < 4; i++)
input_intg = 10 * input_intg + inputer[i];
Serial.print(??FLAG??);
}
Which then causes use to wonder how to find out the required input? One could either check for the assmebly to find out the PIN and then follow the function, and possibly follow to find the code.
But still how to emulate the ELF file? Not everyone has a Arduino at home :(
Dynamic Analysis
That’s where simavr comes handy, since the author had a example case ready for the Arduino Uno ready. (Wondering where we got Uno from, check the strings on the file again) Steps to emulate:
- Compile the program. Follow the instructions in the README.md
- Take a look at the examples/board_simduino directory.
- Again compile the program.
- Run
./simduino.elf
read_ihex_chunks: ATmegaBOOT_168_atmega328.ihex, unsupported check type 03
atmega328p booloader 0x07800: 1950 bytes
avr_special_init
uart_pty_init bridge on port *** /dev/pts/3 ***
uart_pty_connect: /tmp/simavr-uart0 now points to /dev/pts/3
note: export SIMAVR_UART_XTERM=1 and install picocom to get a terminal
- Then run
avrdude -p m328p -c arduino -P /dev/pts/?? -U flash:w:debug.ino.hex
avrdude: AVR device initialized and ready to accept instructions
Reading | ################################################## | 100% 0.00s
avrdude: Device signature = 0x1e950f (probably m328p)
avrdude: NOTE: "flash" memory has been specified, an erase cycle will be performed
To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: reading input file "debug.ino.hex"
avrdude: input file debug.ino.hex auto detected as Intel Hex
avrdude: writing flash (4180 bytes):
Writing | ################################################## | 100% 0.14s
avrdude: 4180 bytes of flash written
avrdude: verifying flash memory against debug.ino.hex:
avrdude: load data flash data from input file debug.ino.hex:
avrdude: input file debug.ino.hex auto detected as Intel Hex
avrdude: input file debug.ino.hex contains 4180 bytes
avrdude: reading on-chip flash data:
Reading | ################################################## | 100% 0.28s
avrdude: verifying ...
avrdude: 4180 bytes of flash verified
avrdude: safemode: Fuses OK (E:00, H:00, L:00)
ioctl("TIOCMGET"): Invalid argument
avrdude done. Thank you.
- And for serial communication
picocom /tmp/simavr-uart0
- To remotely debug the program using a GDB stub
simavr -g -m atmega328p test.bin
And then use avr-gdb
to connect.
Using any of the above methods you can find out that the PIN was 7331 and the Flag : InCTF{4Vr_!sNt_B4D}
If you found the challenge difficult,you could try the unstripped binary for starters :) ELF