Skip to content

seq_operations function-pointer overwrite / leak

32-byte'lık struct seq_operations'ı (/proc/self/stat gibi dosyalar açılırken allocate edilir) spray'leyerek KASLR için bir kernel .text pointer'ı leak et ya da function pointer'larını overwrite ederek read() anında control flow'u hijack et.

Mechanism

struct seq_operations, kernel'in seq_file interface'ini süren dört function pointer (start, stop, next, show) tutar. Tüm struct 0x20 byte'tır, dolayısıyla kmalloc-32 genel cache'inden allocate edilir. /proc/self/stat gibi seq_file-backed bir procfs dosyası açmak yeni bir tane allocate eder — kmalloc-32'de unprivileged, tekrarlanabilir bir allocation.

Note

İki özellik onu birinci sınıf bir exploit object'i yapar. (1) Leak: dört pointer'ı kernel .text'e işaret eder; bunları bir out-of-bounds / UAF read ile okumak bir text adresi verir ve sabit bir offset çıkarmak kernel base'ini verir — KASLR'ı yener. (2) Hijack: fd üzerinde read() çağırmak seq_read()'e ulaşır; bu da seq->op->start/show/...'ı dereference edip çağırır; bu pointer'ları bir write primitive ile overwrite etmek RIP'i attacker'ın seçtiği bir gadget'a yönlendirir. CONFIG_SLAB_FREELIST_RANDOM kapalıyken SLUB free edilmiş kmalloc-32 chunk'larını LIFO sırayla geri verir; bu da henüz free edilmiş bir victim chunk'ının reclaim'ini deterministik yapar.

Walkthrough

seq_operations'ı free edilmiş kmalloc-32 slot'larına spray'le, sonra leak et ve/veya hijack et:

struct seq_operations {            // 0x20 bytes => kmalloc-32
    void *(*start)(struct seq_file *, loff_t *);
    void  (*stop) (struct seq_file *, void *);
    void *(*next) (struct seq_file *, void *, loff_t *);
    int   (*show) (struct seq_file *, void *);
};

int fds[256];
for (int i = 0; i < 256; i++)
    fds[i] = open("/proc/self/stat", O_RDONLY);   // each spreads one seq_operations
  • Leak path: spray'lenmiş bir chunk üzerinde bug'ın OOB/UAF read'ini tetikle; byte'lar bir .text pointer'ıdır (ör. single_start). kbase = leaked - known_offset.
  • Hijack path: write primitive'i kullanarak seq->op->start'ı (ya da stop'u) bir stack-pivot gadget'ıyla overwrite et, sonra read(fds[i], buf, 1) ile seq_read()'e gir ve bozulmuş pointer'ı ateşle → ROP.

Beklenen sonuç: leak edilmiş bir kernel-text adresi ya da read() anında RIP kontrolü.

Mitigation

CONFIG_SLAB_FREELIST_RANDOM ve CONFIG_SLAB_FREELIST_HARDENED reclaim determinizmini azaltır; CFI (CONFIG_CFI_CLANG / FineIBT) overwrite edilmiş pointer üzerinden keyfi bir gadget çağrılmasını engeller; kptr_restrict ve infoleak primitive'lerinin kaldırılması KASLR-leak yarısını köreltir.

References