Researchers: Adam Donenfeld (@doadam)
Relevant Operating Systems: iOS, tvOS and watchOS
As a part of zLabs platform research team (and as a researcher in general), I often find myself wandering in IDA, reversing random pieces of code.
In this blog post, I’m going to show a vulnerability I found deep inside the video decoder driver shipped with every *OS device (AppleD5500.kext). I’m also going to show how I triggered this vulnerability from an unprivileged context, as AppleD5500.kext requires higher privileges to be used, and a typical app can’t just open a direct communication channel with it.
On one of these journeys, I found a vulnerability which was silently fixed by Apple in the famous IOSurface.kext driver.
How it all got started – IOSurface
IOSurface objects store a lot of properties about graphics, one of them is called “plane”. For brevity, there was a sign mismatch in the “offset” of the plane. It means that each driver which used the plane’s offset (or base), would have had a negative int, while the kernel “IOSurface->getPlaneSize()” function regarded the plane’s offset as a uint32_t. This vulnerability resulted in a (potential) buffer overflow.
For those who are familiar with IOSurface – this alone, although seemingly powerful, is a useless primitive as long as nobody actually utilizes IOPlane’s offset\base values. IOSurface is merely a dictionary in that particular case, so having a big value doesn’t really mean we can overwrite something… yet.
To find objects that utilize IOSurface, one has to know a few fundamentals about IOSurface objects, namely, how to actually look them up using other kernel drivers. (We can aim for usermode use as well, but why not pwn the kernel directly?)
Throughout my research (which has been detailed in depth both in HITBGSEC2018 and BeVX), I found out that there’s a driver called AppleD5500.kext, which is responsible for hardware accelerated video decoding in iOS. AppleD5500.kext itself isn’t accessible from within the sandbox, but requests to mediaserverd can be made to somewhat manipulate AppleD5500.kext. Unfortunately, not only can mediaserverd serve as a proxy with the communication with AppleD5500.kext, but it also rebuilds the entire request (so there’s no forwarding, but just a reconstruction of a completely different request).
I further reverse engineered the communication between mediaserverd, AppleD5500.kext and the internals of AppleD5500.kext, specifically, how it parses video packets.
I found out that there was a vulnerability in AppleD5500.kext when tile decoding monochromatic video. You could decode a specific tile, specifying X and Y offsets, which were used as an anchor point for calculating an offset from a buffer and a length of a buffer, while no checks were made on them:
context->tile_offset_x and context->tile_offset_y are user-controlled.
As we can see on the memset_stub call, v85->surf_props.plane_offset is actually v74, which is calculated from our controlled tile_offset_x, tile_offset_y and other IOSurface attributes which are controlled by us. The memset length is (dest_surf->surf_props.plane_height >> 1) * (dest_surf->surf_props.plane_offset >> 0x20), which are both controlled by us as well.
This gives us a powerful primitive: overwrite data with 0x80s, while controlling the offset from which we overwrite, and the length of data we overwrite.
But the vulnerability is only available with monochromatic videos, and amusingly, this is in fact a type that mediaserverd rejects. Luckily, according to the H.264 manual, it is still possible to change the chroma subsampling value of a specific video sequence (which defines whether a video is monochromatic [named chroma_format_idc in the manual]). This way, by sending a “normal” video followed by a new video sequence parameter set, it was possible not only to trigger this vulnerability, but also get it to be working via mediaserverd, thus accessible from within the sandbox 🙂
The entire presentation has been recorded and can be seen here.
There’s also a full whitepaper available on phrack, if you prefer reading: http://www.phrack.org/papers/viewer_discretion_advised.html
- 30/10/2017 – Vulnerability disclosed to Apple
- 02/12/2017 – Apple confirmed the existence of the vulnerability
- 23/01/2018 – Apple deployed the fix to its *OS devices.