Skip to content

Windows kernel Segment Heap pool grooming

19H1'den beri Windows kernel pool arka planı Segment Heap'tir (VS / LFH / Segment / large-alloc backend'leri); groomer'ların bucket bazlı allocator'ı deterministik komşuluk için nasıl şekillendirdiğini, legacy pool ile kontrast içinde anlatır.

Mechanism

Note

Windows 10 19H1'e kadar kernel pool, coalescing yapan bir free-list allocator'dı: komşu free chunk'lar birleştirilir, allocation'lar en yakın uygun bloktan kesilirdi. Bu, POOL_HEADER'ın BlockSize alanı üzerinden boyut seçerek chunk yerleşimini görece serbestçe manipüle etmeye izin veriyordu. 19H1 ile birlikte kernel pool backend'i Segment Heap'e taşındı ve allocator dört alt-servise ayrıldı:

  • kLFH (kernel Low Fragmentation Heap) — kabaca 10x4000 byte arası request'leri sabit size bucket'larına yönlendirir. Chunk'lar hâlâ bir _POOL_HEADER ile prefix'lenir (64-bit'te 0x10), yani tag ve size metadata'sı encode edilmez.
  • VS (Variable Size) — bucket'a düşmeyen orta boy request'ler; chunk'lar _HEAP_VS_CHUNK_HEADER ile prefix'lenir ve bu header encode edilir.
  • Segment alloc ve Large alloc — page-granüler ve büyük allocation'lar.

Groomer için kritik invariant şudur: kLFH coalescing yapmaz. Bir bucket içindeki bir slot free edildiğinde, allocator onu komşu free slot'larla birleştirmez; slot aynı boyuttaki bir sonraki allocation için hazır bekler. Bu yüzden bir hedef bucket'a aynı boyutta object'ler basıldığında, bunlar o bucket'ın subsegment'i içinde deterministik olarak komşu kalır. Legacy allocator'da free-then-split davranışı yerleşimi bulanıklaştırırken, kLFH'te "aynı size → aynı bucket → öngörülebilir adjacency" ilişkisi grooming'i çok daha güvenilir kılar.

Warning

Bucket bazlı model iki yönlü bir kılıçtır: determinism savunucu için de bir imzadır (tek bir process'in bir bucket'ı doldurması gözlemlenebilir) ve VS backend'inin _HEAP_VS_CHUNK_HEADER encoding'i, header üzerinden yürüyen naif overwrite'ları bozar.

Walkthrough

Bayet & Fariello (SSTIC 2020) ile Connor McGarr'ın public writeup'larında tarif edilen kavramsal akış, klasik "spray → hole punch → adjacency" pattern'inin Segment Heap'e uyarlanmış hâlidir. Yüksek seviye mantık:

  1. Backend seçimi. Vulnerable object'in boyutunu ölçün ve onu hangi backend'in servis ettiğini belirleyin. kLFH bucket'ına düşen bir boyut, _POOL_HEADER'ın encode edilmemiş olması sayesinde en erişilebilir hedeftir.

  2. kLFH'i aktive edin. Bir bucket kLFH'e ancak yeterli sayıda allocation o size class'a geldikten sonra "promote" olur. Groomer önce hedef boyutta çok sayıda benign object allocate ederek bucket'ı kLFH moduna geçirir.

// High-level fragment: aynı size class'ta çok sayıda object -> bucket kLFH'e döner.
// (İllüstratif; gerçek exploit'te object seçimi ve sayısı build'e göre ayarlanır.)
for (int i = 0; i < N; i++)
    h[i] = CreateEventA(NULL, FALSE, FALSE, NULL);   // 'Even' tag, ~0x80B chunk
  1. Defragmentation + spray. Bucket'ı aynı boyutta object'lerle doldurarak önce mevcut delikleri kapatın (defrag), sonra üstüne temiz bir dizi attacker-controlled chunk basın. Amaç, subsegment içinde bilinen bir sıra elde etmektir.

  2. Hole punch. Sprayed object'lerin bir kısmını (örneğin her ikincisini) free ederek hedef bucket'ta eşit boyutlu free slot'lardan oluşan bir dama tahtası bırakın. kLFH coalescing yapmadığı için bu slot'lar birleşmez.

for (int i = 0; i < N; i += 2)
    CloseHandle(h[i]);   // aynı bucket'ta free slot'lar açar; coalescing yok
  1. Victim'i yerleştirin. Vulnerable driver path'ini tetikleyin; bug'ın object'i açtığınız free slot'lardan birine, hâlâ sprayed ve içeriğini kontrol ettiğiniz bir komşu chunk'ın yanına düşer. Bir linear overflow için victim'i kontrollü bir chunk'ın önüne; bir UAF için free edilmiş victim slot'unu kontrollü bir allocation ile reclaim edecek şekilde konumlandırın.
Cross-cache mantığı (kavramsal)

Vulnerable object ile işe yarar bir "target" object farklı pool tipi ya da farklı bir yaşam döngüsündeyse, groomer'lar bir bucket'ın tüm bir subsegment'ini free edip aynı fiziksel sayfaların başka bir size class / pool tipi tarafından geri alınmasını bekler — böylece iki farklı object türü aynı belleği paylaşır. Bu cross-cache yaklaşımı, kLFH'in aynı-boyut kısıtını aşmak için sayfaların allocator'lar arasında geri dönüşümüne dayanır; kavramsal olarak legacy "big kids' pool" feng shui'sinin Segment Heap karşılığıdır.

Not: Bu bir vulnerability değil, bir yerleştirme primitive'idir; tek başına bir memory-safety hatası olmadan zararsızdır.

Detection

  • Anormal allocation hacmi/paterni. Tek bir low-privileged process'in kısa sürede aynı boyutta binlerce object (event, pipe, ALPC, WNF vs.) oluşturup seçici olarak free etmesi, bir bucket'ı groom etme imzasıdır. ETW (Microsoft-Windows-Kernel-*) handle create/close ve object allocation telemetrisi ile handle-count spike'ları bunu ortaya çıkarır.
  • Pool corruption bugcheck'leri. kLFH/VS tutarsızlığı 0x19 (BAD_POOL_HEADER), 0xC2 (BAD_POOL_CALLER) ya da 0x1A ile crash üretir; bir crash civarında belirli bir pool tag'inin non-paged/paged pool kullanımındaki ani sıçrama, grooming + overflow denemesini düşündürür.
  • EDR heuristics. Groom'a özgü sequence — bulk allocate → partial free → vulnerable IOCTL / syscall — davranışsal olarak korele edilebilir; special pool / Driver Verifier açıkken corruption daha erken ve daha yüksek sinyalle yakalanır.

Mitigation

  • Segment Heap'in kendi hardening'i. VS backend'inde _HEAP_VS_CHUNK_HEADER encode edilir; kLFH bucket randomization ve non-paged pool'un NX olması eski executable-spray hedefini ortadan kaldırır. Bunlar determinism'i azaltır ama yok etmez — kLFH _POOL_HEADER'ı hâlâ encode edilmez.
  • Driver hijyeni. Sürücü yazarları allocation boyutlarını valide etmeli, integer overflow'a karşı korunmalı ve ExAllocatePool2 gibi modern, tag'li API'leri kullanmalıdır; böylece komşu kontrollü bir chunk'a linear overflow ile ulaşılamaz.
  • Test-zamanı doğrulama. Driver Verifier + special pool ile pool corruption'ı shipping öncesi yakalayın; guard page'li special pool, groom'ların hedeflediği adjacency'yi bozar.
  • Attack surface azaltımı. Gereksiz üçüncü parti sürücüleri kaldırmak ve HVCI / kernel CFG gibi mitigation'ları etkin tutmak, groom'un beslediği overflow/UAF'nin kontrol akışına dönüşmesini zorlaştırır.

References