Skip to content

tcache relative write

Bir tcache chunk'ının next pointer'ının kısmi (düşük-byte) bir overwrite'ı; tam bir adres leak'i olmadan sonraki allocation'ı yakındaki bir heap objesine yönlendirir.

Mechanism

Suistimal edilen invariant: aynı heap page içine işaret etmek için yalnızca next'in düşük byte'larının değişmesi gerekir

Standart tcache-poisoning, kuyruğa alınmış bir tcache_entry'nin tüm next (fd) word'ünü overwrite eder:

typedef struct tcache_entry { struct tcache_entry *next; uintptr_t key; } tcache_entry;

tcache_get, e->next'i hiçbir bounds kontrolü olmadan list head'ine yükseltir, dolayısıyla sonraki malloc, next neyi adlandırıyorsa onu döndürür. Göreli varyant yalnızca next'in düşük 1–2 byte'ını yeniden yazar. Heap allocation'lar adreslerinin üst bit'lerini paylaştığı için (aynı arena page / mmap region'ı), düşük byte'ı çevirmek döndürülen pointer'ı birkaç slot ötedeki farklı bir chunk'a taşır — bitişik bir in-use objeyle overlap eder — yalnızca tek-byte'lık bir overflow kullanarak (örneğin bir off-by-one ya da null-byte overflow). Üst bit'ler için hiçbir heap leak gerekmez, yalnızca göreli offset bilgisi; bu da onu tcache için birincil bir heap-ASLR bypass'ı yapar.

glibc ≥ 2.32'de next, PROTECT_PTR(pos, ptr) = ((pos >> 12) ^ ptr) ile mangle edilir. XOR key'i (pos >> 12) bir page içinde sabittir, dolayısıyla düşük 12 bit mangling'i değişmeden atlatır: en anlamsız byte hâlâ doğrudan düzenlenebilir, bu da relative-write hilesini safe-linking altında canlı tutar.

Walkthrough

tcache-poisoning'in üzerine kurulur; burada write kısmidir.

char *a = malloc(0x40);   // ... a, b, c laid out adjacently on the heap
char *b = malloc(0x40);
char *t = malloc(0x40);   // the object we want a second handle to
free(b);                  // tcache[0x40]: [ b ]  (b->next = mangled 0)

// VULNERABILITY: 1-byte relative overwrite of b->next's LSB
// shift the head from b to point at t's user data within the same page
*(unsigned char *)b = (unsigned char)((size_t)t & 0xff);   // low byte only

malloc(0x40);             // returns b
char *dup = malloc(0x40); // returns ~t  -> overlapping handle to t

Beklenen çıktı: ikinci allocation t'nin içine (ya da bir slot ötesine) düşer, canlı bir objeyi alias'layan overlapping bir pointer verir — tipik olarak hiç tam bir heap adresi leak'lemeden bitişik bir struct'ın size/pointer alanlarını corrupt etmek için kullanılır.

Alignment ve düşük nibble

Forge edilmiş düşük byte 16-byte alignment'ı korumalı (& 0xf == 0) yoksa tcache_get, malloc(): unaligned tcache chunk detected ile abort eder. Düşük nibble'ı zaten 0 olan bir hedef seç. Tek bir byte yalnızca aynı 256-byte pencere içindeki hedeflere ulaşır; daha büyük sıçramalar için iki byte overwrite et (ve pos >> 12'nin farklılaştığı bir page sınırını geçmeye dikkat et).

Mitigation

  • tcache_get'teki aligned_OK kontrolü, forge edilmiş LSB'nin 16-byte aligned kalmasını zorlar.
  • Safe-Linking bir düşük-byte düzenlemesini durdurmaz (key page-sabittir), dolayısıyla relative write hayatta kalır; tam hardening, glibc'nin sağlamadığı, page granülaritesinden daha ince bir heap layout randomization gerektirir.

References