NcN 2013 CTF australia bin write up

In this post I will cover the first 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 elf file which prompts for a flag:

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

The first look at the binary:

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
$ gdb -q ./derp 
Reading symbols from /home/libcrack/Desktop/NcN2013-CTF/derp...(no debugging symbols found)...done.
gdb-peda$ pdisass main
Dump of assembler code for function main:
0x080482d4 <+0>: push ebp
0x080482d5 <+1>: mov ebp,esp
0x080482d7 <+3>: and esp,0xfffffff0
0x080482da <+6>: sub esp,0x20
0x080482dd <+9>: call 0x80483fa <print_header>
0x080482e2 <+14>: mov eax,ds:0x80d1088
0x080482e7 <+19>: mov DWORD PTR [esp],eax
0x080482ea <+22>: call 0x804ffa0 <malloc>
0x080482ef <+27>: mov DWORD PTR [esp+0x1c],eax
0x080482f3 <+31>: cmp DWORD PTR [esp+0x1c],0x0
0x080482f8 <+36>: jne 0x8048329 <main+85>
0x080482fa <+38>: mov eax,ds:0x80d14c4
0x080482ff <+43>: mov DWORD PTR [esp+0xc],eax
0x08048303 <+47>: mov DWORD PTR [esp+0x8],0x1b
0x0804830b <+55>: mov DWORD PTR [esp+0x4],0x1
0x08048313 <+63>: mov DWORD PTR [esp],0x80b2265
0x0804831a <+70>: call 0x8049130 <fwrite>
0x0804831f <+75>: mov eax,0x1
0x08048324 <+80>: jmp 0x80483f8 <main+292>
0x08048329 <+85>: mov eax,ds:0x80d1088
0x0804832e <+90>: mov DWORD PTR [esp+0x8],eax
0x08048332 <+94>: mov DWORD PTR [esp+0x4],0x0
0x0804833a <+102>: mov eax,DWORD PTR [esp+0x1c]
0x0804833e <+106>: mov DWORD PTR [esp],eax
0x08048341 <+109>: call 0x80481a0
0x08048346 <+114>: mov edx,DWORD PTR ds:0x80d14bc
0x0804834c <+120>: mov eax,ds:0x80d1088
0x08048351 <+125>: sub eax,0x1
0x08048354 <+128>: mov DWORD PTR [esp+0x8],edx
0x08048358 <+132>: mov DWORD PTR [esp+0x4],eax
0x0804835c <+136>: mov eax,DWORD PTR [esp+0x1c]
0x08048360 <+140>: mov DWORD PTR [esp],eax
0x08048363 <+143>: call 0x8048fc0 <fgets>
0x08048368 <+148>: test eax,eax
0x0804836a <+150>: jne 0x80483a4 <main+208>
0x0804836c <+152>: mov eax,ds:0x80d14c4
0x08048371 <+157>: mov DWORD PTR [esp+0xc],eax
0x08048375 <+161>: mov DWORD PTR [esp+0x8],0x1b
0x0804837d <+169>: mov DWORD PTR [esp+0x4],0x1
0x08048385 <+177>: mov DWORD PTR [esp],0x80b2281
0x0804838c <+184>: call 0x8049130 <fwrite>
0x08048391 <+189>: mov eax,DWORD PTR [esp+0x1c]
0x08048395 <+193>: mov DWORD PTR [esp],eax
0x08048398 <+196>: call 0x804fee0 <free>
0x0804839d <+201>: mov eax,0x2
0x080483a2 <+206>: jmp 0x80483f8 <main+292>
0x080483a4 <+208>: mov eax,ds:0x80d1088
0x080483a9 <+213>: sub eax,0x2
0x080483ac <+216>: mov DWORD PTR [esp+0x4],eax
0x080483b0 <+220>: mov eax,DWORD PTR [esp+0x1c]
0x080483b4 <+224>: mov DWORD PTR [esp],eax
0x080483b7 <+227>: call 0x804841a <check_buffer>
0x080483bc <+232>: test eax,eax
0x080483be <+234>: jne 0x80483e7 <main+275>
0x080483c0 <+236>: mov eax,ds:0x80d14c4
0x080483c5 <+241>: mov DWORD PTR [esp+0xc],eax
0x080483c9 <+245>: mov DWORD PTR [esp+0x8],0x1c
0x080483d1 <+253>: mov DWORD PTR [esp+0x4],0x1
0x080483d9 <+261>: mov DWORD PTR [esp],0x80b229d
0x080483e0 <+268>: call 0x8049130 <fwrite>
0x080483e5 <+273>: jmp 0x80483f3 <main+287>
0x080483e7 <+275>: mov DWORD PTR [esp],0x80b22ba
0x080483ee <+282>: call 0x8049430 <puts>
0x080483f3 <+287>: mov eax,0x0
0x080483f8 <+292>: leave
0x080483f9 <+293>: ret
End of assembler dump.
gdb-peda$

The call to the function that will check the input buffer is located at 0x080483b7

1
0x080483b7 <+227>:	call   0x804841a <check_buffer>

Let’s what the function check_buffer does:

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
gdb-peda$ pdisass check_buffer
Dump of assembler code for function check_buffer:
0x0804841a <+0>: push ebp
0x0804841b <+1>: mov ebp,esp
0x0804841d <+3>: sub esp,0x10
0x08048420 <+6>: mov BYTE PTR [ebp-0x5],0x0
0x08048424 <+10>: mov BYTE PTR [ebp-0x6],0x0
0x08048428 <+14>: mov BYTE PTR [ebp-0x7],0x0
0x0804842c <+18>: mov DWORD PTR [ebp-0x4],0x0
0x08048433 <+25>: mov DWORD PTR [ebp-0xc],0xcd000000
0x0804843a <+32>: jmp 0x804849c <check_buffer+130>
0x0804843c <+34>: mov eax,DWORD PTR [ebp-0xc]
0x0804843f <+37>: shr eax,0x18
0x08048442 <+40>: mov BYTE PTR [ebp-0x7],al
0x08048445 <+43>: movzx eax,BYTE PTR [ebp-0x7]
0x08048449 <+47>: and eax,0xf
0x0804844c <+50>: mov BYTE PTR [ebp-0x5],al
0x0804844f <+53>: movzx eax,BYTE PTR [ebp-0x7]
0x08048453 <+57>: and eax,0xfffffff0
0x08048456 <+60>: mov BYTE PTR [ebp-0x6],al
0x08048459 <+63>: movzx eax,BYTE PTR [ebp-0x6]
0x0804845d <+67>: mov edx,eax
0x0804845f <+69>: shr dl,0x4
0x08048462 <+72>: movzx eax,BYTE PTR [ebp-0x5]
0x08048466 <+76>: shl eax,0x4
0x08048469 <+79>: add eax,edx
0x0804846b <+81>: mov BYTE PTR [ebp-0x7],al
0x0804846e <+84>: mov edx,DWORD PTR ds:0x80d1090
0x08048474 <+90>: mov eax,DWORD PTR [ebp-0x4]
0x08048477 <+93>: add eax,edx
0x08048479 <+95>: movzx edx,BYTE PTR [eax]
0x0804847c <+98>: mov eax,DWORD PTR [ebp-0x4]
0x0804847f <+101>: mov ecx,DWORD PTR [ebp+0x8]
0x08048482 <+104>: add eax,ecx
0x08048484 <+106>: movzx ecx,BYTE PTR [eax]
0x08048487 <+109>: movzx eax,BYTE PTR [ebp-0x7]
0x0804848b <+113>: xor eax,ecx
0x0804848d <+115>: cmp dl,al
0x0804848f <+117>: je 0x8048498 <check_buffer+126>
0x08048491 <+119>: mov eax,0x0
0x08048496 <+124>: jmp 0x80484a9 <check_buffer+143>
0x08048498 <+126>: add DWORD PTR [ebp-0x4],0x1
0x0804849c <+130>: mov eax,DWORD PTR [ebp-0x4]
0x0804849f <+133>: cmp eax,DWORD PTR [ebp+0xc]
0x080484a2 <+136>: jb 0x804843c <check_buffer+34>
0x080484a4 <+138>: mov eax,0x1
0x080484a9 <+143>: leave
0x080484aa <+144>: ret
End of assembler dump.
gdb-peda$

First, the function will set the local variables after the function prologue at 0x08048420. After that It jumps to 0x080484a2 to finally jump again to 0x804843c which is when the important part takes place:

1
2
3
4
5
6
7
8
9
10
11
12
0x0804841a <+0>:	push   ebp
0x0804841b <+1>: mov ebp,esp
0x0804841d <+3>: sub esp,0x10
0x08048420 <+6>: mov BYTE PTR [ebp-0x5],0x0
0x08048424 <+10>: mov BYTE PTR [ebp-0x6],0x0
0x08048428 <+14>: mov BYTE PTR [ebp-0x7],0x0
0x0804842c <+18>: mov DWORD PTR [ebp-0x4],0x0
0x08048433 <+25>: mov DWORD PTR [ebp-0xc],0xcd000000
0x0804843a <+32>: jmp 0x804849c <check_buffer+130>
0x0804849c <+130>: mov eax,DWORD PTR [ebp-0x4]
0x0804849f <+133>: cmp eax,DWORD PTR [ebp+0xc]
0x080484a2 <+136>: jb 0x804843c <check_buffer+34>

Here, we can se some calculations over the registers eax,ecx and edx

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
0x0804843c <+34>:	mov    eax,DWORD PTR [ebp-0xc]
0x0804843f <+37>: shr eax,0x18
0x08048442 <+40>: mov BYTE PTR [ebp-0x7],al
0x08048445 <+43>: movzx eax,BYTE PTR [ebp-0x7]
0x08048449 <+47>: and eax,0xf
0x0804844c <+50>: mov BYTE PTR [ebp-0x5],al
0x0804844f <+53>: movzx eax,BYTE PTR [ebp-0x7]
0x08048453 <+57>: and eax,0xfffffff0
0x08048456 <+60>: mov BYTE PTR [ebp-0x6],al
0x08048459 <+63>: movzx eax,BYTE PTR [ebp-0x6]
0x0804845d <+67>: mov edx,eax
0x0804845f <+69>: shr dl,0x4
0x08048462 <+72>: movzx eax,BYTE PTR [ebp-0x5]
0x08048466 <+76>: shl eax,0x4
0x08048469 <+79>: add eax,edx
0x0804846b <+81>: mov BYTE PTR [ebp-0x7],al
0x0804846e <+84>: mov edx,DWORD PTR ds:0x80d1090
0x08048474 <+90>: mov eax,DWORD PTR [ebp-0x4]
0x08048477 <+93>: add eax,edx
0x08048479 <+95>: movzx edx,BYTE PTR [eax]
0x0804847c <+98>: mov eax,DWORD PTR [ebp-0x4]
0x0804847f <+101>: mov ecx,DWORD PTR [ebp+0x8]
0x08048482 <+104>: add eax,ecx
0x08048484 <+106>: movzx ecx,BYTE PTR [eax]
0x08048487 <+109>: movzx eax,BYTE PTR [ebp-0x7]
0x0804848b <+113>: xor eax,ecx
0x0804848d <+115>: cmp dl,al
0x0804848f <+117>: je 0x8048498 <check_buffer+126>

Here follows the most important part of this dissasembled. The register ecx will contain the characters of the user’s input string. This value will get xored with eax and then compared to edx. If the input caracted contained in ecx equals to xor(eax,edx) the function will jump to the begining of the algorithm to repeat this check for every character of the string

1
2
3
0x0804848b <+113>:	xor    eax,ecx
0x0804848d <+115>: cmp dl,al
0x0804848f <+117>: je 0x8048498 <check_buffer+126>

To calculate to correct value for the input string, It is needed to calculate the value of xor(eax,edx) every iteration. This can be easily achieved with the following gdb script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 0x804848b <check_buffer+113>:    xor    eax,ecx
# 0x804848d <check_buffer+115>: cmp dl,al
# 0x804848f <check_buffer+117>: je 0x8048498 <check_buffer+126>

break *0x804848b
commands
printf "%c", $edx ^ $eax
continue
end

break *0x804848d
commands
set $eax = $edx
continue
end

run
quit

The execution will look like the followin excerpt:

1
2
3
4
5
6
7
8
$ echo | gdb -q -x au_derp.gdb ./au_derp
Reading symbols from /home/libcrack/Desktop/NcN2013-CTF/australia/au_derp...(no debugging symbols found)...done.
Breakpoint 1 at 0x804848b
Breakpoint 2 at 0x804848d
Facebook CTF
74c86bf89646425f647fcf7643af15f251b186bf58a6d5dc7eb8bf9cfc9d04cdEnter flag: Winner! Post your flag.
[Inferior 1 (process 19655) exited normally]
Warning: not running or target is remote

Challenge solved!

ref

libcrack