Exploit-DB #39380
Description: Apple Mac OSX - OSMetaClassBase::safeMetaCast in IOAccelContext2::connectClient NULL Dereference
Type: dos
Platform: osx
Date: 2016-01-28
Author: Google Security Research

Exploit:
/*
Source: https://code.google.com/p/google-security-research/issues/detail?id=512

IOUserClient::connectClient is an obscure IOKit method which according to the docs is supposed to "Inform a connection of a second connection."

In fact IOKit provides no default implementation and only a handful of userclients actually implement it, and it's pretty much up to them
to define the semantics of what "informing the connection of a second connection" actually means.

One of the userclients which implements connectClient is IOAccelContext2 which is the parent of the IGAccelContext userclient family
(which are the intel GPU accelerator userclients.)

IOUserClient::connectClient is exposed to userspace as IOConnectAddClient.

Here's the relevant kernel code from IOAcceleratorFamily2:

__text:[PII_REDACTED]0057E6 ; __int64 __fastcall IOAccelContext2::connectClient(IOAccelContext2 *__hidden this, IOUserClient *)
__text:[PII_REDACTED]0057E6                 public __ZN15IOAccelContext213connectClientEP12IOUserClient
__text:[PII_REDACTED]0057E6 __ZN15IOAccelContext213connectClientEP12IOUserClient proc near
__text:[PII_REDACTED]0057E6                                         ; DATA XREF: __const:[PII_REDACTED]03BEE8o
__text:[PII_REDACTED]0057E6                                         ; __const:[PII_REDACTED]03D2D80o ...
__text:[PII_REDACTED]0057E6                 push    rbp
__text:[PII_REDACTED]0057E7                 mov     rbp, rsp
__text:[PII_REDACTED]0057EA                 push    r15
__text:[PII_REDACTED]0057EC                 push    r14
__text:[PII_REDACTED]0057EE                 push    r12
__text:[PII_REDACTED]0057F0                 push    rbx
__text:[PII_REDACTED]0057F1                 mov     rbx, rdi
__text:[PII_REDACTED]0057F4                 mov     r14d, 0E00002C2h
__text:[PII_REDACTED]0057FA                 cmp     qword ptr [rbx+510h], 0
__text:[PII_REDACTED]                 jnz     loc_590F
__text:[PII_REDACTED]                 lea     rax, __ZN24IOAccelSharedUserClient29metaClassE ; IOAccelSharedUserClient2::metaClass
__text:[PII_REDACTED]00580F                 mov     rax, [rax]
__text:[PII_REDACTED]                 mov     rdi, rsi             ; <-- (a)
__text:[PII_REDACTED]                 mov     rsi, rax
__text:[PII_REDACTED]                 call    __ZN15OSMetaClassBase12safeMetaCastEPKS_PK11OSMetaClass ; OSMetaClassBase::safeMetaCast(OSMetaClassBase const*,OSMetaClass const*)
__text:[PII_REDACTED]00581D                 mov     r15, rax             ; <-- (b)
__text:[PII_REDACTED]                 mov     r12, [rbx+518h]
__text:[PII_REDACTED]                 cmp     r12, [r15+0F8h]      ; <-- (c)
__text:[PII_REDACTED]00582E                 jnz     loc_590F
__text:[PII_REDACTED]                 mov     rax, [r15+0E0h]
__text:[PII_REDACTED]00583B                 mov     r14d, 0E00002BCh     
__text:[PII_REDACTED]                 cmp     rax, [rbx+4E8h]      ; <-- (d)
__text:[PII_REDACTED]                 jnz     loc_590F
...
__text:[PII_REDACTED]                 mov     rdi, [r15+100h]      
__text:[PII_REDACTED]                 mov     [rbx+510h], rdi
__text:[PII_REDACTED]                 mov     rax, [rdi]
__text:[PII_REDACTED]00588A                 call    qword ptr [rax+20h]  ; <-- (e)

At (a) we completely control the type of userclient which rsi points to (by passing a userclient io_connect_t to IOConnectAddClient.)
safeMetaCast will either return the MetaClassBase of the cast if it's valid, or NULL if it isn't. A valid cast would be an object
which inherits from IOAccelSharedUserClient2. If we pass an object which doesn't inherit from that then this will return NULL.

The "safeMetaCast" is only "safe" if the return value is checked but as you can see at (b) and (c) the return value of safeMetaCast
is used without any checking.

At (c) the qword value at 0xf8 offset from NULL is compared with this+0x518. That value is a pointer to an IntelAccelerator object on the heap.
In order to get past this check towards the more interesting code later on we need to be able to guess this pointer. Fortunately, nothing
untoward will happen if we guess incorrectly, and in practice we only need to try around 65k guess, even with kASLR :) Even so, there's *another*
check we have to pass at (d) also comparing against a heap pointer. Again we can guess this, but having to make two guesses each time
leads to an exponential slowdown... Except, notice that just before making the cmp at (d) r14d was set to 0xE00002BC; this is actually
the IOKit error code and gets returned to userspace! This means that we can actually make our brute-force attempts independent by checking the return
value to determine if we made it past the first check, and only then start guessing the second pointer.

In reality you can guess both the pointers in a few seconds.

After passing the cmp at (d) the code goes on to read a vtable pointer at NULL and call a virtual function at an address we can control :)

Tested on OS X 10.10.5 (14F27)
*/

// ianbeer

// build:clang -o client_connect client_connect.c -m32 -framework IOKit -g -pagezero_size 0x0

/*
Failure to check return value of OSMetaClassBase::safeMetaCast in IOAccelContext2::connectClient leads to
kernel address space layout leak and exploitable NULL dereference

IOUserClient::connectClient is an obscure IOKit method which according to the docs is supposed to "Inform a connection of a second connection."

In fact IOKit provides no default implementation and only a handful of userclients actually implement it, and it's pretty much up to them
to define the semantics of what "informing the connection of a second connection" actually means.

One of the userclients which implements connectClient is IOAccelContext2 which is the parent of the IGAccelContext userclient family
(which are the intel GPU accelerator userclients.)

IOUserClient::connectClient is exposed to userspace as IOConnectAddClient.

Here's the relevant kernel code from IOAcc