tcmalloc exploitation¶
tcmalloc'un per-thread (veya per-CPU) singly-linked free list'lerini bozarak — tipik olarak bir double-free veya freed bir object'in gömülü next pointer'ına overflow ile — sonraki bir allocation'ın attacker'ın seçtiği bir adresi döndürmesini sağlamak.
Mechanism¶
Note
tcmalloc (Google'ın Thread-Caching Malloc'u) bir front-end / middle-end / back-end
allocator'dır. Front-end her thread'e (eski ThreadCache modu) veya her mantıksal
CPU'ya (modern per-CPU modu) hızlı, büyük ölçüde lock-free bir cache verir. Küçük
request'ler ~60–80 size class'tan birine yuvarlanır; her size class free object'lerin
tek bir singly-linked list'i ile sunulur. free'de object kendi size class list'inin
başına eklenir; malloc'ta head pop edilir. Önemlisi, next pointer free object'in
kendi içinde saklanır (freed user region'ının ilk word'ü), tıpkı bir glibc fastbin veya
tcache gibi. Bir thread cache boşaldığında central free list'ten bir batch çeker; o da
page heap back-end'i tarafından yönetilen span'lerden (contiguous page'lerin
dizilerinden) oyulmuş object'ler dağıtır.
Allocator'ın güvendiği invariant, bir free object'in next word'ünün kendisinin yazdığı
geçerli bir list link olmasıdır. Bir attacker bu word'ü yazabilirse — freed bir komşuya
overflow ile, bir use-after-free ile veya aynı object'i list'e iki kez sokarak (double
free) — list zehirlenir (poisoned). Sonraki pop attacker'ın kontrol ettiği bir pointer
döndürür; bu da arbitrary-address bir allocation ve dolayısıyla bir arbitrary write verir.
Walkthrough¶
Kavramsal olarak glibc tcache poisoning ile aynı, bir tcmalloc size-class list'e uygulanmış:
/* size class S, list head H -> objects are singly linked via first word */
void *a = malloc(64); /* same size class */
void *b = malloc(64);
free(a); /* list: a -> ... */
free(b); /* list: b -> a -> ... */
/* overflow / UAF: overwrite the next-pointer stored at the head of b */
*(void **)b = (void *)target; /* b.next = target */
void *x = malloc(64); /* pops b */
void *y = malloc(64); /* pops 'target' -> attacker-controlled chunk */
/* writing through y now writes to `target` */
Not: yukarıdaki at the head of b ifadesi, list'in head'ini değil, b'nin kendi
memory region'ının başındaki (ilk word'deki) gömülü next pointer'ı kastediyor.
Aynı object'in bir double free'si, ayrı bir overflow olmadan poisoning'i sağlar:
free(a); free(a); sonrası object kendine link'lenir / list'te iki kez görünür, böylece
sonraki bir allocation başka bir yerde hâlâ "live" olan bir bellek döndürür ve bir alias'ın
diğerinin next'ini overwrite etmesine izin verir.
Zehirlenmiş bir object'i ortaya çıkaran front-end refill yolu
Zehirlenmiş bir next, yalnızca object front-end list'te yaşadığı sürece dikkate alınır.
Thread cache central free list'e geri flush edilirse, span seviyesindeki bookkeeping (raw
pointer'lar değil, bir span içindeki object index'leri) span dışı bir pointer'ı
reddedebilir. Güvenilir exploitation bozulmuş object'i per-thread / per-CPU cache içinde
tutar:
- Hedef size class'ı "ısıtmak" için allocate et, böylece bir span thread cache'e ait olur.
- Victim('ler)i free et, böylece size-class list'in head'inde otururlar.
- Head object'in ilk word'ünü (
nextlink'ini) boz. - İki kez allocate et — ikinci
mallocforge edilmiş adresi döndürür.
Warning
tcmalloc, exploit yazanları zorlayan iki açıdan glibc'den ayrılır. (1) Span'ler object
index'leri saklar, bazen bir "unrolled linked list" olarak (üç hazır object artı next
index'i tutan dördüncü bir slot) — yani cross-span forge edilmiş pointer'lar, bir object
front-end'den ayrıldığında validation'da başarısız olabilir. (2) per-CPU modunda cache
geçerli CPU ile anahtarlanır; thread'i pin'le (sched_setaffinity), böylece free'ler ve
takip eden alloc'lar aynı per-CPU slab'a düşer. Sanitized / hardened build'ler, naive bir
double free'yi abort ettiren freelist check'leri ekler.
Detection¶
- Allocator'ın kendi debugging / sanitizer desteğiyle ve ASan ile build et; double free'ler ve freed object'lere yapılan out-of-bounds write'lar bozulma noktasında yakalanır.
- Front-end pop yolu içinde unaligned veya non-span bir pointer döndürerek oluşan crash'ler, list poisoning'in güçlü bir işaretidir.
Mitigation¶
- tcmalloc'u güncel tut; modern release'ler per-CPU front-end'i tercih eder ve pop edilen object'lerin sahip olan span'e ait olduğunu doğrular.
- Primitive'i besleyen bug sınıfını ortadan kaldır: double-free, use-after-free ve freed object'lere heap overflow.