NcN 2013 CTF canada write up

In this post I will cover the second binary challenge of the No Con Name 2013 CTF driven by the Facebook security team. The binary is available here

This binary challenge is based on a i386 stripped elf file which prompts for a flag:

1
2
3
4
5
6
7
$ file ./howtobasic
./howtobasic: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.26, BuildID[sha1]=4f288f1a66ad673dc50b51c7e85635358bb11da0, stripped
$ ./howtobasic
Facebook CTF
Enter flag: asdasdasdasd
Sorry, that is not correct.
$

As this binary is stripped, we need to first locate the entrypoint with the info file gdb command

1
$ gdb -q ./ca_howtobasic

Reading symbols from /home/libcrack/Desktop/NcN2013-CTF/canada/ca_howtobasic…(no debugging symbols found)…done.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
gdb-peda$ info file
Symbols from "/home/libcrack/Desktop/NcN2013-CTF/canada/ca_howtobasic".
Local exec file:
/home/libcrack/Desktop/NcN2013-CTF/canada/ca_howtobasic', file type elf32-i386.
Entry point: 0x80481c0
0x080480d4 - 0x080480f4 is .note.ABI-tag
0x080480f4 - 0x08048118 is .note.gnu.build-id
0x08048118 - 0x08048140 is .rel.plt
0x08048140 - 0x08048166 is .init
0x08048170 - 0x080481c0 is .plt
0x080481c0 - 0x080b16ec is .text
0x080b16f0 - 0x080b21b1 is __libc_freeres_fn
0x080b21b4 - 0x080b21cb is .fini
0x080b21e0 - 0x080cb138 is .rodata
0x080cb138 - 0x080cb13c is __libc_atexit
0x080cb13c - 0x080cb168 is __libc_subfreeres
0x080cb168 - 0x080d0c3c is .eh_frame
0x080d0c3c - 0x080d0d6b is .gcc_except_table
0x080d1000 - 0x080d1010 is .tdata
0x080d1010 - 0x080d1028 is .tbss
0x080d1010 - 0x080d1018 is .init_array
0x080d1018 - 0x080d1020 is .fini_array
0x080d1020 - 0x080d1024 is .jcr
0x080d1024 - 0x080d1054 is .data.rel.ro
0x080d1054 - 0x080d105c is .got
0x080d105c - 0x080d107c is .got.plt
0x080d1080 - 0x080d17e0 is .data
0x080d17e0 - 0x080d3374 is .bss
0x080d3374 - 0x080d338c is __libc_freeres_ptrs
gdb-peda$

The gdb finds the program entry point: 0x80481c0. Because the binary is stripped, we cannot use the gdb command disassemble, as gdb does not know the function boundaries. To find it out it is neccesary to examine the code using the program counter register eip as reference. First, we need to set a breakpoint at the discovered program entry point:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
gdb-peda$ 
gdb-peda$ break *0x80481c0
Breakpoint 1 at 0x80481c0
After that, the instructions can be examined by using the program counter as offset:

gdb-peda$ x/20i $pc
=> 0x80481c0: xor ebp,ebp
0x80481c2: pop esi
0x80481c3: mov ecx,esp
0x80481c5: and esp,0xfffffff0
0x80481c8: push eax
0x80481c9: push esp
0x80481ca: push edx
0x80481cb: push 0x8048b00
0x80481d0: push 0x8048b40
0x80481d5: push ecx
0x80481d6: push esi
0x80481d7: push 0x80482d4
0x80481dc: call 0x80484c0
0x80481e1: hlt
0x80481e2: nop
0x80481e3: nop
0x80481e4: nop
0x80481e5: nop
0x80481e6: nop
0x80481e7: nop

At 0x80481dc the program calls __libc_start_main. Before that, it sets the arguments to be passed to __libc_start_main in the stack (as the libc i386 calling convention specifies).

1
2
3
4
5
6
7
8
9
0x80481c8:	push   eax
0x80481c9: push esp
0x80481ca: push edx
0x80481cb: push 0x8048b00
0x80481d0: push 0x8048b40
0x80481d5: push ecx
0x80481d6: push esi
0x80481d7: push 0x80482d4
0x80481dc: call 0x80484c0

By taking a look at __libc_start_main, the arguments can be guessed:

1
2
3
4
5
6
7
8
9
int __libc_start_main(
int (*main) (int, char * *, char * *),
int argc,
char * * ubp_av,
void (*init) (void),
void (*fini) (void),
void (*rtld_fini) (void),
void (* stack_end)
);

After stablishing the program entry point, we need to locate where the important code resides. For that, several searches should be launched inside the binary.For example, the strings that the program uses for input is a good start:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
gdb-peda$ refsearch Enter
Searching for reference to: 'Enter' in: all ranges
Found 5 results, display max 5 items:
howtobasic : 0x8048318 (and cl,BYTE PTR ss:[ebx])
[stack] : 0xffffd2fc --> 0x80b2236 ("Enter flag: ")
[stack] : 0xffffd424 --> 0x80b2236 ("Enter flag: ")
[stack] : 0xffffd8c0 --> 0x80b2236 ("Enter flag: ")
[stack] : 0xffffd8d0 --> 0x80b2236 ("Enter flag: ")
gdb-peda$
It’s also important to checkout the syscalls:

gdb-peda$ asmsearch "int 0x80" all
Searching for ASM code: 'int 0x80' in: all ranges
0x080489dd : (cd80) int 0x80
0x0805763c : (cd80) int 0x80
0x08058656 : (cd80) int 0x80
0x08058d40 : (cd80) int 0x80
0x08077126 : (cd80) int 0x80
0x0807a91f : (cd80) int 0x80
0x0808b0b5 : (cd80) int 0x80
0x0808b0be : (cd80) int 0x80
0x080ad93c : (cd80) int 0x80
0x080ae5c2 : (cd80) int 0x80
0x080ae6de : (cd80) int 0x80
0xf7ffd406 : (cd80) int 0x80
0xf7ffd415 : (cd80) int 0x80
0xf7ffd42e : (cd80) int 0x80
gdb-peda$

This binary look similiar to the first NcN CTF binary challenge. By looking for xor instructions, It can be noticed the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
gdb-peda$ x/51i 0x80483fd
0x80483fd: pop eax
0x80483fe: jmp 0x8048486
0x8048403: mov eax,DWORD PTR [esp+0x1c]
0x8048407: and eax,0x7
0x804840a: movzx eax,BYTE PTR [eax+0x80d108c]
0x8048411: not eax
0x8048413: mov BYTE PTR [esp+0x1b],al
0x8048417: mov eax,DWORD PTR [esp+0x1c]
0x804841b: mov edx,DWORD PTR [esp+0x10]
0x804841f: add eax,edx
0x8048421: movzx eax,BYTE PTR [eax]
0x8048424: not eax
0x8048426: mov BYTE PTR [esp+0x1a],al
0x804842a: mov edx,DWORD PTR ds:0x80d1088
0x8048430: mov eax,DWORD PTR [esp+0x1c]
0x8048434: add eax,edx
0x8048436: movzx edx,BYTE PTR [eax]
0x8048439: movzx eax,BYTE PTR [esp+0x1a]
0x804843e: movzx ecx,BYTE PTR [esp+0x1b]
0x8048443: xor eax,ecx
0x8048445: cmp dl,al
0x8048447: je 0x8048481
0x8048449: mov eax,ds:0x80d14c4
0x804844e: mov DWORD PTR [esp+0xc],eax
0x8048452: mov DWORD PTR [esp+0x8],0x1c
0x804845a: mov DWORD PTR [esp+0x4],0x1
0x8048462: mov DWORD PTR [esp],0x80b227b
0x8048469: call 0x8049140
0x804846e: mov eax,DWORD PTR [esp+0x10]
0x8048472: mov DWORD PTR [esp],eax
0x8048475: call 0x804fef0
0x804847a: mov eax,0x0
0x804847f: jmp 0x80484bd
0x8048481: add DWORD PTR [esp+0x1c],0x1
0x8048486: mov eax,DWORD PTR [esp+0x14]
0x804848a: sub eax,0x2
0x804848d: cmp eax,DWORD PTR [esp+0x1c]
0x8048491: ja 0x8048403
0x8048497: mov DWORD PTR [esp],0x80b2298
0x804849e: call 0x8049440
0x80484a3: push eax
0x80484a4: xor eax,eax
0x80484a6: je 0x80484ab
0x80484a8: jne 0x80484ab
0x80484aa: call 0x2c491007
0x80484af: adc BYTE PTR [ecx+0x38e82404],cl
0x80484b5: jp 0x80484b7
0x80484b7: add BYTE PTR [eax+0x0],bh
0x80484bd: leave
0x80484be: ret
0x80484bf: nop
gdb-peda$

Here, the values or eax and ecx get xored and compared to edx. The only difference in this challenge is that the value stored in eax is the negated value of the user input string. The challenge can be resolved using the following GDB script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
break *0x8048443
commands
silent
printf "%c", (($edx ^ $ecx)*-1)-1
continue
end
break *0x8048445
commands
silent
set $eax = $edx
continue
end
run
quit

Lets try it!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ gdb -q -x howtobasic.gdb ./howtobasic
Reading symbols from /home/libcrack/Desktop/NcN2013-CTF/canada/howtobasic...(no debugging symbols found)...done.
Breakpoint 1 at 0x8048443
Breakpoint 2 at 0x8048445
Facebook CTF
Enter flag: uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
60115893a79735aec54ed5ea91fbdbf0ab192e5eea24956fc29fed38466af9a2Winner! Post your flag.
[Inferior 1 (process 19342) exited normally]
Warning: not running or target is remote
$
$ ./howtobasic
Facebook CTF
Enter flag: 60115893a79735aec54ed5ea91fbdbf0ab192e5eea24956fc29fed38466af9a2
Winner! Post your flag.
$

ref

libcrack