Patching what Apple doesn’t want to or how to make your “old” OS X versions a bit safer

Today a local privilege escalation vulnerability was disclosed in this blog post. It describes a vulnerability in IOBluetoothFamily kernel extension (IOKit is a never-ending hole of security vulnerabilities).

Mavericks and most probably all previous versions are vulnerable but not Yosemite.
The reason for this is that Apple silently patched the bug in Yosemite. This is not a new practice, where Apple patches bugs in the latest and newly released OS X version and doesn’t care about older versions. Mavericks 10.9.5 update was released more or less around Yosemite date and this doesn’t look like a last minute bug found (although I’m going to confirm this). I bet that the bugs disclosed at SyScan 2013 by Stefan Esser still aren’t patched in Mountain Lion.

The blog post authors seem to experience the same “we don’t care attitude”. Their conclusions states:

We contacted Apple on October 20th, 2014, asking for their intention to back-port the security fix to OS X Mavericks. Unfortunately, we got no reply, so we decided to publicly disclose the details of this vulnerability: Yosemite has now been released since a while and is available for free for Apple customers; thus, we don’t think the public disclosure of this bug could endanger end-users.

The patch is very simple with only two instructions. With some luck we can patch it ourselves. What we need is a bit of unused space for installing the patch instructions. The Mach-O header is usually a good place in userland but in kernel extensions you get a no execute (NX) kernel panic. The header is also in not wired memory so it’s not a good place to install a patch. We are left with alignment space. If you search there are quite a few places with 15 alignment bytes (tip: load the driver into IDA, do a text search for align or a byte search for 90 90 90). That’s good enough for our patching, and we will need two of those islands since my proposed patch is 19 bytes long.

The two patch instructions are:

test ecx,ecx
js location

To install the patch we need to replace the first original instruction with a jump to the first island. Then we restore the original instruction and add the new patch instructions. We need to use a second island because the first doesn’t have enough space for this. The new code should be something like this:

original_address:
 jmp first_island
 nop
 remaining original_instructions
 (...)
first_island:
 jge location (original instruction)
 test ecx,ecx (patch instruction)
 jmp second_island
second_island:
 js location (patch instruction)
 jmp next_original_instruction

For Mavericks 10.9.5 you want to patch the following file /System/Library/Extensions/IOBluetoothFamily.kext/Contents/MacOS/IOBluetoothFamily.

Use the following file offsets and bytes:

Original instructions:
0x2855C: E9 B0 F7 FF FF 90
First island:
0x27D11: 0F 8D 43 0B 00 00
0x27D17: 85 C9
0x27D19: E9 23 2E 00 00
Second island:
0x2AB41: 0F 88 13 DD FF FF
0x2AB47: E9 16 DA FF FF

Save file, copy back to the original location, touch /System/Library/Extensions and reboot. Most probably this patch can be improved and reduced in size using smaller jump offsets if nearer islands are available. I didn’t bother to check.

Now go write to Apple and ask them to issue a proper patch. This total crap security policy must come to an end. Just in case you are wondering if this is an isolated case, check this Google Project Zero blog post. Two bugs that remain unpatched on OS X.

Oh, this will break the code signature but in Mavericks that’s just a warning and not a fatal error. You can resign with a developer kext certificate if you have one.

Have fun,
fG!

P.S.:
Another fine example of this crap security policy is that Apple fixed a few integer overflows in C++ code of libkern in Yosemite but didn’t bother to backport to Mavericks 10.9.5. This is just insane…