File Stream Oriented Programming (FSOP)¶
exit'te flush edilen glibc FILE struct'larını forge ederek control flow'u hijack et.
Mechanism¶
Her açık FILE (struct _IO_FILE_plus), _chain field'ı üzerinden
_IO_list_all tarafından head'lenen global bir listeye link'lenir. exit(),
abort() ya da main'den return'de glibc, _IO_flush_all_lockp'yi çağırır; bu da o
listeyi yürür ve _IO_OVERFLOW(fp, EOF)'u çağırır — fp->vtable->__overflow
üzerinden bir indirect call. FSOP, _IO_list_all'ı (ya da mevcut bir FILE'ı) bir
fake _IO_FILE_plus + fake vtable'a işaret edecek şekilde overwrite eder;
böylece flush, attacker-chosen bir fonksiyona dispatch olur.
Note
FSOP, _IO_list_all'a (ya da UAF'lanmış bir FILE'a) yapılan tek bir write'ı,
bir return-address overwrite'ına veya bug'tan sonra canlı bir call site'a ihtiyaç
duymadan, process teardown'da code execution'a çevirir.
Walkthrough¶
_IO_flush_all_lockp içindeki tetikleyici koşul:
if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base))
&& _IO_OVERFLOW (fp, EOF) == EOF) // indirect call: fp->vtable[__overflow]
Yani fake file, _mode <= 0 ve _IO_write_ptr > _IO_write_base koşullarını
sağlamalıdır. Tipik forge edilmiş offset'ler:
// _IO_write_base @ 0x20, _IO_write_ptr @ 0x28, _mode @ 0xc0, vtable @ 0xd8
fake->_IO_write_base = 0;
fake->_IO_write_ptr = 1; // ptr > base
fake->_mode = 0; // <= 0
fake->vtable = target_vtable;
glibc >= 2.24'te forge edilmiş bir vtable IO_validate_vtable tarafından
bloklanır. Standart bypass, vtable'ı _IO_str_jumps gibi in-range geçerli bir
tabloya işaret ettirir; onun __overflow slot'u (_IO_str_overflow)
(*fp->_s._allocate_buffer)(new_size)'ı çağırır; _allocate_buffer = system ve
new_size = "/bin/sh" forge etmek system("/bin/sh") verir — hepsi geçerli bir
vtable üzerinden.
Mitigation¶
IO_validate_vtable (>= 2.24) section dışı vtable'ları bloklar; sonraki glibc
_IO_str/_IO_wstr jump'larını sertleştirdi. _IO_list_all'ın pointer guard'ı ve
indirect __overflow call'unda CFI bar'ı yükseltir.