Nytro Posted November 17, 2013 Report Posted November 17, 2013 [h=1]NcN 2013 CTF australia bin write up[/h]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:borja@PanoramaBar $ 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 strippedborja@PanoramaBar $ ./derpFacebook CTFEnter flag: asdasdasdasdSorry, that is not correct.borja@PanoramaBar $ The first look at the binary:borja@PanoramaBar $ gdb -q ./derpReading symbols from /home/borja/Desktop/NcN2013-CTF/derp...(no debugging symbols found)...done.gdb-peda$ pdisass mainDump 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 0x080483b70x080483b7 <+227>: call 0x804841a <check_buffer>Let’s what the function check_buffer does:gdb-peda$ pdisass check_bufferDump 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 0×08048420. After that It jumps to 0x080484a2 to finally jump again to 0x804843c which is when the important part takes place:0x0804841a <+0>: push ebp0x0804841b <+1>: mov ebp,esp0x0804841d <+3>: sub esp,0x100x08048420 <+6>: mov BYTE PTR [ebp-0x5],0x00x08048424 <+10>: mov BYTE PTR [ebp-0x6],0x00x08048428 <+14>: mov BYTE PTR [ebp-0x7],0x00x0804842c <+18>: mov DWORD PTR [ebp-0x4],0x00x08048433 <+25>: mov DWORD PTR [ebp-0xc],0xcd0000000x0804843a <+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 edx0x0804843c <+34>: mov eax,DWORD PTR [ebp-0xc]0x0804843f <+37>: shr eax,0x180x08048442 <+40>: mov BYTE PTR [ebp-0x7],al0x08048445 <+43>: movzx eax,BYTE PTR [ebp-0x7]0x08048449 <+47>: and eax,0xf0x0804844c <+50>: mov BYTE PTR [ebp-0x5],al0x0804844f <+53>: movzx eax,BYTE PTR [ebp-0x7]0x08048453 <+57>: and eax,0xfffffff00x08048456 <+60>: mov BYTE PTR [ebp-0x6],al0x08048459 <+63>: movzx eax,BYTE PTR [ebp-0x6]0x0804845d <+67>: mov edx,eax0x0804845f <+69>: shr dl,0x40x08048462 <+72>: movzx eax,BYTE PTR [ebp-0x5]0x08048466 <+76>: shl eax,0x40x08048469 <+79>: add eax,edx0x0804846b <+81>: mov BYTE PTR [ebp-0x7],al0x0804846e <+84>: mov edx,DWORD PTR ds:0x80d10900x08048474 <+90>: mov eax,DWORD PTR [ebp-0x4]0x08048477 <+93>: add eax,edx0x08048479 <+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,ecx0x08048484 <+106>: movzx ecx,BYTE PTR [eax]0x08048487 <+109>: movzx eax,BYTE PTR [ebp-0x7]0x0804848b <+113>: xor eax,ecx0x0804848d <+115>: cmp dl,al0x0804848f <+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 string0x0804848b <+113>: xor eax,ecx0x0804848d <+115>: cmp dl,al0x0804848f <+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:# 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 continueendbreak *0x804848d commands set $eax = $edx continueendrunquitThe execution will look like the followin excerpt:borja@PanoramaBar $ echo | gdb -q -x au_derp.gdb ./au_derpReading symbols from /home/borja/Desktop/NcN2013-CTF/australia/au_derp...(no debugging symbols found)...done.Breakpoint 1 at 0x804848bBreakpoint 2 at 0x804848dFacebook CTF74c86bf89646425f647fcf7643af15f251b186bf58a6d5dc7eb8bf9cfc9d04cdEnter flag: Winner! Post your flag.[Inferior 1 (process 19655) exited normally]Warning: not running or target is remoteChallenge solved!Sursa: NcN 2013 CTF australia bin write up | non-sleep thinking Quote