Page-level Overflow in esp6 Linux Kernel Module
The vulnerability here is a fairly straightforward overflow in the esp6 crypto module. When receiving messages, an 8-page buffer is allocated for the incoming data, but it’s possible for messages to be sent that exceed 8 pages in size. When it comes to exploitation though, it gets more complex, as this overflow is at the page-level and not the cache-level.
Background Without going into too much detail, kmalloc caches request pages from the page allocator when needed. Depending on the object size, different “order” pages are requested. Order-0 pages consist of 1 page, order-1 of 2 pages, order-2 of 4 pages, and order-3 of 8 pages. Where the overflow is in an 8-page buffer, corruption will happen in order-3 pages. Only kmalloc-2k, kmalloc-4k, and kmalloc-8k allocate from order-3. After some heap shaping and mitigating the noise of the page allocator, the author was able to get corruption of carefully setup objects in the kmalloc-4k cache.
Another important bit of background for understanding the primitives is on the msg_msg
object. If a message exceeds 4048 bytes in size, the next
pointer will be allocated and used to store excess data. For example, if you pass a 4056-byte message, a msg_msg
object will be allocated for the first 4048 bytes, then the last 8 bytes will be allocated in kmalloc-32 and stored in next
.
Exploitation
Two objects were primarily used for building primitives, user_key_payload
(from the add_key()
syscall) and msg_msg
from sendmsg/recvmsg
. By setting up a user_key_payload
adjacent to the overflow pages, it was possible to corrupt it’s datalen
to get an out of bounds read to leak msg_msg
objects on an adjacent page. These msg_msg
objects would contain a next
pointer into kmalloc-32. By then exploiting the bug again to corrupt a msg_msg
object directly and smash it’s m_ts
(message text size) and next
pointer to one leaked in the first step, OOB read in kmalloc-32 can be achieved. This is useful because seq_operations
objects end up in this cache (which contain .text pointers), and leaking these objects can be used to defeat kASLR.
Finally, the bug can be exploited once more to obtain arbitrary write, again using msg_msg
. Recall that messages > 4048 bytes will use next
for extra storage, and load_msg()
will copy_from_user()
data to it. By freezing the first copy using the FUSE trick, corrupting the next
pointer with a pointer to modprobe_path
, and resuming the copy, modprobe_path
can be smashed with arbitrary data to get root.