This finding was reported to Apple June 4th, and fixed on the 10.10.5 security update August 13th.
I have referenced
mach_shark a few times in previous posts. One feature of the tool is to produce a small c-stub that will allow you to replay mach messages. As hinted at in previous posts, there is a concept of state when doing MACH based IPC. Although the generated c-stubs from
mach_shark do not (yet) perform the full state manipulation to communicate with any arbitrary process, it still gave a starting point that can be used to fuzz. At a minimum, I can send these messages to the kernel or bootstrap / launchd.
Whats the first thing to do at this point? Find the most complex message I can and start a simple fuzzer.
One area that seemed to have a large attack surface was the
open command. Specifically I was interested in understanding how running a command like
open http://wuntee.sexy without the default browser open would spawn the browser, under the correct user context, and direct it to the URL.
After running the
open command through
mach_shark and looking over the ~300 different mach IPC requests, one stood out that seemed like a good starting point. A very large, complex XPC message that seemed to contain some objective-c class names.
fuzzing and crashing
The c-stub I referenced above is fairly dumb / simple in terms of output, but it construct the correct MACH message and properly looks up the port that the original message was trying to communicate with. An example output is:
With the payload from the
open command, I started a simple bit-flipping fuzzer, but sending the message directly to bootstrap /
launchd. Being so excited that I had something to run, I started it against my host machine. I went back to analyzing the different MACH payloads, leaving this running in the background. Within a few minutes, my entire machine “rebooted because of a problem.” I thought to myself - no way it could have been that easy! It was…
The details of why this caused my machine to reboot, and how I ended up debugging the crash can be found in the Debugging
launchd on OSX 10.10.3 post.
XPC serialization / deserialization
After pulling the fuzzer into a VM and then doing some root cause analysis of the crashes that were obtained, I was able to determine that the crash was happening during an XPC deserialization routine when calling
strlen - which seemed kind of strange.
From here I started looking into the XPC message structure. In order to do this, I created a simple service that accepted XPC messages and a simple client that I could create arbitrary messages to send. Form there I used the
mach_shark tool to intercept them and started documenting the different payload structures.
What I found out was the base structure of an XPC payload is:
Where the the header is:
xpc_type_$X is one of:
Root cause analysis
At the time of analyzing the crash I had no clue why on earth it could be crashing on
strlen (although it seems so obvious now that I am writing this). I had a fairly large payload which reproduced the crash 100% of the time, which made root cause analysis difficult. Furthermore, since
launchd was crashing, I was unable to dynamically debug / step through the code; I had to use coredumps as explained in a previous post. I initially attempted to reverse engineer
libxpc but the library was much more complicated than expected. I then switched gears to write my own XPC message [de]serializer to try and see what part of the payload triggered the crash.
The way I wrote this was to use a pointer to iterate through the start of each dictionary item, pull the values and create a new
xpc_dictionary object. When I finished writing the processing of the complex type, I had this:
Note the comment
// TKTK: Cant do this!! Size is user controlled. What I am doing is casting the raw memory to the base XPC message struct and processing based on the
type. With the complex type, there is a entry called ‘size’ that should be the full size of that portion of the XPC object (to the next key string). However, this value is something that is controlled by whoever is constructing the message. It should not be trusted for obvious reasons.
When I ran my deserialization routine over the payload that was causing my machine to crash, it turns out that my code was also crashing on a
strlen. I thought to myself - this couldn’t be a coincidence. After stepping through my deserialization routine’s crash, it became obvious. Just like my code, the
libxpc code is trusting the ‘length’ value of these complex types, increasing a pointer based on that value, and assuming that is where the next key should be. If you have the size set to it will attempt to read a string from the current offset + 0xFFFFFFFF, typically causing a segfault.
Confirming my suspicions
The next step was to create an arbitrary payload to test this theory. Of course I wanted to do it programatically, so I wrote a library that would take a set of custom
xpc_objects and serialize them to the raw XPC packet. In order to be able to control all aspects of each XPC data object I had to recreate each custom type. I then created a minimum payload that should trigger the crash.
Since this was being sent to
launchd I needed a routine that would actually attempt to deserialize my XPC object. Based on some previous research I did with
launchctl I knew that there was a basic XPC structure that needed to be sent, thus the ‘handle,’ ‘subsystem,’ and ‘routine’ keys. The code I used to do this with the complex string type was:
The code I used to trigger the crash:
Dissecting the payload, we can see:
Apple has fixed this specific instance of the bug, but before I upgraded to 10.11 I noticed that the class of bug still existed in the newest version of 10.10. As of 10.11, it seems that Apple has changed from using the ‘mach_msg_send’ function from userland to using another function call - based on the lack of the
mach_msg_send function call in
libxpc.dylib. I have yet to try any of the crashes on OSX 10.11+.
Impact / exploitability
It seems that OSX IPC is moving towards all applications using XPC. Although I did not see anywhere in the kernel that processes XPC (I did not look too hard), this bug was found in
launchd which is PID1 on OSX (similar to
init on Linux). Furthermore, there are dozens of IPC services running as root as well as unprivileged users that all use XPC as a communication mechanism. In turn, the impact of this bug, if it were to be exploitable would be quite critical.
This bug is an arbitrary read forward, and from the basic proof of concept does not look to be extremely useful (granted, my exploitation skills are minimal). Some theories I had:
- It may be possible to use this as a memory leak, if you were able to construct the heap in a way that the offset looked like a key to an XPC message, but would likely still crash as it attempts to interpret the rest of the serialized data.
- Some of the more complex MACH messages perform additional logic, such as transfer rights of ports and ownership of file descriptors. It would be possible to trigger this from a different code path that could allow for more exploitation potential. (Long shot)