House of Orange¶
sysmalloc'u suistimal ederek top chunk'ıfree()olmadan free et, sonra_IO_list_allüzerinde bir unsorted-bin attack'i File-Stream-Oriented-Programming (FSOP)'a zincirle: glibc ≤ 2.23'te naive arbitrary code execution, 2.24–2.25'te House of Apple tarzı bir vtable bypass ile (çünkü 2.24_IO_vtable_checkekledi), 2.26'da isemalloc_printerrartık_IO_flush_all_lockp'ı çağırmadığı için abort→FSOP path'i kırılır.
Mechanism¶
Suistimal edilen invariant
Klasik House of Orange, sysmalloc'un heap'i genişletmesi gerektiğinde ve yeni top eskisiyle bitişik olmadığında eski top chunk'ı free edeceği gerçeğini sömürür. Top chunk normalde asla free edilmez ve dolayısıyla sıradan bir chunk gibi asla validate edilmez: size'ı overflow üzerinden attacker-kontrollüdür. Top'un size'ını PREV_INUSE set'li küçük, page-hizalı bir değere küçülterek, oversized bir request sysmalloc'u (old_top_size >= MINSIZE) && prev_inuse(old_top) check'ini sağlamaya ve eski top'u unsorted bin'e atmaya zorlar — hiç free() çağırmadan freed, attacker-boyutlu bir chunk verir.
Oradan unsorted bin'in partial-unlink'i (bck->fd = unsorted_chunks(av)) bk->fd'ye bir libc address'inin tek bir write'ını yapar. bk'yi _IO_list_all - 0x10'a yönlendirmek global FILE list'inin head'ini overwrite eder. Son olarak, pre-2.26 glibc'de, malloc_printerr → __libc_message → _IO_flush_all_lockp _IO_list_all'u dolaşır ve her FILE'ın _IO_OVERFLOW'unu, yani attacker'ın seçtiği bir vtable entry'sini çağırır. 2.24'ten önce vtable whitelist yoktur, dolayısıyla forge edilmiş FILE'ın vtable'ı __overflow slot'u system olan fake bir table'a işaret edebilir.
Walkthrough¶
glibc ≤ 2.23'e uygulanır. Top chunk'a tek bir heap overflow gerektirir.
- Top chunk'ı küçült. Top'a bitişik bir chunk allocate et, sonra overflow ile top chunk size'ını küçük page-hizalı bir değerle (örn.
0xc01,PREV_INUSE'u tutarak) overwrite et. Page'in geri kalanıold_top + old_size'ın bir page sınırına inecek şekilde hizalanmalı kisysmalloc'un assertion'ı sağlansın.
// overflow target: the top chunk size field
*(size_t*)(top_size_addr) = 0xc01; // page-aligned + PREV_INUSE
sysmalloc'u eski top'u free etmeye zorla. (Artık minik olan) top'tan daha büyük bir chunk iste:
Free edilen eski-top artık size'ını kontrol ettiğin bir unsorted-bin chunk'ıdır.
- Freed chunk'ı unsorted-bin attack + fake FILE için forge et. Freed top'un gövdesine fake bir
_IO_FILEyazmak için overflow'u tekrar kullan vebk = &_IO_list_all - 0x10set et. FILE'ı şöyle craft et: fp->_mode <= 0vefp->_IO_write_ptr > fp->_IO_write_baseki flush sırasında_IO_OVERFLOW'a ulaşılsın;- chunk
sizefield'ı, smallbin index'ini FILE pointer'ına yönlendiren fake bir FILE field'ı olarak ikili görev yapar; -
vtable,__overflowslot'usystemolan fake bir table'a işaret eder ve struct'ın başında/bin/shbulunur. -
Trigger. Başka bir allocation iste. malloc corrupt edilen unsorted chunk'ı sort eder, unsorted-bin attack
unsorted_chunks(av)'i_IO_list_all'a yazar ve bozuk metadatamalloc_printerr'i tetikler:
Footgun'lar
- Top size'ı page-hizalı OLMALI VE
PREV_INUSE'u tutmalı, yoksasysmallocassertion'ı free'den önce başarısız olur. - Fake FILE unsorted chunk header'ıyla overlap eder;
sizefield'ı hem bir chunk size (bir smallbin'e inmek için) hem bir FILE field'ı olarak tekrar kullanılır, dolayısıyla değer her iki rolü de sağlamalı. - Bu temelde bir pre-2.24 technique'idir; sonrasında ayrı bir vtable bypass (örn.
_IO_str_jumps, House of Apple) ile kombine edilmesi gerekir.
Detection¶
- Page-hizalı ve arena'nın map'lenmiş boyutundan çok daha küçük bir top chunk size'ı.
- Heap'te
__libc_IO_vtablessection'ı dışında bir vtable pointer'lı bir FILE. _IO_list_all'un_IO_2_1_stderr_yerine heap'e işaret etmesi.
Mitigation¶
- glibc 2.24:
_IO_vtable_checkeklendi — vtable read-only__libc_IO_vtablesaralığında olmalı, doğrudan fake-vtable adımını kırar. - glibc 2.26 (commit
91e7cf982d):malloc_printerrartık_IO_flush_all_lockp'ı çağırmıyor, abort→FSOP path'ini kaldırır. - glibc 2.29+: unsorted-bin integrity check'i (
bk->fd == victim) unsorted-bin-attack write'ını hardened yapar.