6/25/2008

WARMING UP on STACK - Part II

Last time we worked on the first three levels of the WARMING UP on STACK section of gera's exploit challenge, today we'll clear the remaining two. Both will be covered as a single solution since they only differ in the output message after succeeding. As we mentioned, the path to the solution is a little bit different than the other levels. Last time all we had to do is overflow our buffer until we reached the cookie value. This time rather than changing a local variable's value, we will change the execution flow of our program.

For this to be possible, we need a refresher on what role the stack plays in the proper flow of a program. From a C language point of view each time we call a function a couple of mandatory proceedings happen within the stack. First, when the assembly instruction call is executed, the current value of the eip register gets pushed into the stack (which grows towards lower memory addresses) so that when the function finishes we can get back to where we were executing instructions. Next, we save the current ebp register value in the stack and set the current esp value as ebp to have a consistent placeholder value from where to retrieve local variables and function parameters. To summarize, this is what a x86 assembly stack frame setup would look like:

Random program calls function X():
...
...
call to function X ----------------------> call X
this is where execution will return
after X() does his job -----------------> ...

and after the call we land in X:

first, save current EBP --------------> push ebp
then, update EBP ---------------------> mov esp, ebp
...

This figures might change depending on architecture and syntax, but I think the general idea should be clear for now :-). Now moving on, the saved ebp and ret values (the saved eip value, called ret from now on) are stored on the stack AFTER our buffer. At this point the path seems a little more obvious, why not overrun the stack and change our ret value so instead of returning to the callee (the program that called it), return to the "you win!" path inside the if statement?

First we need to figure out how much we need to overwrite to get to the ret value. If we recall from the previous levels, at the time we used 92bytes plus the value we wanted the cookie to have. If we take a look at the stack layout from the previous sessions and the theory we just reviewed, the ret value should be right after the saved ebp that we have already identified:


With a little math we can see the ret value (shown in green), is 16 bytes ahead of the cookie, so; 92 + cookie(4bytes) + 16 = 112bytes. Let's try that much and see what happens:
As you can see from the Core Dump (use "$ulimit -c unlimited" if you're not getting dumps) we smashed the return value with 0x41414141 which is the hex value of the A character's we used. Therefore, all we need now is to write 108+the ret value we want in the buffer. If we read the disassemble we can use the value of the path is taken had the if comparison be true, 0x08048438. I want to stress at this point that hardcoding this kind of addresses is just possible because we are using a unpatched version of linux without ASLR and other protection measures. Keeping in my the endianness and using a similar command line setup of the previous sessions, we can bypass the cookie comparison again:

A smart reader might have noticed that althought we get the win and loose messages confirming our success, we get a segmentation fault each time. This happens because althought our ret overwriting succeeded, we also smashed the ebp value which gets pop'd when the functions ends. This corrupted value is needed by the callee and other functions and these fail if the value doesn't have a proper meaning, which after filling with A's doesn't. I tried to find a workaround and tested several values from values in various execution situations but a the pushes and pops of the values from the printf parameters seems to screw up my calculations.

Hope anybody got a more elegant solution than mine and posts it as a comment or anywhere else, but for the purpose I think this is enough. This article finishes the WARMING UP on STACK section, in the future I will try to continue with the rest of the sections but I don't promise anything at this point :-) Thanks for reading if you got up to this point!

6/24/2008

WARMING UP on STACK - Part I

In our last post we talked about a challenge set up by gera from Core Security. Since these challenges make up a good training ground for further real world exploit situations I decided to give a shot at them now that my schedule got a little softer.

We will start from the ground up and cover levels #1 to #3 from the "Warming up on Stack" section which, as the name implies, are just a warmup. To get the job done, we needed a linux distribution that doesn't include any security filters such as ASLR or Non-executable stack or the PaX patch; in my case, I chose to use SUSE 9.3 inside a virtual machine. Completenting our toolset we will use the usual set; gdb, python and a terminal (Hello, Konsole :-).

First, let's take a look at the challenges source code:
int main() {
int cookie;
char buf[80];

printf("buf: %08x cookie: %08x\n", &buf, &cookie);
gets(buf);

if (cookie == 0x41424344)
printf("you win!\n");
}
Nothing strange in here, a simple C program that uses libc's very own gets() function to receive some input from stdin. If we take a look at the disassembled code we notice that all it takes to get into the good-boy is to succeed in the hardcoded comparison against the 0x41424344 cookie, no news after the C source. So let's setup a breakpoint at the comparison and take a look at the stack layout.


Once we hit the breakpoint we analyze the stack:


This is where the fun begins. You can see where our garbage A's begin and where our cookie and ebp are so, all that's left is a little hex math. ebp is at 0xbffff118, the cookie at 0xbffff10c and our buffer starts at 0xbffff0b0, thats 92 bytes from our buffer till the cookie(which at the same time is 12 bytes minus ebp, as the cmp instruction shows). Therefore we have all we need, the offset and the value of the cookie. All that's left to do is just injecting those values in our buffer, and that's were python comes to play. We can build our string with python and then pipe the result to our program like this:


The first three levels covered in this post are just little variants of each other where the only difference is the value of the cookie. This is no match for python which handles hex values just as fine, congratulating us with a gentle "you win!".

Now this is it for today, The 4th and 5th levels are a bit different because they deal with carriage return and new line values in the cookie (0x0d and 0x0a, respectively) and thus, require a different approach. I hope you enjoyed this as much as I did solving them. Keep adding NOPs! :-)