CVE-2019-8697: MacOS System Escalation Via Disk Management

October 03, 2019 | Ziad Badawi

Recently, the ZDI program acquired a heap-based buffer overflow in a macOS daemon called diskmanagementd from a researcher who goes by the alias ccpwd. diskmanagementd is a service responsible for managing and partitioning drives. One way a user can interact with it is through Disk Utility. It runs a Mach server, allowing communication through Inter-Process Communication (IPC) where a client uses the Mach IPC Interface for sending and receiving messages. Through this RPC mechanism, clients can execute functions in the Mach server that are generated using the Mach Interface Generator (MIG), which is basically Apple’s RPC stub generator.

All communications essentially pass through launchd, macOS’s implementation of init. More details of that process can be obtained by checking its information property list file at /System/Library/LaunchDaemons/com.apple.diskmanagementd.plist

In the beginning, the daemon assigns a callback function that will be invoked in the future during any Inter-Process Communications by sending and receiving Mach messages.

Figure 1 - Assigning function sub_10000C241 as a callback

Here, CFMachPortCreateWithPort assigns function sub_10000C241 as a callback for handling messages received on the Mach port after obtaining its receive right at 0x10000BE1F. Depending on the msgh_id, a value sent in a Mach message that conveys an operation or function ID, sub_10000C241 indirectly uses that id as an index for a dispatch table of two main remote functions. sub_100001DA2 and sub_100002005 which are in charge of initiating and tearing down subsequent communication sessions respectively.

Figure 2 - Setting port rights

Mach ports are unidirectional, meaning that each send/receive request will require its own port. Apple describes it as:

“A port is an endpoint of a unidirectional communication channel between a client who requests a service and a server who provides the service. If a reply is to be provided to such a service request, a second port must be used. This is comparable to a (unidirectional) pipe in UNIX parlance.”

Figure 3 - msgh_id used as an index in a dispatch table located at off_1001AB6E0

In sub_10000CCA9, which is reachable from sub_100001DA2, another Mach port is created with callback function sub_10000DACC and saved in a dictionary with the key “Comms-F2TPort”. A 0x1000 byte buffer is allocated for a response Mach message and is saved under the key “Comms-F2T-replyarea”.

Figure 4 - Reply buffer allocated and mach port created

setObject:forKey: is called for adding key-value pair to the dictionary.

Figure 5 - Adding key-value pairs of port and reply buffer to dictionary

When a client sends a message, sub_10000DACC gets triggered and based on the msgh_id, has access to several MIG remote procedure calls. We will be focusing on sub_1000087C9.

Figure 6 - Setting up reply buffer via mig_reply_setup and calling RPC dispatcher

The buffer overflow occurs within the sub_1000087C9 function, where user input ends up in the reply buffer. Some arithmetic done on the length winds up with an offset larger than 0x1000.

Figure 7 - Arithmetic on length create a larger offset

The 0x1000 reply buffer in Figure 6 is basically r14 and user input starts at offset 0x38 from r14 so that leaves 0xfc8 bytes for user input. When input reaches 0xfc8, strlen will return the same value. After some arithmetic, the final value will be 0xfcc [ ((0xfc8 + 1) + 3) & 0xfffffffc]. At address 0x100008ABD, the offset becomes 0x1004 [0xfcc+0x38] which will allow leaking 4 bytes and writing 4 bytes making it a solid primitive for exploitation. The leak may not be obvious, since the size is 0x1000 and the write is happening at 0x1004. This means 4 bytes are being read from the next block.

There are a couple of limitations here. The input must not contain a NULL character, because that will cause strlen to stop at that location. Another limitation is that the data written past the end of the buffer will always be the contents of var_DC, which is the error code returned by sub_100085B30.

Picture8.png

Conclusion

It is always interesting analyzing those kinds of bugs since it shows how a subtle mistake could lead to a vulnerability that ends up achieving code execution on a target system. Apple patched this bug (and many others) with macOS Mojave 10.14.5, which released on July 22, 2019. According to their write up, they addressed the vulnerability “with improved memory handling.” Interestingly, Apple lists the Disk Management component and the nebulous “Security” component with this CVE, with the “Security” portion being patched on macOS Sierra 10.12.6 and macOS High Sierra 10.13.6. Regardless, it’s always good to see vulnerable code get corrected.

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