Skip to content

Glibc Safe-Linking

glibc 2.32+, tcache/fastbin singly-linked list'lerindeki fd pointer'larını (&pos >> 12) ile XOR'lar; böylece bir freelist pointer'ını corrupt eden bir attacker'ın heap'in page address'ini zaten biliyor olması gerekir.

Mechanism

tcache ve fastbin'ler, her chunk'ın fd field'ı boyunca dizilen singly-linked free list'lerdir. Klasik "tcache poisoning" primitive'i o fd'yi attacker'ın seçtiği bir address ile overwrite eder, böylece bir sonraki allocation attacker'ın istediği bir pointer döndürür. Safe-Linking (Check Point Research, 2020; glibc 2.32'ye merge edildi, default on), böyle her pointer'ı store etmeden önce mangle eder ve read'de demangle eder:

#define PROTECT_PTR(pos, ptr) \
  ((__typeof (ptr)) ((((size_t) pos) >> 12) ^ ((size_t) ptr)))
#define REVEAL_PTR(ptr)  PROTECT_PTR (&ptr, ptr)

pos, pointer'ın store edildiği address'tir; ptr ise linked-list pointer'ıdır. pos'u 12 (PAGE_SHIFT) kadar sağa kaydırmak, store konumunun page number'ını — ASLR ile randomize edilmiş — store edilen değere karıştırır.

Invariant enforced

Store edilmiş bir freelist fd'si artık ham next-chunk address'i değil, (storage_page >> 12) XOR next'tir. Seçilen bir hedefe demangle olan bir fd forge etmek için attacker'ın pointer'ın bulunduğu heap page'ini bilmesi gerekir — yani bir heap-base leak'e ihtiyaçları vardır. Rastgele bir address'in kör injection'ı engellenir.

Buna ikinci, ucuz bir check eklenir: demangle edilmiş pointer'lar chunk alignment'ını (64-bit'te 16 byte) sağlamalıdır. Rastgele/yanlış forge edilmiş bir pointer 64-bit'te 16'da 15 kez alignment'tan geçemez, bu da birçok corruption'ı anında abort'a dönüştürür.

Walkthrough

2.32 öncesi ve sonrası davranışı küçük bir tcache poison ile karşılaştır:

#include <stdlib.h>
#include <stdio.h>
int main(void) {
    void *a = malloc(0x20);
    void *b = malloc(0x20);
    free(a); free(b);              // tcache: head -> b -> a
    // b's fd now stores PROTECT_PTR(&b->fd, a) on glibc >= 2.32,
    // i.e. (((size_t)&b->fd) >> 12) ^ (size_t)a  -- not the raw address of a.
    printf("stored fd word = %p\n", *(void **)b);
    printf("real a         = %p\n", a);
    return 0;
}
Expected output (glibc >= 2.32)
stored fd word = 0x55a1c3d2e010   # mangled: differs from a, low bits scrambled
real a         = 0x55a1c3d2f2a0
# On glibc < 2.32 the stored fd word equals real a exactly.

Not a leak-proof barrier

Safe-Linking entropy'yi yalnızca ASLR'dan ödünç alır. Bir heap-base/heap-address info leak ile attacker PROTECT_PTR'ı yeniden hesaplayıp geçerli bir mangle edilmiş pointer forge edebilir, dolayısıyla çıtayı yükseltir ama freelist-corruption exploitation'ını ortadan kaldırmaz. Ayrıca free edilmiş bir chunk'ın fd'sinden geçmeyen linear heap overflow'lara karşı hiçbir şey yapmaz.

Mitigation

Safe-Linking'i glibc-tcache-double-free-key, heap ASLR ve allocator hardening ile eşleştir. Alignment check, doğru hizalanmış hedeflerin (16-byte sınırlarına groom edilmiş page-aligned heap) pratik saldırı yolu olarak kaldığı anlamına gelir, dolayısıyla altta yatan UAF/overflow'u ortadan kaldırmanın yerini almaz.

References