Tuesday, March 22, 2016

Race you to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel!

Posted by Ian Beer of Google Project Zero

The OS X and iOS kernel code responsible for loading a setuid root binary invalidates cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 old task port after first swapping cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 new virtual memory map pointer into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 old task object, leaving a short race window where you can manipulate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory of an euid 0 process before cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 old task port is invalidated. Going a step furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r this also allows you to also gain any entitlement and, on OS X, load an unsigned kernel extension.


I reported this bug to Apple on December 16th 2015 and it was patched in OS X 10.11.4/iOS 9.3 as CVE-2016-1757. For more technical details see cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original bug report where you can also grab cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 updated exploit.

Task Ports

If you’ve ever tried to use ptrace on OS X you were probably sorely disappointed. Whilst cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 syscall does exists it’s severely limited in what it can do. For example cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s no support for peeking and poking memory, a pretty fundamental use-case.


This is one of many areas of XNU where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 duality of its kernel is evident; along with ptrace cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s a parallel abstraction layer in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Mach side of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel which supports building debugging tools via a set of special mach task ports


Each process executing on OS X has a task port. For example, to allocate a page of memory in your process you might use code like this:


mach_vm_address_t addr = 0;
mach_vm_allocate(mach_task_self(),
                &addr,
                0x1000,
                VM_FLAGS_ANYWHERE);


mach_vm_allocate is a MIG method implemented by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel (and defined in mach_vm.defs in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 xnu kernel source.) The first argument to this method is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current task port; if you happened to have send rights to anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r task’s task port and you were to call this method passing that ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r task’s task port cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 allocation would end up in that ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r task’s virtual address space, even though your task made cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 call. Similarly cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mach_vm_read and mach_vm_write methods allow you to read and write cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory of ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r processes if you  have cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir task ports, effectively giving you complete control over cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m.


By default tasks only have send rights to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir own task ports, but it’s easy to give ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r processes send rights to your task port using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 standard mach messaging mechanisms.

execve

Usually new task ports will only be created when new processes are created (via fork or posix_spawn). Just execing a new binary won’t reset cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task port. This leads to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 obvious question: since having a send right to a process’s task port give you complete control over that process, how do cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y deal with SUID binaries where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exec call will increase cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 privileges of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process?


Looking through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exec code we find this snippet:


/*
* Have mach reset cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task and thread ports.
* We don't want anyone who had cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ports before
* a setuid exec to be able to access/control cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365
* task/thread after.
*/
ipc_task_reset(p->task);


Sounds sensible. But execve is a really complex syscall and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s a lot going on here:



This diagram outline cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 four major phases of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exec syscall which we’re interested in along with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 lifetimes of some data structures.


In phase 1 cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 old task port is still valid, and via cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task struct gives us access to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 old vm_map which still contains cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 virtual address space of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task prior to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exec. As part of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 load_machfile method cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel will create a new vm_map into which it will load cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 new executable image. Note though that at this point cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 new vm_map isn’t matched up with any task; it’s just a virtual address space with no process.


In phase 2 cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 parse_machfile method does cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 actual parse and load of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 MachO image into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 new virtual address space. It’s also during this phase that any code signatures which cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 binary has are checked; we’ll come back to that detail later.


In phase 3 load_machfile calls swap_task_map; this sets cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task struct to point to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 new vm_map into which cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 target executable was just loaded. From this point on any mach_vm* methods invoked on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 old task port will target cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 new vm_map.


In phase 4 exec_handle_sugid will check if this binary is SUID, and if it is, will reset cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task port, at which point cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 old task port will no longer work.

Somethings not right cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re…

From this high-level view it’s clear that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s a fundamental race condition. Between phase 3, when cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vm_map is swapped, and phase 4, when cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task port is invalidated, we will have full read/write access via cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 old task port to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 new vm_map containing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 loaded binary which is about to be executed, even if it’s SUID which means that we can just use mach_vm_write to overwrite its text segment with code we control!


From cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 diagram it’s hard to tell but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s actually quite a long period between those two phases, certainly enough time to sneak in a few mach kernel MIG calls.

Building a reliable exploit

There’s one small stumbling block to overcome though: although we can now try to write to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 new vm_map, where should we write? Userspace ASLR on OS X (for binaries, not dynamic libraries) is randomized per-exec so we first have to figure out where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel loaded cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 image.


Luckily with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task port we can do a lot more than just blindly read and write cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 new vm_map; we can also API’s like mach_vm_region to ask about properties of memory regions in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 map. In this case we just loop constantly asking for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 lowest mapping. When it changes we know that it changed to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 base address of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 image in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 new vm_map and  that we’re now in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 race window between phases 3 and 4.


Using this base address we can work out cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 binary’s entrypoint in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 new vm_map, mark that page as RWX using mach_vm_protect and overwrite it with shellcode using mach_vm_write.


Run root_shell.sh in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attached exploit to see this in action. It should work out of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 box on any semi-recent OS X system, using otool to extract cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 entrypoint of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 traceroute6 suid-root executable.

Going furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r: codesigning and entitlements

In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exec phases diagram I mentioned that parse_machfile (in phase 2) is responsible for both loading cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 MachO as well as verifying any code signatures present. You might ask why this is relevant on OS X since, unlike iOS, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s no mandatory userspace code signing and we can quite happily execute unsigned code anyway.


But OS X does use userspace code signing to enforce entitlements which are just xml blobs stored in your MachO and signed as part of your code signature.


Kernel code can check whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r user space processes have particular entitlements (via IOTaskHasEntitlement) and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel kext loading code will now only accept load requests from processes which are both running as root and also have cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 com.apple.rootless.kext-management entitlement.


This is particularly important since on OS X kext code signatures are enforced by userspace. This means that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kext-management entitlement is a vital part of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 separation between root in userspace and kernel code execution.


Two system binaries which do have cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kext-management entitlement are kextd and kextload; so in order to defeat kernel code signing we just need to first exec a suid-root binary to get root cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n exec eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r kextload or kextd to get cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 right entitlement.

Patching kextload

kextload is probably cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 easier target for a binary patch since it’s a standalone tool which can load a kext without talking to to kextd. We just need to patch cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 checkAccess function so that kextload thinks it can’t reach kextd and will try to load cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kext itself. Then patch out cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 calls to checkKextSignature and checkSignaturesOfDependents such that kextload thinks cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y succeeded and it will now load any kext, signed or unsigned :-)

The released exploit contain a binary patch file to remove cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 signature checks in kextload on OS X 10.11.3 as well as a simple HelloWorld kext and load_kext.sh and unload_kext.sh scripts to load and unload it. These can be run as a regular user. Look in Console.app to see cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel debug printf output.

1 comment: