tcache metadata poisoning¶
tcache_perthread_struct'ın kendisini (counts/entriesdizilerini) corrupt ederek, allocator'ın her size class için attacker'ın seçtiği pointer'ları aynı anda dağıtmasını sağlamak.
Mechanism¶
Suistimal edilen invariant: per-thread tcache_perthread_struct'ın kendisi bir heap chunk'tır ve tamamen güvenilirdir
tcache defter-tutması, thread'in heap'indeki ilk chunk olan tek bir heap-allocated kontrol yapısında yaşar:
typedef struct tcache_perthread_struct
{
uint16_t counts[TCACHE_MAX_BINS]; /* TCACHE_MAX_BINS == 64 */
tcache_entry *entries[TCACHE_MAX_BINS];
} tcache_perthread_struct;
static __thread tcache_perthread_struct *tcache = NULL;
tcache_get/tcache_put bu dizileri doğrudan okur ve yazar:
e->next = PROTECT_PTR (&e->next, tcache->entries[tc_idx]);
tcache->entries[tc_idx] = e;
++(tcache->counts[tc_idx]);
Sade tcache-poisoning bir free edilmiş chunk'ın next'ini forge ederken,
metadata poisoning kontrol struct'ının entries head pointer'larını ve
counts'unu forge eder. entries[tc_idx] ham, mangle edilmemiş list
head'i olduğundan (yalnızca chunk'ların içindeki next link'leri
PROTECT_PTR ile mangle edilir), entries[tc_idx]'i yazmak, o size'ın
sonraki malloc'unun nerede döndürüleceğini doğrudan ayarlar — o ilk pop
için hiçbir safe-linking dansı gerekmez. Bir counts[tc_idx]'i artırmak,
allocator'ı bir bin'in boş olmadığına inandırır ki tcache fast path'ini alsın.
Tüm struct'ı kontrol etmek, malloc'u her size class için eşzamanlı olarak bir
arbitrary-address allocator'a dönüştürür.
Walkthrough¶
tcache_perthread_struct heap'teki ilk chunk'tır (64-bit'te size 0x290),
dolayısıyla ona ulaşan erken bir UAF/overflow ya da ona bir pointer döndüren
bir tcache-poisoning, alanlarını overwrite etmene olanak tanır.
// 1. Obtain a write handle to the tcache control struct.
// e.g. poison a freed 0x290 chunk's next to point at the heap base,
// or free the very first allocation and reclaim it.
uint16_t *counts = (uint16_t*)tcache_struct; // counts[64]
void **entries = (void**)(tcache_struct + 0x80); // entries[64]
// 2. Forge a bin: make the size-0x40 bin (tc_idx) claim one ready chunk
size_t tc_idx = 2; // (0x40 user size class)
counts[tc_idx] = 1; // pretend the bin is non-empty
entries[tc_idx] = (void*)target; // head -> arbitrary address (unmangled!)
// 3. Allocate: tcache_get pops `target` directly.
void *p = malloc(0x40); // returns `target`
Beklenen çıktı: p == target. entries[] list head'i olduğundan
(chunk-içi bir next değil), bu ilk pop'a hiçbir PROTECT_PTR mangling'i
uygulanmaz, dolayısıyla ilk arbitrary allocation için bir heap leak gereksizdir.
Neden genelleşir
Birkaç i için counts[i] ve entries[i]'yi ayarlamak, kontrol struct'ının
tek bir corruption'ının her biri seçilmiş bir adreste olmak üzere birçok
farklı malloc(size) isteğini servis etmesine olanak tanır — tek seferlik
bir poison yerine etkili biçimde global bir allocation primitive'i.
tcache_get hâlâ alignment'ı kontrol eder
Pop edilen head aligned_OK'i sağlamalı, yoksa
malloc(): unaligned tcache chunk detected tetiklenir. Forge edilmiş
entries[i] 16-byte aligned olmalıdır.
Detection¶
- Herhangi bir bilinen eşlenmiş heap aralığının dışına düşen bir pointer
döndüren
malloc(), ya da aynı adresi alias'layan iki canlı allocation, heap-debug araçlarında (AddressSanitizer, glibcMALLOC_CHECK_, electric-fence) yakalanır. - Process log'larında/core'larında abort string'leri:
malloc(): unaligned tcache chunk detected(forge edilmiş hizasız head) ve tcache-key /countstutarlılık abort'ları, runtime'da kontrol struct'ının kurcalandığına işaret eder. - EDR/telemetri:
.bss, GOT,stackya da hook region'larına düşen ve hemen ardından üzerinden bir write gelen birmalloc()türevli pointer güçlü bir anomalidir; toplanan backtrace'lerdetcache_get/_int_mallociçindeki çökmeleri ilişkilendir. - Fuzzing/CI: hedefleri ASan altında çalıştır; ilk heap chunk'ına (perthread struct) ulaşan UAF ve overflow, bir primitive haline gelmeden önce güvenilir biçimde yakalanır.
Mitigation¶
- Safe-Linking chunk-içi
nextlink'lerini korur ama kontrol struct'ındakientries[]head'lerini korumaz, dolayısıyla bu yol pointer mangling'den etkilenmez. tcache_get'tekialigned_OKhizasız forge edilmiş head'leri reddeder.- Kontrol struct'ını öngörülemez biçimde yerleştiren bir heap layout'u ya da guard page'ler ona ulaşmanın maliyetini yükseltir; glibc'nin kendisi onu izole etmez.