IO_2_1_stdout leak¶
stdout FILE struct'ını corrupt et; böylece bir sonraki puts/printf bitişik libc memory'sini flush'lar ve ASLR'ı yenmek için bir libc pointer'ı leak eder.
Mechanism¶
Note
glibc'nin buffered write path'i (_IO_new_file_overflow → _IO_do_write →
new_do_write) count = _IO_write_ptr - _IO_write_base byte flush'lar.
Boştaki bir stdout için bu pointer'lar eşittir, dolayısıyla count == 0 ve
hiçbir şey leak olmaz. Attack bu invariant'ı _IO_2_1_stdout_ FILE struct'ını
düzenleyerek bozar:
_flags = 0xfbad1800=0xfbad0000(magic)| _IO_CURRENTLY_PUTTING (0x800) | _IO_IS_APPENDING (0x1000)ayarla. Bunlar yürütmeyi kontrolleri aşıp doğrudan write'a yönlendirir ve "buffered output var" diye iddia eder._IO_write_base'in düşük byte'(lar)ını0x00'a overwrite et (tek bir relative null-byte write). Artık_IO_write_base < _IO_write_ptr, dolayısıylacountbüyük olur vewrite()düşürülmüş base'ten ileriye memory döker — pointer dolu bitişik libc yapıları boyunca._IO_read_endde sıfırlanır, böylecenew_do_writeoffset/lseekdoğrulaması (_IO_IS_APPENDINGset olduğunda atlanır) abort etmez.
Invariant: FILE struct'ı flush descriptor'ının ta kendisidir. Onun write base'ini düşürmek normal bir print'i bir out-of-bounds memory dump'ına çevirir.
Warning
Offset'ler libc-build'e-özgüdür. _flags/_IO_write_base trick'i glibc
2.23, 2.27, 2.31 üzerinde gösterilmiştir; numeric leak offset'i (ör.
leak - 0x3ed8b0) her libc için yeniden hesaplanmalı. Modern glibc (≥ 2.35)
FILE handling'ini ve vtable kontrollerini sıkılaştırır, dolayısıyla klasik FSOP
mekanikleri farklıdır.
Walkthrough¶
_IO_2_1_stdout_'u _flags'ten başlayarak overwrite et, sonra leak'i oku:
from pwn import *
payload = p64(0xfbad1800) # _flags: magic | CURRENTLY_PUTTING | IS_APPENDING
payload += p64(0)*3 # _IO_read_ptr / _IO_read_end / _IO_read_base
payload += b"\x00" # low byte of _IO_write_base -> 0x00
# ... deliver via your write primitive onto &_IO_2_1_stdout_ ...
# next buffered output triggers the leak:
leak = u64(p.recv(6).ljust(8, b"\x00"))
libc.address = leak - 0x3ed8b0 # glibc-2.27 specific offset
log.info("libc base: %#x", libc.address)
Beklenen çıktı: program normal yazdırmak yerine libc pointer'ları içeren bir dizi raw byte yayar; bilinen symbol offset'ini çıkarmak libc base'ini verir, ardından bir ret2libc ya da hook overwrite önemsizdir.
Mitigation¶
- glibc ≥ 2.35
_IO_FILEvtable/handle kontrollerini sıkılaştırır ve naif varyantı bozar; modern FSOP_IO_vtable_check'i sağlamalıdır. - Leak,
&_IO_2_1_stdout_'a ulaşan bir relative write ya da partial overwrite gerektirir; PIE/ASLR çıtayı yükseltir ama bu, kendisi ASLR-yenen bir leak'tir.