header-logo
Suggest Exploit
vendor:
Mac OS X
by:
Ian Beer
7,8
CVSS
HIGH
Race Condition
362
CWE
Product Name: Mac OS X
Affected Version From: iOS and OS X
Affected Version To: iOS and OS X
Patch Exists: YES
Related CWE: CVE-2015-5820
CPE: o:apple:mac_os_x
Other Scripts: N/A
Tags: N/A
CVSS Metrics: N/A
Nuclei References: N/A
Nuclei Metadata: N/A
Platforms Tested: iOS and OS X
2015

Race Condition

This exploit is a proof-of-concept (PoC) for a race condition vulnerability in the IORegistryIterator::reset() function in the XNU kernel of iOS and OS X. The vulnerability is caused by a race condition between two threads, where one thread frees the done OSOrderedSet* while the other thread calls ->release on the now free'd OSOrderedSet. This can be exploited by a malicious user to gain control of the instruction pointer, which can be used to execute arbitrary code. The PoC uses OSUnserializeXML to unserialize an OSData object with controlled contents, which puts a controlled heap allocation at the head of the kalloc.80 freelist, giving the attacker control of the instruction pointer.

Mitigation:

As a hardening measure, it is suggested to flip the location of the obfuscated and unobfuscatable pointers in the OSOrderedSet implementation.
Source

Exploit-DB raw data:

Source: https://code.google.com/p/google-security-research/issues/detail?id=620

I wanted to demonstrate that these iOS/OS X kernel race condition really are exploitable so here's a PoC
which gets RIP on OS X. The same techniques should transfer smoothly to iOS :)

The bug is here:

void IORegistryIterator::reset( void )
{
    while( exitEntry())
    {}
    
    if( done) {
        done->release();
        done = 0;
    }
    
    where->current = root;
    options &= ~kIORegistryIteratorInvalidFlag;
}

We can call this from userspace via the IOIteratorReset method.

done is an OSOrderedSet* and we only hold one reference on it; therefore we can race two threads
to both see the same value of done, one will free it but before it sets done to NULL the other will
call ->release on the now free'd OSOrderedSet.

How to get instruction pointer control?

The XNU kernel heap seems to have been designed to make this super easy :) When the first thread frees
done zalloc will overwrite the first qword of the allocation with the freelist next pointer (and the last qword
with that pointer xor'd with a secret.) This means that what used to be the vtable pointer gets overwritten
with a valid pointer pointing to the last object freed to this zone. If we can control that object then
the qword at +0x28 will be called (release is at offset +0x28 in the OSObject vtable which is the base
of all IOKit objects including OSOrderedSet.)

This PoC uses OSUnserializeXML to unserialize an OSData object with controlled contents then free it, which
puts a controlled heap allocation at the head of the kalloc.80 freelist giving us pretty easy instruction pointer control.

I've attached a panic log showing kernel RIP at 0xffffff8041414141. You will probably have to fiddle with the
PoC a bit to get it to work, it's only a PoC but it does work! (I have marked the value to fiddle with :) )

As a hardening measure I would strongly suggest at the very least flipping the location of the obfuscated and
unobfuscate freelist pointers such that the valid freelist pointer doesn't overlap with the location of the
vtable pointer.


Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/39357.zip