Exploitation of a fairly constrained UAF Primitive in the Linux Kernel [CVE-2022-42703]

We discussed this vulnerability during Episode 176 on 13 December 2022

A post on exploiting a bug that Jann Horn discovered in the linux kernel’s memory management (MM) subsystem. The bug isn’t detailed in this post and is fairly complex (there is a project zero bug report but it’s difficult to understand without deep knowledge of MM internals), though they state it will be written up in a future blogpost. What’s important to takeaway from the bug is that it essentially gets two VMAs merged with different secondary anon_vma, and subsequently have some mapped pages with a dangling page->mapping pointer (or folio->mapping on later kernel versions). This UAF can be triggered by calling madvise() on the affected page, which eventually calls folio_lock_anon_vma_read().

Restricted write Looking at folio_lock_anon_vma_read(), they found there was a down_read_trylock() call that would take a pointer to a reader writer semaphore that came from the UAF’d object. This gave them a restricted write primitive, which would increment a uint64 by 0x100 assuming the most significant bit and the 3 least significant bits weren’t set. This effectively prevents this write from being able to corrupt kernel pointers or anything that isn’t aligned to an 8-byte boundary. KASLR is also a factor, and so this seems like a tricky exploit scenario. Fortunately, it’s possible to take advantage of the fact that there’s non-randomized CPU entry area stacks to store register context (Interrupt Stack Tables / ISTs). By triggering an exception (such as #DB) in a copy_to/from_user(), the register context could get dumped to this stack on interrupt and be restored when the thread resumes.

Getting stack OOB read/write + code exec By intentionally triggering an exception on a copy_to_user() from a stack address, they could then use the restricted write primitive to increment the register containing the length of the copy to get an out-of-bounds read and leak stack data (including the stack cookie and pointers to defeat kASLR). Conversely, by doing the same on a copy_from_user() call to a stack address, they could smash the return pointer to get code execution, while maintaining the cookie value they leaked earlier.