CVE-2019-6225
Vulnerability
The problem is in the MIG code.
1 | routine task_swap_mach_voucher( |
this routine simply swaps two vouchers. Let’s take a look at the following codes.
1 | mig_internal novalue _Xtask_swap_mach_voucher |
Well, everything seems correct here. Increase reference count and decrease it later.
1 | new_voucher = convert_port_to_voucher(In0P->new_voucher.name); |
But the implementation of task_swap_mach_voucher
1 | kern_return_t |
Let in_out_old_voucher
points to new_voucher
.
Notice that new_voucher
and old_voucher
will point to the same voucher after the swapping routine, so ipc_voucher_release()
and convert_voucher_to_port()
actually decrease the reference count for a same voucher twice! Actually, there are two bugs in this routine. One can release the voucher and one can increase the reference count.
Exploit
Obviously, the routine above caused a UaF but the problems is how to exploit it. Usually, we can convert a UaF into type confusion. But vouchers are allocated in a specific kernel zone, ipc.voucher
, in XNU. If we want to achieve type confusion attack, we need to allocate the same memory space in another kernel zone.
Cross Zone Attack
Each XNU kernel zone has a free list and the memory allocator can retrieve free memory blocks from the free list. Once the elements in the free list run out, they need to allocate memory from the OS again. So if we can force the kernel to perform garbage collection and allocate tons of memory in other zones, we can make the ipc_voucher_t
pointer points to a new kernel zone other than ipc.voucher
.
Heap Spray
Depending on our exploitation strategy, we have two different ways to do the heap spraying.
OOL Ports
In XNU, we can send mach ports with out-of-line messages. Let’s take a look at the following codes.
1 | ipc_kmsg_copyin_ool_ports_descriptor( |
So with specific ports count, we can kalloc in any kalloc.x
zone we want. Here we show any example, we reallocate the ith_voucher
field in kalloc.1024
will 0xffffffffffffffff
.
1 | (lldb) p (*(thread_t)0xffffff8029275840).ith_voucher |
IOSurface
IOSurfaceRootClient
exports IOSurfaceRootUserClient::s_set_value(IOSurfaceRootUserClient*,void *,IOExternalMethodArguments *)
and use OSUnserializeXML
to unserialize XML. If we pass a crafted binary data to set_value
, we can alloc any data in any kalloc.x
zone we want. In this way, we can even construct a fake voucher instead of simple port pointer with OOL message.
1 | (lldb) p *(ipc_voucher_t)0xffffff802d843af0 |
Also, we can even replace the iv_port
field in voucher
, so that we can completely control the ipc_port
.
1 | (lldb) x/32xg 0xffffff80170cf690 |
Exploit Strategy
Based on two different cross zone attack method, we can have two different strategy.
- Spray pipe buffer
- use
thread_set_mach_voucher
to save our uaf voucher pointer - release the uaf voucher
- reallocate the uaf voucher with OOL ports pointer, let
iv_ref
field overlaps with the pointer - use CVE-2019-6225 again to tweak
iv_ref
, move the pointer to the pipe buffer control by us. - construct the fake port in pipe buffer
- receive the OOL ports again in userland
- construct tfp0, profit!
But it’s a little bit difficult to get a valid iv_ref
value since we need to make sure the higher 31 to 27 bits are 0.
So we can consider the following steps
- use
thread_set_mach_voucher
to save our uaf voucher pointer - release the uaf voucher
- replace the uaf voucher with fake voucher, and let
iv_port
points to the fakeipc_port
in userland - use
thread_get_mach_voucher
to get the fake port - use
pid_for_task
to read kernel - build tfp0, profit!
Get root privilege
KASLR
With clock_sleep_trap
we can brute force kernel slide.
Kernel Read and Write
We can still use pid_for_task
to read any kernel memory once we construct our fake task. Since iOS 11, Apple added a new mitigation that only kernel can resolve kernel task port. But if we want to achieve arbitrary kernel rw, we only need the vm map of kernel task. So we can copy the vm_map_t
pointer from kernel task to our fake task then we can get arbitrary kernel and then can get root privilege by overwriting cred. Check https://github.com/KpwnZ/g3tr00t for the exploitation detail.
References
https://googleprojectzero.blogspot.com/2019/01/voucherswap-exploiting-mig-reference.html