Skip to content

kmalloc cache feng shui

SLUB allocator'ın per-cache freelist'ini, seçilen bir boyutta nesneleri spray'leyip seçici biçimde free edip reclaim ederek bilinen bir state'e sür — böylece bir vulnerable nesne ile bir attacker-controlled nesne şansla değil, inşa yoluyla bitişik ya da overlapping olarak düşsün.

Mechanism

SLUB, kmalloc(n)'i bir size-class cache'ten (kmalloc-32, kmalloc-64, …) servis eder. Her cache, nesneleri bir slab'den (bir ya da birkaç page) per-CPU bir freelist yoluyla dağıtır. Attacker'ın dayandığı belirleyici özellik: freelist LIFO'dur — en son free edilen nesne bir sonraki allocate edilen olur. Taze bir slab içindeki allocation sırası ise sequential'dır (artan adresler), dolayısıyla boş bir slab tahmin edilebilir biçimde dolar.

Note

"Feng shui", bu allocation'ların bilinçli düzenlenmesidir, böylece heap akıl yürütebileceğin bir layout'a ulaşır. Üç kaldıraç tüm işi yapar: (1) LIFO reuse — nesne X'i free et, bir sonraki aynı boyutlu kmalloc X'i döndürür; (2) taze slab'lerin sequential dolması — bir slab yeni ve boş olduğunda ardışık allocation'lar adres olarak bitişiktir, ki bir overflow'un ihtiyaç duyduğu şey budur; (3) cache merging — birçok farklı kmalloc çağrıcısı boyuta göre tek bir cache'i paylaşır, dolayısıyla attacker'ın ulaşabildiği bir spray nesnesi ile aynı boyutta bir victim nesne aynı freelist için yarışır. Önce partial slab'leri tüketerek (böylece sonraki allocation'lar taze, sıralı slab'lerden gelir), ardından hedeflenmiş free'lerle delikler açarak, vulnerable nesneyi tam olarak bir sonraki reclaim spray'inin onu overwrite edeceği yere (UAF) ya da overflow edeceğin şeyin yanına (OOB) yerleştirirsin.

İnvariant: allocator yerleşimi, allocation/free sırasının deterministic bir fonksiyonudur — eğer önce entropy'yi (önceden var olan free delikleri ve modern kernel'lerde freelist randomisation'ı) cache'leri tüketip yeniden doldurarak ortadan kaldırırsan.

Walkthrough

kmalloc-32'ye karşı kanonik bir UAF-reclaim groom'u:

  1. Target cache'i incele. /sys/kernel/slab/<cache>, spray'i planlamak için ihtiyaç duyduğun geometriyi açığa çıkarır:
$ cat /sys/kernel/slab/kmalloc-32/objs_per_slab
128
$ cat /sys/kernel/slab/kmalloc-32/cpu_partial
30
$ ls -l /sys/kernel/slab/kmalloc-32        # aliasing: which callers merge here
... kmalloc-32 -> :t-0000032
  1. Partial slab'leri defragment et / tüket. Mevcut free delikleri tüketmeye yetecek kadar same-size nesne spray'le, böylece sonraki allocation'lar taze, sequential dolan slab'lerden gelir:
for (int i = 0; i < objs_per_slab * (cpu_partial + 2); i++)
    spray_obj();          // any kmalloc-32 allocation you control (e.g. msg_msg seg, key payload)
  1. Victim'i, sonra bir delik yerleştir. Vulnerable nesneyi taze bir slab'de oturacak şekilde allocate et, sonra onu free et (UAF) ya da overflow etmeyi planladığın komşuyu free et.

  2. Controlled bir nesne ile reclaim et. LIFO yüzünden, hemen bir sonraki same-size allocation freed slot'u yeniden kullanır. Reclaim nesnesini dangling chunk'ı kaplayacak şekilde spray'le:

free(victim);                         // creates the hole / dangling pointer
for (int i = 0; i < 64; i++)
    open("/proc/self/stat");          // sprays struct seq_operations into kmalloc-32 -> overlaps victim

Artık dangling reference ile seq_operations (function pointer'lar) aynı byte'ları alias eder — bir leak (kernel-base-leak-via-ops-pointer.md) ya da bir hijack (kernel-function-pointer-overwrite.md) için kurulum.

Warning

CONFIG_SLAB_FREELIST_RANDOM, yeni oluşturulan slab'larda objelerin başlangıç yerleşim sırasını (shuffle_freelist() ile slab creation'da) Fisher-Yates ile karıştırır; sonuçta freelist traversal'ı öngörülemez olur, dolayısıyla "X'i free et sonra alloc et" artık X'in komşusunu adres sırasına göre tetikçe döndürmez. Karşı önlem, sequential adjacency'ye güvenmeyi bırakmaktır: çok delik aç ve çok reclaimer spray'le, sonra hangisinin overlap ettiğini tahmin etmek yerine hangisinin overlap ettiğini tespit et (örn. hangi sprayed nesne artık corrupt okunuyor). Ayrıca cross-CPU etkilerine dikkat et — per-CPU freelist per-core'dur, dolayısıyla thread'lerini pin'le (sched_setaffinity) yoksa free ve reclaim'in farklı freelist'lere isabet edebilir. Son olarak, SLAB_ACCOUNT/kmalloc-cg-* ve RANDOM_KMALLOC_CACHES çağrıcıları ayrı cache'lere böler, dolayısıyla spray nesnen ile victim'in gerçekten bir cache paylaştığını /sys/kernel/slab alias'ı üzerinden doğrula.

Detection

  • KASAN/KFENCE, heap'in ne kadar temiz düzenlendiğinden bağımsız olarak groom'un kurduğu UAF/overflow'u gözlemler.
  • Spray imzaları: hemen ardından free'ler gelen same-size allocation'ların büyük patlamaları (toplu msgsnd, open("/proc/self/stat"), key/add_key, crafted ancillary data ile sendmsg).

Mitigation

  • CONFIG_SLAB_FREELIST_RANDOM, deterministic intra-slab sıralamayı kaldırır.
  • CONFIG_SLAB_FREELIST_HARDENED, freelist next-pointer'ını XOR ile gizler ve freelist-pointer corruption varyantlarını engeller (freelist-poisoning.md'a bak).
  • RANDOM_KMALLOC_CACHES ve kmalloc-cg-* separation, cache merging'i azaltır, böylece attacker spray nesneleri ve victim'ler daha seyrek bir freelist paylaşır — attacker'ları cross-cache-attack.md'a iter.
  • Quarantine tarzı gecikmeli free (KASAN SLAB_QUARANTINE), freed nesneleri yeniden kullanımdan önce tutar ve anlık LIFO reclaim'i reddeder.

References