House of IO¶
tcache_perthread_struct'ın kendisini — tcache bin head'lerinin heap-resident array'ini — corrupt et; böyleceentries[]pointer'ları arbitrary, unmangled address'lerle overwrite edilir ve temiz bir arbitrary-allocation primitive elde edilir.
Mechanism¶
Suistimal edilen invariant
Her thread'in tcache metadata'sı tek bir heap chunk'ta yaşar; şöyle tanımlanır:
typedef struct tcache_perthread_struct {
uint16_t counts[TCACHE_MAX_BINS]; /* per-bin chunk count */
tcache_entry *entries[TCACHE_MAX_BINS]; /* per-bin freelist head */
} tcache_perthread_struct;
entries[] head'leri raw olarak saklanır — chunk içindeki fd
pointer'larının aksine, safe-linking pointer mangling'inden geçirilmezler.
Yani bu struct'a yazabilen biri herhangi bir bin'in head'ini herhangi bir
address'e set edebilir; o boyutta sonraki malloc() address'i doğrudan geri
verir. Allocator'ın dayattığı tek püf nokta counts[i]: tcache fast path'leri
bir bin'den ancak count'u non-zero ise pop yapar, ama allocator count'u
validate etmez, dolayısıyla bir attacker poison'lanmış entry'nin yanına
sadece pozitif bir count yazar.
House of IO'nun katkısı struct'a nasıl ulaştığındır. tcache_perthread_struct
thread'in ilk allocation'ında lazy olarak allocate edilir, yani main
thread'de heap'in en başında oturur ve normalde sadece bir heap underflow
ile erişilebilir. House of IO bunun yerine onun üzerinde sıradan bir write/UAF
elde eder.
Walkthrough¶
glibc ≥ 2.26'ya uygulanır (tcache tanıtıldı); raw entries[] tasarımı 2.34'e
kadar devam eder, safe-linking ise chunk fd'sine (bu head'lere değil) 2.32'de
eklendi. İki ana yol var:
-
Underflow varyantı.
tcache_perthread_structheap'teki ilk chunk olduğunda, ilk user chunk'tan yapılan negative-index / underflow write struct'a geriye doğru uzanır. Target bin'inentries[i]'sinitargetile overwrite et vecounts[i]'yi artır ki allocator bin'i non-empty sansın. -
Heap-reuse varyantı (adını veren "IO"). Bir arena'yı paylaşmaya zorlanacak kadar thread spawn et (thread'ler per-CPU arena cap'ini aştığında). İkincil bir thread'in
tcache_perthread_struct'ı bu durumda shared arena'da sıradan bir heap chunk olarak allocate edilir — artık heap base'ine sabitlenmemiş. Şimdi bitişik bir main-thread chunk'tan yapılan düz bir use-after-free, double-free veya linear buffer overflow onun üzerine düşer ve nadir underflow primitive'ine olan ihtiyacı ortadan kaldırır. -
Entries slot'unu bul.
ibin index'ini poison'lamak için chunk header'ını (0x10) vecountsarray'ini (0x80byte) geçipentries[]'e yazarsın.szboyutundaki bir chunk için slot,entries[]içinde((sz - 0x20) / 0x10) * 8byte'tadır. -
Target'ı yaz.
target'ı (mangling yok, alignment kısıtı yok) o slot'a sakla ve eşleşencountsentry'sini non-zero bir değere set et. -
Reclaim.
malloc(sz)entries[i]'yi pop'lar vetarget'ı döndürür. İkinci birmalloc(sz)target'ın bir sonraki işaret ettiği şeyi döndürür — bunu libc global'lerine,__free_hook'a, saklı bir object pointer'a vb. bir write'a çevir.
Count neden önemli
tcache get path'i kabaca if (tcache->counts[tc_idx] > 0) { ... return e; }
şeklindedir. Poison'lanmış bir entries[i] ama counts[i] == 0 ile bin
atlanır ve address'in asla döndürülmez — her zaman iki field'ı birlikte patch'le.
Footgun'lar
- Heap-reuse varyantı arena sharing'e bağlıdır ki bu sadece
arena_max/CPU-count eşiğinin ötesinde devreye girer; single-arena bir target'ta yine underflow'a ihtiyaç duyarsın. countsuint16_t'dir;entries[]'e ulaşmak için counts array'i boyunca 8-byte filler yazmak birçok bin count'unu non-zero yapar — target bin için zararsız ama sonraki allocation'ları bozabilir.
Detection¶
entries[]head'i heap dışına (libc/stack içine) işaret eden birtcache_perthread_structbelirleyici işarettir.counts[i]ileibin'i için gerçekten link'li freelist uzunluğu arasındaki uyumsuzluk tampering'i gösterir.
Mitigation¶
- Safe-linking (glibc 2.32) chunk
fdpointer'larını korur ama bu struct head'lerini korumaz, dolayısıyla House of IO'yu doğrudan durdurmaz. - tcache key/double-free guard (glibc 2.29+) struct'a giden double-free path'ini zorlaştırır ama UAF/overflow write'larını değil.
tcache_perthread_struct'ı off-heap saklayan veyaentries[]'i mangle eden allocator build'leri technique'i kapatır; kaynak UAF/overflow'u kaldırmak prerequisite'i kaldırır.