Skip to content

Page Table Entry (PTE) overwrite for SMEP/NX bypass

Bir kernel read/write primitive'i kullanarak page-table entry'leri düzenlemek — U/S ve NX/XD bit'lerini flip ederek — böylece attacker-controlled page'ler kernel context'inde executable olur ve SMEP ile NX yenilir.

Mechanism

Note

SMEP (Supervisor Mode Execution Prevention) ve NX/XD (No-eXecute), bir virtual address'i bir physical frame'e map'leyen page-table entry (PTE) içindeki bit'ler tarafından page başına zorlanır:

  • U/S bit — bir page'i User (U/S=1) veya Supervisor (U/S=0) olarak işaretler. CPU, supervisor (kernel) mode'undayken User olarak işaretli bir page'den execute ederse SMEP fault verir.
  • NX/XD bit — set edildiğinde, mode'dan bağımsız olarak page'den instruction fetch fault verir.

Bu bit'lerin encode ettiği invariant şudur: "kernel mode yalnızca OS'in kernel code olarak işaretlediği page'leri ve yalnızca executable olarak işaretli page'leri execute edebilir." Bir attacker zaten bir kernel arbitrary read/write primitive'i elinde tutuyorsa, PTE'nin kendisini okuyup yeniden yazabilir. U/S bit'ini temizlemek, attacker'ın user page'ini bir supervisor page'i olarak yeniden etiketler (SMEP artık fault vermez); NX/XD'yi temizlemek onu executable yapar. Koruma, CPU check'ini kırarak değil, check'in danıştığı metadata'nın ta kendisini düzenleyerek bypass edilir — R/W'ye sahip olduğunda page table'ları sıradan writable kernel data olarak ele almanın genel bir sonucu.

Walkthrough

Üst düzey teknik, Windows kernel exploitation için public olarak belgelendiği gibi (Connor McGarr) ve Linux'a da eşit derecede uygulanabilir. Stabil bir arbitrary-read ve arbitrary-write primitive'i varsayar.

Kavramsal PTE düzenlemesi (gerçek offset'ler yok)
// 1. Place shellcode at a known *user* virtual address (uVA).
// 2. Compute the PTE address for uVA from the PTE base
//    (self-referencing / MiGetPteAddress-style index math).
pte_va = pte_base + ((uVA >> SHIFT) & MASK) * 8;

// 3. Read the current PTE via the read primitive.
pte = kread64(pte_va);

// 4. Flip the protection bits:
pte &= ~NX_BIT;     // clear NX/XD  -> page becomes executable
pte &= ~US_BIT;     // clear U/S    -> page treated as supervisor (SMEP-safe)

// 5. Write it back via the write primitive, then redirect
//    kernel control flow to uVA.
kwrite64(pte_va, pte);
  • Zor kısım PTE'yi bulmaktır: (çoğunlukla randomize edilmiş) page-table base'i bilmeyi gerektirir. Public writeup'lar onu, ilgili kernel symbol/offset'i leak'leyip sonra mimarinin index aritmetiğini uygulayarak türetir.
  • PTE patch'lendiğinde, attacker bir token-stealing / commit_creds-tarzı payload çalıştırmak için execution'ı (corrupted function pointer, ROP stack pivot, vb.) artık-executable page'e pivot eder.
  • Bu bir post-primitive tekniğidir: arbitrary read ve arbitrary write'ı öngerektirir.

Detection

  • Page-table integrity / W^X ihlalleri: kernel page'lerinde W^X'i zorlayan hypervisor-tabanlı korumalar (Windows HVCI, Linux pKVM), bir page hem writable hem executable yapıldığında fault verir veya log'lar — güçlü bir sinyal.
  • Kernel context'inde beklenmedik executable user-range page'leri: page protection'larını örnekleyen EDR/telemetri, supervisor + executable map'lenmiş bir user VA gözlemleyebilir, ki anomalidir.
  • Control-flow anomalileri: bir user-range address'ine kernel return/branch anormaldir; CET/shadow-stack veya PatchGuard-tarzı integrity check'leri bunu açığa çıkarabilir.
  • Crash kalıntısı: başarısız PTE düzenlemeleri (yanlış base, randomization) çoğunlukla tuhaf adreslerde kernel page-fault panic'leri üretir.

Mitigation

Warning

Bir attacker güvenilir kernel arbitrary read/write'a sahip olduğunda, PTE düzenlemesini hedeflediği aynı CPU bit'leriyle durdurmak zordur. Kalıcı savunmalar, enforcement'ı writable kernel address space'inin dışına taşır.

  • HVCI / Hypervisor-enforced code integrity & MBEC/RUM: hardware Mode-Based Execution Control, execute izinini in-kernel U/S bit'inden bağımsız bir şekilde doğrular, böylece yalnızca U/S'yi flip etmek artık execution vermez.
  • KVA Shadow / KPTI: ayrı user ve kernel page table'ları, naif user-page yeniden etiketlemesinin erişimini azaltır.
  • Randomize edilmiş PTE/page-table base: ek bir leak'i zorlar, maliyeti yükseltir.
  • SMAP, çevredeki primitive'leri zorlaştırır (stac olmadan user data'nın doğrudan kernel read'i yok).
  • Öncülü azalt: gerçek fix, en baştan R/W primitive'ini veren memory-corruption bug'ını ortadan kaldırmaktır — use-after-free ve type confusion'a bak.

References