Skip to content

set_memory_x page-table attack

set_memory_x()'i kötüye kullan (ya da NX bit'ini temizlemek için page-table entry'lerini doğrudan corrupt et); böylece writable bir kernel page'i executable hale gelir ve enjekte edilen shellcode kernel mode'da çalışır.

Mechanism

set_memory_*() API'si, bir kernel virtual address aralığının attribute'larını onu map'leyen page-table entry'lerini düzenleyerek değiştirir. Kernel header'ı attribute'ların "Cacheability, Executability, Read/Write, Presence, Encryption" içerdiğini belirtir. set_memory_x(addr, numpages), numpages'i kapsayan PTE'lerdeki NX bit'ini (x86'da _PAGE_NX; arm64'te UXN/PXN) temizler ve aralığı executable yapar; set_memory_nx() tersini yapar.

Note

Executability, en temelde tek bir page-table bit'idir. (a) içeriğini de kontrol ettiği bir kernel bölgesinde set_memory_x()'i çağırabilen ya da (b) NX'i temizlemek için ilgili PTE'yi doğrudan corrupt edebilen bir attacker, writable bir kernel page'inde attacker'ın yazdığı byte'ları executable yapar. Bu, o page için W^X / NX'i yener ve saf bir ROP payload'ına ihtiyaç duymadan kernel shellcode'unu indirecek bir yer verir.

Walkthrough

İlgili kernel yüzeyi (imzalar ve semantik):

/* include/linux/set_memory.h (generic no-op stubs when CONFIG_ARCH_HAS_SET_MEMORY unset) */
static inline int __must_check set_memory_x(unsigned long addr,  int numpages){ return 0; }
static inline int __must_check set_memory_nx(unsigned long addr, int numpages){ return 0; }
/* arch/x86 supplies the real implementation that walks/splits PTEs */

Bir write veya call primitive var olduğunda kavramsal attack path'i:

  1. Shellcode'u writable bir kernel page'ine yerleştir (ör. bir BPF map, spray'lenmiş bir slab object ya da direct map).
  2. Ya hijack edilmiş bir function pointer/ROP aracılığıyla set_memory_x(page, 1) çağır, ya da bir page-table write primitive kullanarak o page'in PTE'sindeki _PAGE_NX bit'ini bul ve temizle.
  3. Control flow'u artık executable olan page'e yönlendir; shellcode ring 0'da çalışır (ör. commit_creds(prepare_kernel_cred(0))).

Beklenen sonuç: W ve NX olan bir page, W+X olur ve içeriğinin yürütülmesi supervisor mode'da başarılı olur.

Mitigation

Strict kernel W^X (CONFIG_STRICT_KERNEL_RWX), set_memory_* path'lerini ve page table'ların kendisini mümkün olduğunda read-only yapmak, SLAT (EPT/NPT) aracılığıyla hypervisor-enforced W^X ve corrupt edilmiş bir pointer üzerinden executable page'e ulaşmayı durduran CFI, hepsi bunu köreltir. Bir PTE'nin NX bit'ine ulaşabilen herhangi bir data-only write'ı RIP'e eşdeğer say.

References