Skip to content

SMAP Bypass via Kernel-Resident Payload

SMAP, kernel'in user page'lerine dokunmasını yasakladığında, attacker'lar user memory'yi dereference etmeyi tamamen bırakır ve bunun yerine kontrol ettikleri data/kodu kernel'in erişebileceği memory'ye (physmap, module region) yerleştirir, böylece hiçbir SMAP fault'u oluşmaz.

Mechanism

Note

Kavramsal: NEDEN çalışır, invariant/teori.

Supervisor Mode Access Prevention (SMAP), CR4.SMAP (bit 21) ile kapılan bir x86 CPU özelliğidir. Enabled olduğunda, user-accessible işaretli bir page'e (PTE'de U/S=1) yapılan herhangi bir supervisor-mode (ring 0) data erişimi bir fault üretir — RFLAGS'taki AC (Alignment Check) flag'i set değilse (SMAP enabled iken bu flag supervisor'ın user page'lere erişimini gate'ler). Kernel, RFLAGS.AC'yi STAC ("set AC") ve CLAC ("clear AC") instruction'larıyla toggle'lar: copy_from_user/copy_to_user gibi meşru user-memory erişimcileri önce pointer'ın user aralığında olduğunu teyit etmek için access_ok() çağırır, sonra STAC, kopyayı yapar ve CLAC. O pencerelerin dışında kernel asla bir user pointer'ı dereference etmemelidir.

Bu yüzden SMAP, "bozulmuş bir kernel function pointer'ını bir ROP chain veya shellcode tutan user-space buffer'a yönlendir" klasik exploitation primitive'ini öldürür — o user page'inden gelen ilk fetch fault verir. Modern kernel'lerde CR4.SMAP ayrıca pinned'dir, bu yüzden bit 21'i temizlemek için CR4'e bir write ROP'lamak basitçe bloklanır.

Kernel-resident payload stratejisi SMAP'ı disable etmek yerine onu atlatır: kontrol edilen data zaten kernel'in okumasına/çalıştırmasına izin verilen bir adreste yaşıyorsa, hiçbir U/S=1 page'ine dokunulmaz ve RFLAGS.AC alakasızdır. Böyle memory'nin iki kanonik kaynağı vardır:

  • physmap / direct map (ret2dir): attacker tarafından spray edilmiş user page'leri dahil her physical page, kernel space'e de bir supervisor alias'ta lineer olarak map'lenir. Chain'e o kernel alias üzerinden ulaşmak SMAP'ı tetiklemez. (Bkz. stack-pivot-into-physmap-direct-map.)
  • Module region veya diğer yazılabilir kernel mapping'leri: bir page-table write primitive'i kullanarak bir user virtual address'ini kernel memory'sine remap edin ve oraya shellcode yerleştirin, böylece execution supervisor page'lerinde kalır.

Walkthrough

Temsili bir chain (bir öğretici write-up'tan) baştan sona user-pointer dereference'inden kaçınır:

  1. Physmap üzerinden kernel'e kontrol edilen data spray edin. Birçok user page allocate edin ve onları resident olmaya zorlayın ki physical frame'leri — ve dolayısıyla kernel direct-map alias'ları — bilinen içerik tutsun:
for (int i = 0; i < N; i++) {
    void *p = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE,
                   MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0);
    memcpy(p, payload, PAGE_SIZE);   /* MAP_POPULATE faults pages in now */
}

Kernel artık bu attacker data'sını physmap alias'ı üzerinden, hiç SMAP-kontrollü bir user erişimi yapmadan okuyabilir.

  1. Physmap'te page table'ları bulun. Direct map'i, mevcut process'in PGD'sini imzasıyla tarayarak bulun — ör. low byte'ı kernel PTE flag pattern'ına (0x67) en-anlamlı (NX/present-türevli) bit set ile eşleşen 256 ardışık entry içeren bir page.

  2. PTE'leri manipüle edin. PGD bulunduktan sonra, seçilen bir user virtual address'i (write-up 0xdead000 kullanır) kernel module memory'sine map'leyecek şekilde entry'leri yeniden yazın, sonra oraya shellcode yazın. Payload artık kernel space'te yaşıyor.

  3. Meşru bir kernel entry point üzerinden çalıştırın. Vulnerable driver yolunu (/dev/vuln) tetikleyin ki control kernel-resident shellcode'a aksın. Instruction ve data fetch'leri supervisor page'lerini hedeflediğinden, SMAP (ve CR4.SMAP pinning'i) hiç araya girmez.

Warning

İlişkili bir bypass sınıfı, data relocation yerine state confusion'dır: bir exception/fault handler bir copy_* rutininden RFLAGS.AC'yi temizlemeden dönerse, syscall'un geri kalanı SMAP fiilen kapalıyken çalışır. Böyle handler bug'ları gerçek kernel'lerde ortaya çıktı (ör. copyin()/copyout() sonrası %RFLAGS.AC'yi set bırakan bir FreeBSD copy_fault yolu).

Detection

  • Bir fault'ta user copy rutinlerine yeniden giren kernel code path'lerini denetleyin ve her çıkışta CLAC'ın daima çalıştığını teyit edin (handler boyunca AC sızıntısı yok).
  • Process context'inden gelen beklenmeyen page-table write'larını ve user VA'larının kernel physical frame'lerine remap edilmesini izleyin.

Mitigation

  • SMAP'ı, user frame'lerini physmap direct alias'ından unmap eden ve ret2dir tarzı kernel-resident payload'ları doğrudan yenen exclusive-page-frame-ownership (XPFO) ile birleştirin.
  • CR4.SMAP'i pinned tutun (bit'i ROP'layarak kapatmayı engeller) ve strict-kernel-rwx'i uygulayın ki yazılabilir kernel staging alanları aynı zamanda executable olmasın.
  • Allocator'ları sertleştirin (hardened-usercopy) ve layout'u randomize edin ki page table'ları / staging alanlarını bulmak daha zor olsun.

References