Skip to content

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:

$ 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

$ gdb -q ./ca_howtobasic 
Reading symbols from /home/libcrack/Desktop/NcN2013-CTF/canada/ca_howtobasic...(no debugging symbols found)...done.
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
	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
	0x080d1054 - 0x080d105c is .got
	0x080d105c - 0x080d107c is .got.plt
	0x080d1080 - 0x080d17e0 is .data
	0x080d17e0 - 0x080d3374 is .bss
	0x080d3374 - 0x080d338c is __libc_freeres_ptrs

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:

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).

   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:

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:

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: ")

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

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

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

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:

break *0x8048443
    printf "%c", (($edx ^ $ecx)*-1)-1
break *0x8048445
    set $eax = $edx

Lets try it!

$ 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.
Published inctfdebugginghackinglinux