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:
- 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.
-
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. -
PTE'leri manipüle edin. PGD bulunduktan sonra, seçilen bir user virtual address'i (write-up
0xdead000kullanı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. -
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 (veCR4.SMAPpinning'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 boyuncaACsı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
ret2dirtarzı 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.