Skip to content

Fake object forgery

Free edilmiş bir object'in gömülü SLUB freelist next-pointer'ını overwrite et, böylece sonraki allocation, fake bir kernel object'inin forge edildiği attacker-seçimli bir adreste dağıtılsın.

Mechanism

SLUB allocator'ı free object'leri singly-linked per-CPU bir freelist'te tutar. Bir sonraki free object'e olan link, free object'in kendi memory'sinin içinde (unsigned long)object + s->offset (yani freeptr_addr) konumunda saklanır. Allocation'da SLUB freelist head'ini döndürür ve head = get_freepointer(head) ile ilerler; free'de set_freepointer() ile önceki head'i yeni object'e yazar.

Note

İstismar edilen invariant, SLUB'ın free bir chunk'tan okuduğu next-pointer'a güvenmesidir. Bir attacker free bir object'e yazabilirse (UAF) ya da bitişik free bir chunk'a overflow yapıp o next-pointer'ı keyfi bir değer T ile değiştirebilirse, araya giren bir allocation sonrasında allocator T'yi döndürür. Attacker T konumundaki memory'yi önceden forge edilmiş bir object'le (fake bir struct file, fake ops tablosu, vb.) doldurur, böylece o cache'in bir sonraki "meşru" allocation'ı attacker-şekilli veriye düşer — bir arbitrary-allocation / fake-object primitive'i.

Warning

Modern kernel'larda freelist pointer'ı XOR-obfuscate edilir (CONFIG_SLAB_FREELIST_HARDENED), bu yüzden geçerli encode edilmiş bir next-pointer forge etmek önce per-cache s->random secret'ını ve storage adresini leak etmeyi gerektirir; kör bir overwrite unmapped bir adrese decode olur ve oops yaptırır.

Walkthrough

mm/slub.c içindeki ilgili accessor'lar:

/* simplified: pointer lives inside the free object body */
static inline void *get_freepointer(struct kmem_cache *s, void *object)
{
        unsigned long ptr_addr = (unsigned long)object + s->offset;
        return freelist_ptr_decode(s, *(freeptr_t *)ptr_addr, ptr_addr);
}

static inline void set_freepointer(struct kmem_cache *s, void *object, void *fp)
{
        unsigned long freeptr_addr = (unsigned long)object + s->offset;
        BUG_ON(object == fp);   /* under hardening: kills same-object double-free */
        *(freeptr_t *)freeptr_addr = freelist_ptr_encode(s, fp, freeptr_addr);
}

Kavramsal exploitation akışı (free bir chunk'a overflow ya da UAF):

  1. Hedef cache'i öyle groom et ki free bir chunk kontrol edilebilir bir object'e bitişik otursun (bkz. slab grooming).
  2. Bug'ı tetikleyerek free chunk'ın next-pointer'ını attacker-controlled memory'nin adresiyle overwrite et (örn. bir setxattr/userfaultfd heap spray ile doldurulmuş bir bölge ya da bilinen bir physmap konumu).
  3. Bozulmuş chunk'ı tüketmek için bir kez allocate et; bir sonraki allocation forge edilmiş adresi döndürür.
  4. Victim object tipini o adreste allocate et — allocator artık attacker'ın fake object'ini dağıtır; bu, forge edilmiş function pointer'ları ya da yetkiyle ilgili data field'ları taşıyabilir.
Offset 0 neden garanti değil

Eski writeup'lar next-pointer'ın object offset 0'da durduğunu varsayar, ama s->offset allocator-seçimlidir; hardened kernel'larda object'in ortasına doğru kaydırılır, böylece küçük linear overflow'lar ona ulaşamaz. Offset 0 varsaymak yerine her zaman hedef kernel için gerçek s->offset davranışını oku.

Mitigation

CONFIG_SLAB_FREELIST_HARDENED next-pointer'ı obfuscate eder ve BUG_ON(object == fp) ekler; CONFIG_SLAB_FREELIST_RANDOM başlangıç object sırasını karıştırır, böylece bitişiklik deterministik olmaz. Bunlar birlikte attacker'ları doğrudan freelist forgery yerine cross-cache ve data-only yaklaşımlara iter.

References