Skip to content

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:

  1. Hedef size class'ı "ısıtmak" için allocate et, böylece bir span thread cache'e ait olur.
  2. Victim('ler)i free et, böylece size-class list'in head'inde otururlar.
  3. Head object'in ilk word'ünü (next link'ini) boz.
  4. İki kez allocate et — ikinci malloc forge 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.

References