EPOLL/eventpoll waiter kernel address leak¶
Bir fd'yi bir epoll instance'ına register etmek, kernel
.textpointer'ıep_poll_callbackile heap list pointer'larını tutan bir wait structure allocate eder; o object üzerinden bir infoleak KASLR'ı kırar.
Mechanism¶
Note
Bir process bir epoll instance'ına bir fd eklediğinde, kernel .func'ı kernel function'ı
ep_poll_callback'e set edilmiş bir wait_queue_entry içeren bir struct eppoll_entry
allocate eder. O tek qword'ü ayrı bir infoleak (OOB read, UAF read, uninitialized
disclosure) ile leak etmek kernel .text base'ini açığa çıkarır; structure'ın
list/whead/base pointer'ları kernel heap adreslerini açığa çıkarır.
İlgili upstream structure (fs/eventpoll.c):
struct eppoll_entry {
struct eppoll_entry *next; /* link to next in epitem->pwqlist */
struct epitem *base; /* the owning epitem (heap ptr) */
wait_queue_entry_t wait; /* .func = ep_poll_callback (.text)*/
wait_queue_head_t *whead; /* target file's wait queue head */
};
ep_ptable_queue_proc() onu özel pwq_cache slab'ından allocate eder ve callback'i register eder:
pwq = kmem_cache_alloc(pwq_cache, GFP_KERNEL);
init_waitqueue_func_entry(&pwq->wait, ep_poll_callback); /* writes .text ptr */
pwq->whead = whead;
pwq->base = epi;
add_wait_queue(whead, &pwq->wait); /* links wait.entry into heap list */
pwq_cache sabit bir size class olduğundan ve ep_poll_callback kernel base'inden bilinen
bir symbol offset olduğundan, bu object'ler güvenilir bir spray + leak hedefidir.
Walkthrough¶
int epfd = epoll_create1(0);
struct epoll_event ev = { .events = EPOLLIN, .data.u64 = 0 };
/* target_fd is an fd whose .poll calls poll_wait (pipe, eventfd, socket, ...) */
epoll_ctl(epfd, EPOLL_CTL_ADD, target_fd, &ev); /* allocates eppoll_entry,
writes ep_poll_callback */
pwq_cache'i doldurmak için (ya da vulnerable bir object'in yanındaki delikleri doldurmak
için) bu tür kayıtlardan çokça spray et, sonra bir eppoll_entry üzerinden okumak için ayrı
infoleak bug'ını tetikle:
wait.func'ı (==ep_poll_callback) okumak -> kernel.textbase, KASLR'ı kırarak;whead,base, ya dawait.entry.{next,prev}list pointer'larını okumak -> kernel heap adresleri.
Warning
Bu structure'ların önem taşıdığı gerçek-dünya bir vaka CVE-2019-2215'tir (Android
/dev/binder UAF): free edilmiş bir binder_thread'in inline wait queue'su, register
edilmiş eppoll_entry'yi dangling bir whead/wait.entry ile bırakır ve epoll teardown
onu dereference eder. Pasif "eventpoll spray et, ep_poll_callback'i leak et" pattern'i
doğrudan yukarıdaki fs/eventpoll.c mekanikleri üzerine kuruludur.
Mitigation¶
Hash'lenmiş %pK ve pointer sanitization, bir bug tarafından okunan ham bir object'i
korumaz; dolayısıyla gerçek savunmalar upstream infoleak'i ortadan kaldırmak, KASLR/FG-KASLR
ve bitişikliği daha az deterministik kılan slab freelist hardening'idir.