CVE-2020-27897: Apple macOS Kernel OOB Write Privilege Escalation Vulnerability

December 10, 2020 | Ziad Badawi

On Tuesday of this week, we published six advisories covering vulnerabilities in Apple macOS. One of those advisories covered a bug reported by ABC Research s.r.o. pertaining to GPUs in Apple hardware. It’s one of many macOS bugs they have submitted to the program. Now that these bugs are fixed in Big Sur, we can cover the details. In this blog, we’ll be taking a closer look at ZDI-20-1403/CVE-2020-27897, which could allow an attacker to escalate privileges and execute arbitrary code in the context of the kernel.

Certain MacBook models come with an Intel graphics module, and macOS uses kernel extensions for managing it. One of these is called AppleIntelKBLGraphics. We will be talking about a local privilege escalation vulnerability in that module.

Communication between user-space and the Intel kernel driver is done using IOConnectCallMethod, which ultimately utilizes Mach messages. IntelMTLRenderFunctions is a class that handles kernel commands from clients for rendering the UI via its execute() method. Each kernel command is identified by a numeric value. In our case, we will be concerned with command 0x10005. Along with the kernel command number, execute() also accepts a buffer from the client. An offset in this buffer is used in an arithmetic operation that produces an address of a structure. This operation occurs without boundary checks and causes an out-of-bounds write vulnerability.

The code path that ends up in IntelMTLRenderFunctions::execute() starts as follows: A user-space client creates two shared memory mappings by calling the IOAccelSharedUserClient2::create_shmem() function, which is made available by another kernel extension, IOAcceleratorFamily2. The first memory mapping will be used as a segment descriptor for the request and the second contains a command buffer. create_shmem() registers the mappings with unique IDs and returns them. Those IDs are then passed to IOAccelCommandQueue::s_submit_command_buffers() along with the kernel command buffer, which is roughly an array of the following structure:

The array is split up later and eventually reaches IGAccelCommandQueue::processKernelCommand() as a single kernel command having a structure such as:

From there, it goes to IntelMTLRenderFunctions::execute() in AppleIntelKBLGraphics for processing the kernel command 0x10005.

A functional exploit is shown where the aforementioned kernel task port is exposed.

Conclusion

Bugs in kernel drivers are always interesting even though their attack vector requires an accompanying RCE vulnerability. With such a pair of vulnerabilities, full compromise becomes possible. This is accurate for both macOS and iOS. Thanks again to ABC Research s.r.o. for submitting this and all of their other bugs to the program. We’ll be publishing details about the more interesting submissions as they patch out.

You can find me on Twitter at @ziadrb, and follow the team for the latest in exploit techniques and security patches.