Skip to content

ptmalloc consolidation abuse

glibc'in malloc_consolidate() rutinini attacker-chosen bir anda fastbin chunk'larını unsorted bin'e (veya top'a) merge etmeye zorlamak; bu sayede fastbin invariant'larını kırıp double-free, overlap veya libc leak kurmak.

Mechanism

glibc'in ptmalloc'u, yakın zamanda free edilmiş küçük chunk'ları hız için singly-linked fastbins içinde tutar ve coalescing'lerini erteler. malloc_consolidate(), bu ertelenmiş fastbin'leri sonradan boşaltan rutindir: her fastbin'i dolaşır ve her chunk için in-use bit'i temizler, komşu free chunk'larla backward/forward merge eder ve sonucu doubly-linked unsorted bin'e taşır (ya da top chunk'a merge eder). Çalıştıktan sonra fastbinY array girdisi NULL'a reset edilir.

Note

Exploit edilebilir özellik consolidation'ın ne yaptığı kadar ne zaman tetiklendiğidir. Bir chunk'ın muhasebesi consolidate edildiği an değişir: "bir fastbin'de, in-use bit set, singly linked" durumundan "unsorted bin'de, free, doubly linked" durumuna geçer. Bir pointer'ı ya da bir free-list varsayımını consolidation'dan önce donduran kod artık yanlıştır. malloc_consolidate() (glibc internals'a göre) birkaç yerde tetiklenir, özellikle: _int_malloc bir large-bin boyutunda request servis ederken; _int_malloc hiçbir bin request'i karşılayamadığında ve top chunk çok küçük olduğunda; _int_free free edilen chunk'ın boyutu >= FASTBIN_CONSOLIDATION_THRESHOLD (0x10000 / 65536) olduğunda; ve koşulsuz olarak malloc_trim() ile mallopt() içinde. Allocation boyutlarını kontrol eden bir attacker bu yüzden consolidation'ı talep üzerine çağırabilir — en basitiyle tek bir büyük malloc ile — ve sebep olduğu state transition'ı silaha dönüştürebilir.

İki kazanç öne çıkar. İlki, fastbin double-free check'ini bypass etmek: bir fastbin'e free edilmiş chunk hâlâ kendi in-use bit'ini taşır; consolidation bunu temizleyip chunk'ı unsorted bin'e taşır, böylece aynı pointer'ı tekrar free etmek artık "double free or corruption (fasttop)" guard'ına takılmaz — sonuç bir duplicate chunk olur. İkincisi, bir libc leak / write target: fastbin chunk'ları yalnızca ileri bir heap pointer saklar, ama doubly-linked unsorted bin'e consolidate edildiğinde bir chunk'ın fd/bk alanları main_arena'ya (libc) işaret eder, dolayısıyla bir read-after-free artık sadece bir heap adresi değil bir libc adresi leak eder.

Walkthrough

shellphish'in how2heap'indeki fastbin_dup_consolidate.c, consolidation'ı bir double-free'ye dönüştürür. Modern glibc'de (tcache mevcut) önce tcache'i doldurur ki hedef free fastbin'e ulaşsın:

/* fill the 0x40 tcache bin so the next free goes to the fastbin */
for (int i = 0; i < 7; i++) tcache[i] = malloc(0x40);
for (int i = 0; i < 7; i++) free(tcache[i]);

void *p1 = malloc(0x40);
free(p1);                 /* tcache full -> p1 lands in the 0x40 fastbin   */

void *p2 = malloc(0x400); /* LARGE request -> triggers malloc_consolidate  */
                          /* p1's fastbin chunk consolidates into top;     */
                          /* p2 is carved from top and p1 == p2 (alias!)   */

free(p1);                 /* p1 is no longer "in a fastbin" -> no fasttop
                             double-free abort; it re-enters a bin         */

void *p3 = malloc(0x400); /* hands back the same chunk again: p2 == p3     */

malloc(0x400) buradaki kaldıraçtır: malloc_consolidate'i zorlar ve p1'in fastbin üyeliğini temizler, böylece sonraki free(p1) bir double free olarak tespit edilmez. Sonuç tek bir chunk'a işaret eden iki canlı pointer'dır (p2, p3).

fastbin_dup_consolidate.c'nin beklenen sonucu

Allocated two fastbins: p1=0x...  p2=0x...
Now free p1!
Allocated large chunk p3=0x...   <- the malloc that triggers consolidate
Trigger the double free vulnerability!  free(p1) again
Now p1, p2 and p3 are pointing to the same chunk:
  p1 = 0x55...010
  p2 = 0x55...010
  p3 = 0x55...010
İki (veya üç) bağımsız allocation aynı adrese çözümlenir — fastbin double-free guard'ına hiç takılmadan elde edilmiş bir duplicate chunk.

Warning

Boyutlar versiyona duyarlıdır. "Large" request, large-bin/consolidation yolunu tetikleyecek kadar büyük olmalıdır (≈0x400+) ve glibc ≥ 2.26'da tcache'i hesaba katmalısın (doldur ya da tcache aralığı dışındaki boyutları kullan), aksi hâlde free fastbin'e hiç ulaşmaz. FASTBIN_CONSOLIDATION_THRESHOLD (0x10000) yolu farklı bir trigger'dır (büyük bir free sırasındaki consolidation), bu PoC'nin kullandığı değildir.

Detection

Consolidation'dan sonra programın hâlâ canlı sandığı stale bir pointer bir use-after-free / double-free'dir; duplicate chunk yanlış kullanıldığında ASan ve glibc'in kendi malloc(): ... / free(): double free or corruption mesajları tetiklenir. Bir büyük allocation boyunca bin durumunu diff'leyen heap tracker'lar, fastbin'lerin beklenmedik şekilde unsorted bin'e boşaldığını fark edebilir.

Mitigation

Bu istismar bir double-free ya da use-after-free üzerine biner; dangling/duplicate pointer'ı elemek onu kaldırır. tcache double-free key'leri, fastbin fasttop check'leri ve unsorted-bin consistency check'leri pek çok takip eden corruption'ı yakalar. Eager şekilde coalesce eden (ertelenmiş fastbin'i olmayan) hardened allocator'lar, consolidation-zamanı boşluğunu tamamen ortadan kaldırır.

References