Back again with the ROP challenge this time we will go back again to basic training to strengthen our understanding more about this exploitation. You maybe start to get sick of me doing this loop of refreshing material, well this is just my preference of study since many people are so impatient and want to go to the next level but how can you go to the next level if you cannot master the basic right? ( this is the wisdom that I got for rewatching Avatar Last Airbender :) LOL)
alright cool!
This post was inspired by the 4th picoctf2014 challenge which is "rop4" (link: https://github.com/ctfs/write-ups-2013/tree/master/pico-ctf-2013/rop-4)
but I altered the source code little bit to align with my objective:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
char exec_string[20];
void exec_the_string() {
execlp(exec_string, exec_string, NULL);
}
void call_me_with_cafebabe(int cafebabe) {
if (cafebabe == 0xcafebabe) {
strcat(exec_string, "/sh");
}
}
void call_me_with_two_args(int deadbeef, int cafebabe) {
if (cafebabe == 0xcafebabe && deadbeef == 0xdeadbeef) {
strcat(exec_string, "/bin");
}
}
void vulnerable_function() {
char buf[128];
read(STDIN_FILENO, buf, 512);
}
void main(){
vulnerable_function();
}
so from this source code, we need to chain call_me_with_two_args and call_me_with_cafebabe function together to concatenate the string "/bin" and "/sh". Don't need to be confused with the execlp() function this is equivalent to execve() function
compile the following source code with this specification. We will turn off the canary and PIE protection in the binary but ASLR and NX is still on
I'm not going to waste your time we will skip the "how many offsets that we need" part and tell you that we need 140 offset to overwrite the EIP.
next is to craft the first phase of the payload that will set the "/bin" string to the global variable exec_string.
first, we need to know the location of this call_me_with_two_args :
For all of you who just start learning may incline to set the stack in the following way:
| call_me_with_two_args function |
| call_me_with_cafebabe |
| param1 |
| param2 |
| param3 (for call_me_with_cafebabe) |
so after we successfully passed the two parameters for call_me_with_two_args function we will jump to call_me_with_cafebabe function with param3
but this is actually wrong and could mess up the flow of the ROP since the leftover variable from two parameters still reside in the stack we need to pop these two params and we can continue the execution
how to do that?
like the title suggests we are going to use ROP gadget to pop the stack, you can find it using peda by:
It's pretty simple to read the results, for example popret means that "pop x ; ret" (take out one value from the stack) and pop2ret means "pop x; pop x; ret" (take out two values from the stack). x is going to be the name of registers (ebx, esp, edi) it is doesn't matter what register you choose as long the sequence of the pop and ret is align with our execution
cool from this information we can supply the first phase of our rop to be like this
let's go through the execution one by one so we know that we set the stack right:
lets put a breakpoint at the end of vulnerable_function and run the peda with the python script we just create:
(gdb-peda) r <<< $(python exploit.py)
now if you step through the execution by typing:
(gdb-peda) ni
you will end up in the call_me_with_two_args function and if you look at the function there are two comparisons going just like the source code listed this will match the first two params that we passed in the ROP
we passed the two comparisons and if you remember from the source code that the "/bin" string will be stored in the global variable called exec_string and if we go down through the execution we can see it is stored in the address 0x804a024 and it was moved to the eax register.
and then along the line all it's left will be poping the stack
the first pop, take out the0x deadbeef from the stack
the second pop will be getting rid the 0xcafebabe value from our stack cool :)
so we know how the ROP gadget work in detail manner lets update script to called the second function which is call_me_with_cafebabe and with the parameter of 0xcafebabe and then finally called the exec_the_string() function to execute the shell
we need to get the location of the last two mentioned functions and then we construct our script to be like this:
If we execute the script again with peda when you try to debug it and try to examine the stack at the end of the execution you can see that we successfully go to the second function and bypass the comparison
finally, as you can see the global variable is concatenated with the "/sh" string and we can get a shell from it
you can try it by running the payload outside the gdb:
cool :) so we did it we are able to construct a working rop exploitation
that's all folks, hope you enjoy it
Comments
Post a Comment