Skip to content

Kernel function pointer overwrite

Bir kernel nesnesinin callback/ops pointer'ını (örn. bir seq_operations veya tty_operations entry'si) corrupt et, böylece üzerinden dispatch eden sonraki syscall attacker'ın seçtiği bir adrese atlasın — bir write primitive'i RIP control'üne çevir.

Mechanism

Linux kernel'i indirect dispatch etrafında kurulmuştur: file operation'ları, sequence iterator'ları, TTY line discipline'ları, netlink handler'ları ve sayısız driver, function pointer tablolarından çağrı yapar. Bir file descriptor üzerindeki read(), ioctl() ya da close(), obj->ops->handler(obj, ...) olarak dispatch edilir. Kernel, bu pointer'ların meşru text'e işaret ettiğine güvenir. Target'ın kernel .text aralığında olduğunu doğrulayan per-call bir validation yoktur.

Note

Bir attacker bu nesnelerden birine (heap overflow, UAF reclaim ya da out-of-bounds write yoluyla) yazıp bir callback pointer'ı overwrite edebilirse, üzerinden yapılan bir sonraki dispatch control'ü seçilen değere aktarır. struct seq_operations ders kitabı target'ıdır: dört function pointer'dır (start, stop, next, show), /proc/self/stat gibi single_open() ile desteklenen bir dosya açıldığında kmalloc-32'de allocate edilir ve dördünden herhangi birine bir userspace syscall ile ulaşılır — proc fd üzerindeki read(), startshownextstop sırasını yürür. start'ı (offset 0x0) overwrite et, read() çağır ve RIP senindir. struct tty_operations (/dev/ptmx açıldığında doldurulan tty_struct->ops pointer'ı üzerinden ulaşılır), pty üzerindeki ioctl()/write() ile ulaşılan analog "vtable" target'ıdır.

İhlal edilen invariant: kernel'deki indirect-call target'ları kod değil data'dır ve attacker'ın ulaşabildiği allocation'ların yanında writable slab memory'de yaşar. Control-flow integrity yalnızca o data attacker-controlled olmadığı sürece geçerlidir. Pointer'ın integrity'sini bozarsan call'un integrity'sini de bozarsın.

Walkthrough

Bu, nesne üzerinde zaten bir write primitive'in ve bir kernel-base leak'in olduğunu varsayar (kernel-base-leak-via-ops-pointer.md'a bak).

  1. Victim nesneyi allocate/reclaim et. seq_operations için /proc/self/stat aç:
int fd = open("/proc/self/stat", O_RDONLY);   // allocates struct seq_operations in kmalloc-32
  1. Bir callback'i overwrite et. read() tarafından ilk ulaşılan layout offset 0x0'daki start'tır:
struct seq_operations {       // offset
    void *(*start)(...);      // 0x00  <- overwrite this
    void  (*stop) (...);      // 0x08
    void *(*next) (...);      // 0x10
    int   (*show) (...);      // 0x18
};

Write primitive'ini kullanarak (örn. UAF ile overlap edilmiş writable bir nesne) start'ı istenen target'a ayarla. Yaygın bir ilk durak stack-pivot gadget'ıdır, böylece bir ROP chain'e pivot yapabilirsin, çünkü call site'ta RSP hâlâ kernel stack'ine işaret eder:

unsigned long target = kbase + STACK_PIVOT_GADGET;  // e.g. "mov rsp, rax ; ret" style pivot
write_qword(overlap, 0x00, target);                 // clobber seq_operations.start
  1. Dispatch'i tetikle:
char buf[16];
read(fd, buf, sizeof(buf));   // kernel calls seq_operations.start(...) -> attacker target

Bu noktada RIP = target. Privilege escalation için klasik devam, commit_creds(prepare_kernel_cred(0)) çağıran bir ROP chain ve ardından KPTI trampoline üzerinden userspace'e dönüştür (commit-creds.md'a bak).

Warning

seq_operations.start az çok controlled argument register'larıyla çağrılır ama SMEP/KPTI altında temiz bir RIP→shellcode elde edemezsin — kernel page table'larıyla kernel context'ine düşersin, dolayısıyla bir userspace target fault verir. Userland adresi değil, bir kernel-text gadget (stack pivot ya da commit_creds gadget chain) kullan. Ayrıca hijack'ten sonra read() path'i muhtemelen stop/show'u da çağırmaya çalışır; chain'in normal döndürürse corrupt edilmiş struct'a yeniden girilebilir — temiz dönmek yerine escalate edip çık (signal/do_task_dead).

Detection

  • KASAN/KFENCE, ops pointer'a düşen alttaki overflow/UAF write'ını işaretler.
  • Target'ı kernel .text aralığının dışında olan bir indirect call, tam olarak CONFIG_CFI_CLANG'in (kCFI) call site'ta yakalamak için yapıldığı şeydir.

Mitigation

  • kCFI / CONFIG_CFI_CLANG, indirect-call target'larının beklenen function-type signature'la eşleşmesini zorlar; bir stack-pivot gadget adresi type check'i geçemez ve trap eder. Bu, ops-pointer hijack'lerine karşı tek başına en güçlü mitigation'dır.
  • SMEP/SMAP + KPTI, ucuz varyantı (callback'i doğrudan userspace shellcode'a yönlendirme) engeller ve attacker'ı kernel-text ROP'a iter.
  • Heap hardening (RANDOM_KMALLOC_CACHES, cache separation, freelist randomisation), victim nesneyi güvenilir biçimde reclaim etmenin maliyetini artırır; kmalloc-cache-feng-shui.md'a bak.
  • Write primitive'ini (overflow/UAF) ortadan kaldırmak gerçek çözümdür.

References