86 - Kernel UAFs and a Parallels VM Escape
A Use-After-Free in Android’s ION Allocator used by the kernel for DMA buffers that can be shared across user/kernel/device boundaries. The issue starts from the
DMA_BUF_IOCTL_SYNC that is exposed by the buffer’s file descriptor, this IOCTL can arbitrarily increment or decrement the reference counter for the shared buffer. Enabling a malicious user-space application to allocate a DMA buffer, pass it into another kernel function and then trigger the reference count to drop to zero resulting in it being freed while the other function is still using it.
An interesting primitive in
io_uring resulting in the ability to free adjacent kernel buffers.
For files that do not provide the
read_iter operation io_uring will fall back to use
loop_rw_iter to manually perform iterative read/writes. In doing so as it reads it will increment the requests
addr field by the size of the read. Normally, this will be a userland pointer however by calling
IORING_OP_PROVIDE_BUFFERS beforehand a kernel buffer can be used, which is later free’d once the operation completes. As the
addr field is no longer where it started it is possible to free adjacent kernel buffers.
Straight forward version is two Out-Of-Bounds accesses in reading and writing the
Driver feature set. A guest provided value is stored, and then used as an array index without any validation both in
PciVirtIOWriteMM and in
PciVirtIOReadMM giving relative read/write primitives.
This post also covers the exploitation (discovery and triaging at the start too!). First step was using the relative read to read out index
6 which contained a pointer nearby in memory, revealing the base address of the
libMonitor.dyld that that vulnerability was in.
Second step was turning the relative r/w into arbitrary r/w, this was done by targeting pointers in an
io_port_handler array that was in
__bss memory, which were used as parameters to the various handlers. Control of these pointers gave arbitrary read/write gadgets through
Finally for arbitrary code execution on the host, the read was used to find the address of
system and the write was used to replace a function pointer used by
smb_write, and setup the command string for the variable used when
smb_write would call that function pointer.