CVE-2015-3795
Background
This finding was reported to Apple June 4th, and fixed on the 10.10.5 security update August 13th.
References:
mach_shark
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:
and each xpc_type_$X
is one of:
For example:
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)