Skip to content

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_check ekledi), 2.26'da ise malloc_printerr artı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.

  1. 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ı ki sysmalloc'un assertion'ı sağlansın.
// overflow target: the top chunk size field
*(size_t*)(top_size_addr) = 0xc01; // page-aligned + PREV_INUSE
  1. sysmalloc'u eski top'u free etmeye zorla. (Artık minik olan) top'tan daha büyük bir chunk iste:
malloc(0x1000); // top can't serve it -> sysmalloc() -> old top freed into unsorted bin

Free edilen eski-top artık size'ını kontrol ettiğin bir unsorted-bin chunk'ıdır.

  1. Freed chunk'ı unsorted-bin attack + fake FILE için forge et. Freed top'un gövdesine fake bir _IO_FILE yazmak için overflow'u tekrar kullan ve bk = &_IO_list_all - 0x10 set et. FILE'ı şöyle craft et:
  2. fp->_mode <= 0 ve fp->_IO_write_ptr > fp->_IO_write_base ki flush sırasında _IO_OVERFLOW'a ulaşılsın;
  3. chunk size field'ı, smallbin index'ini FILE pointer'ına yönlendiren fake bir FILE field'ı olarak ikili görev yapar;
  4. vtable, __overflow slot'u system olan fake bir table'a işaret eder ve struct'ın başında /bin/sh bulunur.

  5. 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 metadata malloc_printerr'i tetikler:

malloc(0x10); // unsorted-bin attack fires -> abort -> _IO_flush_all_lockp -> system("/bin/sh")

Footgun'lar

  • Top size'ı page-hizalı OLMALI VE PREV_INUSE'u tutmalı, yoksa sysmalloc assertion'ı free'den önce başarısız olur.
  • Fake FILE unsorted chunk header'ıyla overlap eder; size field'ı 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_vtables section'ı 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_check eklendi — vtable read-only __libc_IO_vtables aralığında olmalı, doğrudan fake-vtable adımını kırar.
  • glibc 2.26 (commit 91e7cf982d): malloc_printerr artı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.

References