Exploiting an OOB read/Type Confusion in iOS <= 14.7
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 IOSurface
object.
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.
Arbitrary Read
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.
Arbitrary Write
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.
Infoleak
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
IOSurfaceRoot
object - The parent
IOSurfaceRootUserClient
- The
IOSurfaceClient
array