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
Two objects were primarily used for building primitives,
user_key_payload (from the
add_key() syscall) and
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
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_pathcan be smashed with arbitrary data to get root.