Skip to content

House of Rust

glibc 2.32 Safe-Linking'e karşı leakless, full-PIE bir technique: tcache_perthread_struct'ı overlap etmek için tcache-stashing-unlink attack'lerini ve largebin attack'lerini zincirle, _IO_2_1_stdout_ üzerinden libc'yi FSOP-leak et, sonra bir shell düşür.

Mechanism

Suistimal edilen invariant

Safe-Linking (glibc 2.32+) her singly-linked free-list fd'sini PROTECT_PTR(pos, ptr) = (pos >> 12) ^ ptr ile mangle eder, dolayısıyla bir tcache/fastbin fd'sini körü körüne corrupt etmek bir heap leak olmadan imkânsızdır. Ama tcache-stashing path'i mangle edilmiş bir fd'den geçmez. Bir smallbin _int_malloc sırasında boşaltıldığında, arta kalan smallbin chunk'ları tcache_entry pointer'ları yazılarak eşleşen tcache'e stash edilir — ve stashing-unlink path'i smallbin'in doubly-linked bk/fd'sini (ki bunlar Safe-Linking-mangle edilmiş değildir) takip eder ve tcache_perthread_struct base'ini (tcache_key) freed bir chunk'a yazar. Bir smallbin chunk'ının bk low byte'ını tcache_perthread_struct içine işaret edecek şekilde corrupt ederek, bir stash per-thread struct'ın kendisini bir tcachebin'e link'ler ve tcache metadata'sıyla overlap eden bir allocation verir — heap leak olmadan ve mangle edilmiş bir pointer forge etmeye gerek olmadan. Largebin attack'leri (bunlar da unmangled bk_nextsize kullanır) stash'in geride bıraktığı yarı-kırık pointer'ları onarmak için kullanılır.

Hiç leak'siz full-PIE binary'lerde glibc 2.32'yi (Safe-Linking) hedefler; çekirdek hile sonraki sürümlere de taşındı. c4ebt (2021) tarafından yayımlandı.

Walkthrough

Prerequisite: allocation pointer'larının null'lanmadığı bir UAF (freed chunk'lar üzerinde write-after-free), stdout'u line/full buffered olmayan bir target'ta (FSOP leak için gerekli). Beş aşama:

  1. Heap feng shui. ~65 chunk (0x1b00'a kadar) önceden allocate et ki sonraki adımlar beklenmedik servisi tetiklemesin ve downstream'de kullanılan write-after-free chunk'larını konumlandır.

  2. Tcache-stashing-unlink+ (TSU+) + largebin attack. Bir 0x90 bin ile: tcache'e 7 chunk ve unsorted bin'e 7 chunk free et (0x90 smallbin'e sort olur). Bir victim'in size'ını WAF-corrupt et, sonra smallbin bk low byte'ını 0x80'e corrupt et ki tcache_perthread_struct içinde &tcache_head - 0x18'e işaret etsin. Bir largebin attack kırık fd'yi onarır (ikinci large chunk'ın address'ini victim'in fd'sine yazar). Stash'i tetiklemek per-thread struct'ı 0x90 tcache'e link'ler — bir sonraki 0x90 allocation tcache_perthread_struct içinden servis edilir.

  3. Tcache-stashing-unlink (TSU) + largebin attack. Bir 0xa0 bin ve farklı bir largebin ile tekrarla. TSU (TSU+ değil) writable bir target+0x18 gerektirmez. Bu, Stage-2 overlap'ı yakınında tcache_perthread_struct'a bir libc address yazar.

  4. stdout FSOP leak. tcache_perthread_struct'la overlap eden chunk'ı düzenle; Stage-3 libc değerinin 2 low byte'ını _IO_2_1_stdout_'a işaret edecek şekilde tahrif et (brute-force 4 bit, 1/16). stdout'la overlap edecek şekilde allocate et, sonra file struct'ını craft et:

stdout->_flags      = 0xfbad1800;
stdout->_IO_read_ptr = stdout->_IO_read_end = stdout->_IO_read_base = 0;
/* null the LSB of _IO_write_base so it under-points -> leaks libc/heap */
Bir sonraki buffered write attacker'ın seçtiği byte'ları flush eder ve libc'yi leak eder.

  1. Shell. libc bilinince, bir tcache head'ini &__free_hook'a yönlendir, onu allocate et, __free_hook = system yaz, sonra "/bin/sh\x00" içeren bir chunk'ı free() et → system("/bin/sh").

Footgun: pointer onarımı zorunludur

İki stash attack de smallbin/largebin list'lerini yarı-corrupt bir state'te bırakır. Eğer eşleşen largebin attack stash çalışmadan önce victim'in fd'sini onarmazsa, _int_malloc bozuk bir pointer dereference eder ve crash eder. Largebin attack ve stash bağımsız adımlar değil, eşleşmiş bir çifttir.

fd yerine neden stashing hedeflenir

Safe-Linking altında doğrudan bir tcache fd overwrite'ı pos >> 12'yi (chunk'ın kendi heap page'i) bilmeyi gerektirir, yani bir heap leak. Stash path'i doubly-linked smallbin/largebin metadata'sından türetilen pointer'ları yazar, ki bu asla XOR-mangle edilmez — dolayısıyla tek bir low-byte bk overwrite'ı yeter ve tüm exploit bir leak yerine sadece 4-bit'lik bir tahmine ihtiyaç duyar.

Detection

  • tcache_perthread_struct içine işaret eden bir smallbin bk veya largebin bk_nextsize anormaldir.
  • Sıfırlanmış read pointer'larıyla _IO_2_1_stdout_._flags == 0xfbad1800 kanonik stdout-FSOP imzasıdır.

Mitigation

  • glibc 2.32+ Safe-Linking bu technique'in özellikle etrafından dolaştığı korumadır; sonraki glibc alignment check'leri (PROTECT_PTR + aligned) ve daha sıkı tcache-stashing-count check'leri ekler.
  • glibc 2.34 __free_hook'u kaldırdı, dolayısıyla Stage 5 bunun yerine FSOP/_IO_* veya exit-handler target'larına pivot etmeli.
  • Daha yeni _IO_2_1_stdout_ vtable/_codecvt check'leri FSOP leak adımını zorlaştırır.

References