KVM Bugs and an iOS IOMFB Kernel Exploit
Out-of-bounds (OOB) access in the
VMGExit handler, which is triggered for string I/O instructions. The
sev_es_string_io() function is responsible for doing the string copy between the unencrypted guest memory regions and the virtualized target. When doing so, it’ll setup a scratch buffer at
svm->ghcb_sa (GHCB is short for Guest-Hypervisor-Communication-Block), and it’ll set it up using guest-controlled values for the length, I/O access size, and contents for the scratch buffer. When the control flow reaches
emulator_pio_out() for outgoing I/O, it’ll copy the contents of the scratch buffer to the virtual CPU pio data using the I/O access size multiplied against the provided length. The problem is,
pio_data is only one page size in length. If you provide a count or size that makes the
memcpy() go beyond one virtual page, there’s an out of bounds access on adjacent pages.
Since this bug is in both the input and output paths, it provides an OOB read and an OOB write. The patch here was to ensure you couldn’t provide a size that would exceed the page size of the system.
recv_server-device_response_msg_process() handler, a
nums field gets pulled out of the packet’s JSON payload, and is used to represent the total number of UDP server domains. The application then iterates based on this field, looking for its respective
domain%d key in the JSON. If the key is found, the value (maximum of 0x80 bytes) is copied out to an offset calculated based on the current iteration count.
As there is no bounds checking, and the destination is a fixed size region of memory, by proving a high
nums value attacker controlled data can be written well beyond the expected memory region. Additionally as the loop will not write any data if the
domain%d an exploit could avoid smashing too much data on the way ot a desired target by providing an high
nums value and only one
This same bug also exists in the
Uninitialized use found in Apple’s ColorSync via fuzzing. When parsing an image, the library will calculate the start address for reading from a Color Lookup Table (CLUT) data point array for pixel data. It will do this by indexing into it using
2 * x * y. But if the
in_channels field in the header is set to 0, the
y coordinate never gets initialized, and is left as whatever was left on the stack in that location. As such, if that stale value is really large or negative, the array access goes out of bounds and triggers the crash.
Focuses on exploiting an Out-of-Bounds (OOB) read in the
IOSurface subsystem. The vulnerability was an unchecked
scalar0 index into the scalar input array in
IOMobileFramebufferUserClient::get_displayed_surface() called by
IOMobileFramebuffers::s_displayed_fb_service(). This gives you the ability to get an arbitrary pointer interpreted and used as an
Exploiting this bug turned out to be very tricky though due to iOS 14’s introduction of kheaps, a form of kernel heap isolation. It isolates
data_buffer (user-data objects), default heap, kernel extension heap, and temp heap into their own zones. Furthermore, kheap provides guarantees that pages will not be reclaimed by other zones via sequestering, making spraying difficult. Fortunately for the researcher though, large objects (>37,668 bytes) weren’t affected by kheap isolation and sequestering. By spraying with large OSData buffers, and abusing various external methods with the type confusion, it’s possible to derive arbitrary read/write as well as an infoleak.
The primary gadget these primitives rely on is a pointer at
0xC0 inside of the
IOSurface object which can be faked via the type confusion.
It turns out there’s an external method called
IOSurfaceRootUserClient::get_surface_use_count(), which will dereference the pointer at offset
0xC0 in the object you can spray into. This gives an easy arbitrary read primitive.
Another external method,
IOSurfaceRootUserClient::set_compressed_tile_data_region_memory_used_of_plane(), will similarly perform a write using the pointer at
0xC0 using contents you control, giving arbitrary write.
It was possible to derive an infoleak by abusing the internal references that
IOSurfaceClient would keep to it’s child
IOSurface object and it’s parent
IOSurfaceRootUserClient object. Most functions would get the client array from the
IOSurfaceRootUserClient, index into that array using the
IOSurface ID, and pass the
IOSurface pointer from the
IOSurfaceClient into the function for whatever work that needs to be done. The researcher pondered what would happen if you caused another type confusion and passed a pointer to an
IOSurfaceRootUserClient instead. By doing some careful heap shaping and abusing a 32-bit increment on the
0xC0 pointer to shift the
IOSurface reference to point to
IOSurfaceRootUserClient, a bunch of kernel pointers are leaked, including;
- The parent