CTF: Level 2 of Practical Binary Analysis challenges

Solving the CTF using minimum tools available



Introduction

The book presents a set of CTF challenges. The first one (Level 1) is solved by the author. This article shows how to solve the second challenge, using the minimum tools possible (i.e.: the tools that at that point in the book, Chapter 5, have been introduced). Using advanced tools, such as Ghidra or IDA, would probably make things much easier. But they might not be ideal for the purpose of learning.

Solution

Running lvl2 prints what appears to be a byte in hex representation. Everytime we run the elf we get output a different byte, with repetitions.

The first thing we will do is run ltrace to find out what is happening:

binary@binary-VirtualBox:~/code/chapter5$ ltrace -i ./lvl2
[0x400569] __libc_start_main(0x400500, 1, 0x7ffd9b7edeb8, 0x400640 <unfinished ...>
[0x40050b] time(0) = 1595600247
[0x400512] srand(0x5f1aed77, 0x7ffd9b7edeb8, 0x7ffd9b7edec8, 0) = 0
[0x400517] rand(0x7f12900f5620, 0x7ffd9b7edd9c, 0x7f12900f50a4, 0x7f12900f511c) = 0x61d569b5
[0x400531] puts("36"36
) = 3
[0xffffffffffffffff] +++ exited (status 0) +++

Apparently there's a puts call that gets called after some random calls. If we were to guess, time(0) is the initialization seed init for rand() routine.

Let's see from the objdump of the file what's happening:

0000000000400500 <.text>:
400500: 48 83 ec 08 sub rsp,0x8
400504: 31 ff xor edi,edi
400506: e8 c5 ff ff ff call 4004d0 <time@plt>
40050b: 89 c7 mov edi,eax
40050d: e8 ae ff ff ff call 4004c0 <srand@plt>
400512: e8 c9 ff ff ff call 4004e0 <rand@plt>
400517: 99 cdq
400518: c1 ea 1c shr edx,0x1c
40051b: 01 d0 add eax,edx
40051d: 83 e0 0f and eax,0xf
400520: 29 d0 sub eax,edx
400522: 48 98 cdqe
400524: 48 8b 3c c5 60 10 60 mov rdi,QWORD PTR [rax*8+0x601060]
40052b: 00
40052c: e8 6f ff ff ff call 4004a0 <puts@plt>
400531: 31 c0 xor eax,eax
400533: 48 83 c4 08 add rsp,0x8
400537: c3 ret
400538: 0f 1f 84 00 00 00 00 nop DWORD PTR [rax+rax*1+0x0]

Apparently it prints a random value given by 8*rax starting from 0x601060.
This address is in the .data section. We can check its content:

0000000000601040 <.data>:
...
601060: c4 (bad)
601061: 06 (bad)
601062: 40 00 00 add BYTE PTR [rax],al
601065: 00 00 add BYTE PTR [rax],al
601067: 00 c7 add bh,al
601069: 06 (bad)
60106a: 40 00 00 add BYTE PTR [rax],al
60106d: 00 00 add BYTE PTR [rax],al
60106f: 00 ca add dl,cl
601071: 06 (bad)
601072: 40 00 00 add BYTE PTR [rax],al
601075: 00 00 add BYTE PTR [rax],al
601077: 00 cd add ch,cl
601079: 06 (bad)
60107a: 40 00 00 add BYTE PTR [rax],al
60107d: 00 00 add BYTE PTR [rax],al
60107f: 00 d0 add al,dl
601081: 06 (bad)
601082: 40 00 00 add BYTE PTR [rax],al
601085: 00 00 add BYTE PTR [rax],al
601087: 00 d3 add bl,dl
601089: 06 (bad)
60108a: 40 00 00 add BYTE PTR [rax],al
60108d: 00 00 add BYTE PTR [rax],al
60108f: 00 d6 add dh,dl
601091: 06 (bad)
601092: 40 00 00 add BYTE PTR [rax],al
601095: 00 00 add BYTE PTR [rax],al
601097: 00 d9 add cl,bl
601099: 06 (bad)
60109a: 40 00 00 add BYTE PTR [rax],al
60109d: 00 00 add BYTE PTR [rax],al
60109f: 00 dc add ah,bl
6010a1: 06 (bad)
6010a2: 40 00 00 add BYTE PTR [rax],al
6010a5: 00 00 add BYTE PTR [rax],al
6010a7: 00 df add bh,bl
6010a9: 06 (bad)
6010aa: 40 00 00 add BYTE PTR [rax],al
6010ad: 00 00 add BYTE PTR [rax],al
6010af: 00 e2 add dl,ah
6010b1: 06 (bad)
6010b2: 40 00 00 add BYTE PTR [rax],al
6010b5: 00 00 add BYTE PTR [rax],al
6010b7: 00 e5 add ch,ah
6010b9: 06 (bad)
6010ba: 40 00 00 add BYTE PTR [rax],al
6010bd: 00 00 add BYTE PTR [rax],al
6010bf: 00 e8 add al,ch
6010c1: 06 (bad)
6010c2: 40 00 00 add BYTE PTR [rax],al
6010c5: 00 00 add BYTE PTR [rax],al
6010c7: 00 eb add bl,ch
6010c9: 06 (bad)
6010ca: 40 00 00 add BYTE PTR [rax],al
6010cd: 00 00 add BYTE PTR [rax],al
6010cf: 00 ee add dh,ch
6010d1: 06 (bad)
6010d2: 40 00 00 add BYTE PTR [rax],al
6010d5: 00 00 add BYTE PTR [rax],al
6010d7: 00 f1 add cl,dh
6010d9: 06 (bad)
6010da: 40 00 00 add BYTE PTR [rax],al
6010dd: 00 00 add BYTE PTR [rax],al
...


Name Type Address Offset Size EntSize Flags Link Info Align
[25] .data PROGBITS 0000000000601040 00001040 00000000000000a0 0000000000000000 WA 0 0 32

The reader should be aware that the decoded instructions do not hold any real information in this case. The first 0x20 bytes of the .data segment are not useful for us. (0x60-0x40).

Let's see how we can extract this values in an automated way.

binary@binary-VirtualBox:~/code/chapter5$ dd if=lvl2 of=lvl2_data skip=4160 count=160 bs=1

This command will get the whole .data content. Otherwise we can extract only the meaningful part, and then get 0x1c values of 8 bytes (refer to the disasm bold instruction to see where this value comes from) each:

binary@binary-VirtualBox:~/code/chapter5$ dd if=lvl2 of=lvl2_data_skip skip=$((0x1060)) count=$((0xa0 - 0x20)) bs=1
binary@binary-VirtualBox:~/code/chapter5$ dd if=lvl2_data_skip of=lvl2_magic count=$((0x1c)) bs=8
binary@binary-VirtualBox:~/code/chapter5$ xxd -c 8 -ps -e lvl2_magic
00000000: 004006c4 00000000 ..@.....
00000008: 004006c7 00000000 ..@.....
00000010: 004006ca 00000000 ..@.....
00000018: 004006cd 00000000 ..@.....
00000020: 004006d0 00000000 ..@.....
00000028: 004006d3 00000000 ..@.....
00000030: 004006d6 00000000 ..@.....
00000038: 004006d9 00000000 ..@.....
00000040: 004006dc 00000000 ..@.....
00000048: 004006df 00000000 ..@.....
00000050: 004006e2 00000000 ..@.....
00000058: 004006e5 00000000 ..@.....
00000060: 004006e8 00000000 ..@.....
00000068: 004006eb 00000000 ..@.....
00000070: 004006ee 00000000 ..@.....
00000078: 004006f1 00000000 ..@.....

This could be our flag. We can use any method to display these values as string, and considering we want to solve this using an approach as minimal as possible, a cool way is to use gdb itself display function:

(gdb) x/32bs 0x004006c4
0x4006c4: "03"
0x4006c7: "4f"
0x4006ca: "c4"
0x4006cd: "f6"
0x4006d0: "a5"
0x4006d3: "36"
0x4006d6: "f2"
0x4006d9: "bf"
0x4006dc: "74"
0x4006df: "f8"
0x4006e2: "d6"
0x4006e5: "d3"
0x4006e8: "81"
0x4006eb: "6c"
0x4006ee: "df"
0x4006f1: "88"


The binary to check the flag (provided with the book files) is called oracle. It simply takes the flag as argument.
Let's test it!

binary@binary-VirtualBox:~/code/chapter5$ ./oracle 034fc4f6a536f2bf74f8d6d3816cdf88
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
| Level 2 completed, unlocked lvl3 |
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
Run oracle with -h to show a hint

And indeed it works!