Skip to content

House of Obstack

vtable'ı glibc'nin meşru _IO_obstack_jumps'ına işaret eden bir FILE forge et; obstack flush path _obstack_newchunk'ın CALL_CHUNKFUN macro'suna ulaşır ki bu attacker-kontrollü bir chunkfun function pointer'ını çağırır — _IO_vtable_check whitelist'ini bypass ederek.

Mechanism

Suistimal edilen invariant

glibc 2.24'ten beri _IO_vtable_check bir FILE'ın vtable'ının read-only __libc_IO_vtables section'ı içinde olmasını gerektirir ki bu arbitrary fake vtable'ları nötralize eder. House of Obstack, _IO_obstack_jumps'ın kendisinin o section içinde meşru, whitelist'lenmiş bir vtable olması gerçeğini suistimal eder. Forge edilmiş bir FILE'ın vtable'ını ona yönlendirmek check'i geçer, ama onun _IO_obstack_xsputn slot'u — obstack_grow_obstack_newchunk üzerinden — CALL_CHUNKFUN macro'suna götürür:

#define CALL_CHUNKFUN(h, size) \
  (((h)->use_extra_arg) ? (*(h)->chunkfun)((h)->extra_arg, (size)) \
                        : (*(struct _obstack_chunk *(*)(long))(h)->chunkfun)((size)))

chunkfun, extra_arg ve use_extra_arg hepsi heap'teki attacker-kontrollü _IO_obstack_file yapısının içinde yaşar. Yani whitelist'lenmiş obstack vtable, read-only section dışına hiç vtable yerleştirmeden, attacker-kontrollü bir argument'la attacker-kontrollü bir function pointer üzerinden dolaylı çağrı yapar — esasen system("/bin/sh").

Walkthrough

_IO_obstack_xsputn/obstack_grow path'i üzerinden glibc 2.24–2.36'ya uygulanır (bu aralıkta _IO_vtable_check mevcuttur ve whitelist bypass gerçekten gereklidir). Obstack code path'inin kendisi 2.23'te de var, ama orada henüz _IO_vtable_check yok — dolayısıyla whitelist bypass'a ihtiyaç duyulmadan sade FSOP zaten çalışır. 2.37+'da eşdeğer flush path __printf_buffer_as_file_overflow__printf_buffer_flush__printf_buffer_flush_obstack__obstack_newchunk çalıştırır ve aynı CALL_CHUNKFUN'a ulaşır.

  1. Bir FILE pointer'ını kontrol altına al. Heap corruption üzerinden (örn. _IO_list_all'dan erişilebilir bir overlapping/UAF chunk veya doğrudan kontrol edilen bir stream), bir _IO_FILE/_IO_obstack_file değerinde byte'ı kontrol edecek şekilde ayarla.

  2. vtable'ı obstack jumps table'a set et. FILE'ın vtable field'ına &_IO_obstack_jumps (artı _IO_OVERFLOW/xsputn dispatch'inin _IO_obstack_xsputn'a ulaşması için uygun slot offset'i) yaz. Bu __libc_IO_vtables içine işaret ettiği için _IO_vtable_check'i geçer.

  3. Obstack field'larını forge et. _IO_obstack_file bir struct obstack gömer. Heap'i yapı base'ine göre şöyle yerleştir:

  4. obstack pointer field'ı senin forge ettiğin obstack'e referans verir;
  5. obstack->chunkfun = system (veya bir gadget);
  6. obstack->extra_arg = "/bin/sh"'a pointer;
  7. obstack->use_extra_arg set edilir ki CALL_CHUNKFUN'un iki-arg dalı alınsın;
  8. size/next_free/chunk_limit field'ları obstack_grow'un yeni bir chunk gerektiğine karar verip _obstack_newchunk'ı çağıracağı şekilde düzenlenir.
forged FILE:
  vtable          -> &_IO_obstack_jumps (+slot)
  obstack ptr     -> &fake_obstack (heap)
fake_obstack:
  chunkfun        -> system
  extra_arg       -> "/bin/sh"
  use_extra_arg   -> 1
  (size fields)   -> force _obstack_newchunk
  1. Flush'ı tetikle. _IO_flush_all_lockp'a ulaş (örn. exit, abort/_IO_cleanup veya bir stdio çağrısı üzerinden). Obstack xsputn çalışır, obstack_grow mevcut obstack chunk'ını overflow eder, _obstack_newchunk CALL_CHUNKFUN'ı ateşler ve system("/bin/sh") çalışır.

Footgun'lar

  • Slot offset'i önemli: dispatch edilen virtual function _IO_obstack_xsputn'a inmeli; off-by-slot bir vtable pointer farklı bir obstack rutini dispatch eder.
  • Obstack size hesabı gerçekten bir yeni chunk zorlamalı; eğer obstack_grow yer bulursa, _obstack_newchunk asla çağrılmaz ve chunkfun asla invoke edilmez.
  • Field offset'leri glibc sürümleri arasında ve pre-2.37 ile 2.37+ path'leri arasında farklıdır — target'ın struct obstack/_IO_obstack_file'ına karşı doğrula.

Detection

  • Program meşru obstack stream I/O yapmazken vtable'ı _IO_obstack_jumps olan bir FILE.
  • libc/programın kendi chunk allocator'ının dışına işaret eden bir obstack chunkfun.

Mitigation

  • _IO_obstack_jumps whitelist'lenmiş olsa da, obstack stream desteğini düşüren veya per-vtable argument validation ekleyen hardened build'ler gadget'ı kaldırır.
  • Genel FSOP hardening'i (örn. _IO_cleanup/exit'in attacker-erişilebilir stream'leri flush etmemesi) trigger yüzeyini azaltır.

References