Skip to content

pipe_buffer ops overwrite data-only attack

Freed/yeniden-spray'lenmiş bir pipe_buffer'ın ops pointer'ını forged bir pipe_buf_operations table'a yeniden yönlendir (ya da bir page UAF zincirle) ki pipe teardown attacker'ın seçtiği davranışı sürsün — herhangi bir kernel .text'ini overwrite etmeden.

Mechanism

Note

Her pipe_buffer ops taşır, bir struct pipe_buf_operations vtable'ına (anon_pipe_buf_ops) bir pointer; pipe kodu bunun release/try_steal/get callback'lerini normal lifecycle event'leri sırasında çağırır (örn. release, tüketilen bir buffer recycle edildiğinde ya da pipe kapatıldığında çalışır). Kernel ops'a körü körüne güvenir. Onu bir pipe_buffer UAF üzerinden overwrite etmek — buffer array'ini free et, aynı slab slot'unu kontrol edilen byte'larla yeniden allocate et — attacker'ın ops'u, function pointer'larını kendisinin doldurduğu fake bir table'a yönlendirmesini sağlar. Bu "data-only"dir, şu anlamda: hiçbir kernel kodu yamalanmaz ve hiçbir read-only vtable değiştirilmez; attacker kontrol edilen memory'de writable bir replica table forge eder ve kernel onu gerçekmiş gibi dereference eder. Yakından ilişkili, tamamen data-only bir varyant ops'tan büsbütün kaçınır: page pointer'ını kısmen overwrite et ki bir pipe'ın buffer'ı başka bir pipe'ın canlı page'ini alias etsin (PageJack page-UAF), hiç forged pointer olmadan page-granular R/W vererek.

struct pipe_buf_operations {
    int  (*confirm)(struct pipe_inode_info *, struct pipe_buffer *);
    void (*release)(struct pipe_inode_info *, struct pipe_buffer *); /* invoked on recycle/close */
    bool (*try_steal)(struct pipe_inode_info *, struct pipe_buffer *);
    bool (*get)(struct pipe_inode_info *, struct pipe_buffer *);
};

Buffer array'i, varsayılan 16-buffer'lı bir pipe için kmalloc-1k'dır (16 * 40 = 640 byte); fcntl(fd, F_SETPIPE_SZ, …) grooming için onu başka bir cache'e taşır. Array sade bir kmalloc allocation'ı olduğundan, bir UAF ya da cross-cache reclaim içeriğini toptan değiştirebilir.

Walkthrough

UAF akışı: bir pipe_buffer array'ini free ettir, sonra slot'a aynı boyutta kontrol edilen bir object spray'le ki pipe_buffer[i].ops olarak yorumlanan byte'lar attacker memory'sini göstersin; bir buffer recycle / close() tetiklemek ops->release'i çağırır:

int pfd[2];
pipe(pfd);
write(pfd[1], buf, 0x10);            /* live pipe_buffer with real page + ops */

/* ... free the pipe_buffer array (UAF) and re-spray the slot ... */
/* attacker bytes now overlay the buffer:                          */
fake[i].ops = &forged_ops;           /* forged_ops.release = controlled */

close(pfd[0]); close(pfd[1]);        /* pipe teardown calls ops->release */

Saf page-UAF (PageJack) alternatifi forged bir table gerektirmez — page'in kısmi bir overwrite'ı iki pipe'ın bir page'i paylaşması için yeterlidir:

/* OOB write touches only the low bytes of pbuf->page, aliasing it
   onto a page that another, still-open pipe owns -> page use-after-free */
pbuf->page = (struct page *)((uintptr_t)pbuf->page & ~0xffffUL | groomed_low);
Bu neden "data-only" kalıyor

kernel CFI'li platformlarda ya da ops'un yalnızca veri olarak tüketildiği yerlerde (örn. statik anon_pipe_buf_ops symbol address'i üzerinden kernel base'ini sızdırmak için ops'u okuyan türetilmiş primitive'ler — bkz. kernel-base-leak-via-ops-pointer), attacker'ın asla executable bir hedefe ihtiyacı olmaz. Özellikle PageJack varyantı yalnızca bir data pointer'a ve bir page'e dokunur, dolayısıyla SMEP/SMAP/CFI'den etkilenmez; mitigation, heap corruption'ının kendisini engellemekten gelmelidir.

Warning

release/get refcounting'i sadakatle uygulamayan forged bir ops table'ı, page allocator'ı desenkronize eder ve corruption anında değil, bir sonraki ilgisiz allocation'da crash eder — bu da bu bug'ları aldatıcı biçimde kararsız kılar. Teardown'dan önce orijinal page/ops'u geri döndürmek güvenilir cleanup path'idir.

Mitigation

Primitive'e özgü bir mitigation yoktur; savunma pipe_buffer UAF/OOB'sini engellemektir: freelist hardening, cross-cache'in farkında olan allocator'lar ve slab cache isolation. anon_pipe_buf_ops'un statik konumu (bir KASLR-leak oracle'ı) corruption'la ilgisizdir ve kendisi düzeltilebilir bir surface değildir.

References