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():
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!
...
...
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:
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
retvalues
(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:
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!