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):
- Hedef cache'i öyle groom et ki free bir chunk kontrol edilebilir bir object'e bitişik otursun (bkz. slab grooming).
- 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).
- Bozulmuş chunk'ı tüketmek için bir kez allocate et; bir sonraki allocation forge edilmiş adresi döndürür.
- 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.