Monday, April 1, 2019

Splitting atoms in XNU

Posted by Ian Beer, Google Project Zero

TL;DR

A locking bug in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 XNU virtual memory subsystem allowed violation of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 preconditions required for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 correctness of an optimized virtual memory operation. This was abused to create shared memory where it wasn't expected, allowing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 creation of a time-of-check-time-of-use bug where one wouldn't usually exist. This was exploited to cause a heap overflow in XPC, which was used to trigger cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 execution of a jump-oriented payload which chained togecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r arbitrary function calls in an unsandboxed root process, even in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 presence of Apple's implementation of ARM's latest Pointer Aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication Codes (PAC) hardware mitigation. The payload opened a privileged socket and sent cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file descriptor back to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sandboxed process, where it was used to trigger a kernel heap overflow only reachable from outside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sandbox.

Exploit for iOS 12.0 on iPhone Xs .

Part I: A virtual memory bug

What's in your space?

Most operating systems maintain two data structures representing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 virtual address space of processes:

  • A management data-structure, which contains abstract descriptions of what every virtual memory address which is valid in a process should contain

  • A hardware data-structure, typically used by a hardware memory management unit to implement cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 virtual-to-physical address translations which happen each time memory is read or written

The management data-structures contain book-keeping information like "cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 4KB region from address 0x1234000 to 0x1235000 should contain cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bytes from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file /tmp/hello starting at offset 0x3000".

The hardware data-structures contain cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hardware-specific implementation details of how to translate from virtual address to physical memory address; cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hardware will use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m at runtime to find cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 physical addresses which should be used for each memory access.

In XNU cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 management data structure is a red-black tree of vm_map_entry structures, contained in a struct vm_map. There's generally one vm_map per task. For iOS on modern iPhones cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hardware data structures are ARM64 Translation Tables.

One of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 major responsibilities of an OS virtual memory subsystem is to keep cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se data structures in sync; modifications to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 high-level representation of virtual memory should be accurately reflected in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hardware data structures when required. The hardware structures are generally created lazily on demand in response to actual memory usage, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 management structures must be cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ground truth representation of what a task's virtual address space should contain.

Any bugs in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 maintenance of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se management structures are likely to have interesting consequences.

Copyin'

vm_map_copyin_internal in vm_map.c converts a virtual memory region from a task's vm_map into a "copied in" form, constructing a vm_map_copy structure representing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 copied virtual memory which can be passed around and subsequently mapped into anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r task's vm_map (or mapped back into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same vm_map which it came from.)

The function contains a while loop which iterates through each of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vm_map_entry structures making up cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 virtual memory region to be copied and tries to append a copied form of each vm_map_entry to a vm_map_copy structure.

Under certain circumstances this copy operation can be optimized into a move operation, here's a code snippet with verbatim comments describing one such case:

/*
 *  Attempt non-blocking copy-on-write optimizations.
 */
       
 if (src_destroy &&
      (src_object == VM_OBJECT_NULL ||
        (src_object->internal &&
         src_object->copy_strategy == MEMORY_OBJECT_COPY_SYMMETRIC &&
         !map_share)
       )
     )
 {
   /*
    * If we are destroying cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 source, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 object
    * is internal, we can move cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 object reference
    * from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 source to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 copy.  The copy is
    * copy-on-write only if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 source is.
    * We make anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r reference to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 object, because
    * destroying cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 source entry will deallocate it.
    */
   vm_object_reference(src_object);
   
   /*
    * Copy is always unwired.  vm_map_copy_entry
    * set its wired count to zero.
    */

   goto CopySuccessful;

This optimization will apply if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 source vm_map_entry represents anonymous memory (such as that returned via mach_vm_allocate) and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 semantics of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 copy operation being performed will cause that memory to be deallocated from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 source vm_map. In that case, as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 comment describes, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vm_map_entry can be "moved" from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 source vm_map into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vm_map_copy structure, racá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r than a copy-on-write copy being created.

In practise this optimization will be encountered when a mach message is sent containing an out-of-line descriptor with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 deallocate flag set. This is a low-overhead way to move large regions of virtual memory between processes, something which can happen with some frequency in XNU.

Only mostly atomic...

The vm_map_entry's making up cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 source of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 region to be moved will only be removed from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 source vm_map after cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y have all been copied into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vm_map_copy. This happens in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vm_map_delete call, right after cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 while loop:

 }   // end while(true)
   
 /*
  * If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 source should be destroyed, do it now, since cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365
  * copy was successful.
  */
 if (src_destroy) {
   (void) vm_map_delete(src_map,
                        vm_map_trunc_page(src_addr,
                                          VM_MAP_PAGE_MASK(src_map)),
                        src_end,
                        ((src_map == kernel_map) ?
                          VM_MAP_REMOVE_KUNWIRE :
                          VM_MAP_NO_FLAGS),
                        VM_MAP_NULL);

In order for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 move optimization to be correct it is fundamentally important that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 copy and removal of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 entries is performed atomically; nothing else should be able to mutate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 source vm_map while this is happening, as if it could it might also be able to perform an "optimized move" at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same time! In reality, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 atomicity is easy to break :(

Above cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 while loop which iterates through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vm_map_entry's in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 source region cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y take cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 source vm_map's lock:

 vm_map_lock(src_map);

but looking down cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code for calls to vm_map_unlock we find this (again, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 comment is verbatim from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 source) :

 /*
  *  Create a new address map entry to hold cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 result.
  *  Fill in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fields from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 appropriate source entries.
  *  We must unlock cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 source map to do this if we need
  *  to allocate a map entry.
  */
 if (new_entry == VM_MAP_ENTRY_NULL) {
   version.main_timestamp = src_map->timestamp;
   vm_map_unlock(src_map);
           
   new_entry =
     vm_map_copy_entry_create(copy,
     !copy->cpy_hdr.entries_pageable);
           
   vm_map_lock(src_map);

   if ((version.main_timestamp + 1) != src_map->timestamp) {
     if (!vm_map_lookup_entry(src_map,
                              src_start,
                              &tmp_entry))
     {
         RETURN(KERN_INVALID_ADDRESS);
     }
     if (!tmp_entry->is_sub_map)
       vm_map_clip_start(src_map, tmp_entry, src_start);
     continue; /* restart w/ new tmp_entry */
 }

We'll hit this path if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 region being copied is comprised of more than one vm_map_entry, since we allocate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first vm_map_entry for new_entry before initially taking cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 src_map lock.

Quickly dropping a very important lock and retaking it is a common anti-pattern I've observed across cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 XNU codebase; I'm sure this isn't cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 only instance of it. In this case this is presumably a hack because vm_map_copy_entry_create may in some cases need to take a vm_map lock.

After reacquiring cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 src_map lock cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y perform cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following check:

 if ((version.main_timestamp + 1) != src_map->timestamp)

The vm_map timestamp field is a 32-bit value incremented each time cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 map is unlocked:

 #define vm_map_unlock(map) \
   ((map)->timestamp++ , lck_rw_done(&(map)->lock))

This is trying to detect whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r thread acquired and dropped cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 lock while this thread dropped it cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n reacquired it. If so, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code checks whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re's still a vm_map_entry covering cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current address its trying to copy and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n bails out and looks up cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 entry again.

The problem is that this check isn't sufficient to ensure cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 atomicity of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 optimized copy; just because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re's still a vm_map_entry covering this address doesn't mean that while cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 lock was dropped anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r thread didn't start its own optimized move operation.

The entries which have previously been appended to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vm_map_copy aren't also invalidated, meaning cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 atomicity of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 optimization can't be guaranteed.

The locking is insufficient to prevent two threads concurrently believing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y are performing atomic vm_map_entry move operations on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same vm_map_entry.

Overlap

Triggering cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 issue requires us to create two threads, each of which will attempt to perform cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 move optimization at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same time. If we create a large virtual memory region consisting of alternating anonymous memory entries and memory object entries, we can ensure that copies of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 region will require multiple iterations of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vm_map_copy building loop which contains cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bad locking primitive. I chose to structure cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 region as shown in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 diagram below, where two out-of-line descriptors consisting of alternating mapping types overlap by one anonymous memory entry. It is this entry to which we want to have cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 move optimization applied twice, meaning it will appear in two vm_map_copy lists, each believing it has also been atomically removed from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 source address space:

         
By sending one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se out-of-line descriptors to anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r process via a mach message, and one to ourselves, we will inadvertently create shared memory! This means that once both processes have received cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mach messages cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sender's writes to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 anonymous page are reflected in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 target's address space, something which violates cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 semantics of mach messages.

build-your-own-bug with virtual memory issues

In 2017 lokihardt found CVE-2017-2456, a similar style of issue involving out-of-line descriptors being backed by shared memory. He found that this could be turned into a heap overflow in libxpc when it parses an XPC dictionary. Specifically, libxpc will call strlen on a buffer in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 now-shared memory, use that length plus one to allocate a buffer, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n call strcpy to fill cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buffer. The strcpy will copy until it finds a NULL byte, unaware of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 destination buffer.

By itself such code does not have a bug, because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 semantics of mach messages imply that received out-of-line descriptors cannot be modified by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sender. But, if due to a virtual memory bug, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory is actually shared cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n this code has a time-of-check-time-of-use "bug."

We'll use this same primitive to build a controlled heap overflow primitive with which we can target any XPC service. I used cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 custom XPC serialization library I wrote for triple_fetch. For more details check out cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit. From here on we'll assume we can groom cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 heap using XPC and cause a heap overflow with non-null bytes during deserialization of an XPC dictionary.

Part II: Escaping userspace sandboxes with PAC

Apple's latest A12 system-on-a-chip is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first widely deployed implementation of ARMv8.3's Pointer Aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication Codes feature, commonly referred to as PAC. For a deep-dive into PAC internals check out Brandon Azad's prior work. In this section I'll explore PAC's impact on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploitation of memory corruption bugs in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 context of a userspace sandbox escape. For a more technical overview read section D5.1.5 of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ARM manual.

unPACking Pointer Aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication Codes

PAC introduces a new set of instructions which treat some of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 higher bits of a 64-bit value as an "aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code" field. There are instructions to add, validate and remove aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication codes, with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 intended use case being to add cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication codes into pointers stored in memory. The idea is that an attacker now has to be able to guess, forge or leak a valid aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y wish to corrupt a pointer and have that pointer used by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 target process. Let's take a closer look:

Pointers
In iOS userspace pointer aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication codes are 16 bits wide, occupying cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bits above cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 39-bit userspace virtual address space:


A pointer without an aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code might look like this:
 0x000000019219816c

And that same pointer with an aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code might look like this:
 0x001f32819219816c

(Note that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 highlighting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re isn't aligned on a 39-bit boundary, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code actually begins at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 high bit of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 8.)

The lower 39 bits of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code match cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same bits in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer without cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code. The pointer containing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code can't be dereferenced; it's outside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 valid address space (unless cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code were all zeros.) Instead, ARMv8.3 provides instructions to remove and verify cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code. If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 verification fails 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 hardware will flip a high bit in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 resulting pointer, causing it to become invalid. It's only when code attempts to dereference such a pointer that an address translation exception will occur; a PAC code verification failure by itself doesn't cause an exception.

Contexts
The aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code is derived from three sources: a key, a value to be aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticated (cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer), and a 64-bit context value. It's this context value which enables many of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 more interesting applications of PAC. For example, a pointer's PAC can be created using 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 pointer itself, meaning that even if a PAC'ed pointer could be disclosed to an attacker, it would only be valid were it reused at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same address. In many cases, however cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 context value is zero, and PAC provides convenience instructions for specifying a zero context value.
Keys
The kernel manages five keys, grouped into three data types (instruction, data and general) and two key families (A and B). In iOS userspace cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 A-family keys are shared between all processes and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 B-family keys are unique per-process. Userspace cannot read or write cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se keys, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y are controlled by EL1 (cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel) and used implicitly by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC instructions.

Instructions
Section C3.1.9 of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ARM manual describes all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 new pointer aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication instructions. They fall into four categories:

 PAC* : add an aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code to a value
 AUT* : aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticate a value containing an aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code
 XPAC* : remove an aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code without validation
 COMBINATION : combine one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 above PAC operations with anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r instruction

Let's look at PACIA. The I and A tell us which key this instruction uses (cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 A-family Instruction key.) PACIA has two operands:

 PACIA <Xd>, <Xn|SP>

Xd is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 register containing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer which should have an aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code added to it. Xn|SP is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 register containing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 context value which should be used in combination with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 A-family instruction key to generate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code, which can be a general-purpose register or cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SP register.

There are many variants of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC* instructions for using different keys and specific context values, for example:

 PACIZA : use zero as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 context value for creating an aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code for register Xd with A-family instruction key

 PACDZB : use zero as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 context value for creating an aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code for register Xd with B-family data key

 PACIBSP : add an aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code to X30 (cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 link register, containing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 return address from a function call) using SP as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 context value and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 B-family instruction key

There are similar variations for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 AUT* instructions, which perform cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 inverse verification operation to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir PAC* counterparts:

 AUTIA ,

Here Xd is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 register containing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer with an aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code to be validated. Xn|SP is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 register containing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 context value; in order for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication to succeed cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 context value passed here must match cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value provided when cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code was added. This variant will use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 A-family instruction key. If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code matches, it is stripped from register Xd such that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 register contains cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original raw pointer.

If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code doesn't match (because eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer value is incorrect, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code is incorrect, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 context value is incorrect or cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 key is different) 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 code is still stripped from register Xd but a high bit is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n flipped in Xd such that any subsequent dereference of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer would cause an address translation exception.

AUTIZA, AUTDZB, AUTIBSP and so on perform cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 inverse aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication operation to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir PAC* counterparts.

The XPAC* instructions remove cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC bits from a pointer without verifying cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code.

The combination instructions provide simple primitives for using PAC to perform one of four common operations:

 B(L)RA* : branch (and link) with aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication

 RETA* : return with aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication

 ERETA* : return across exception level with aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication

 LDRA* : load from address with aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication

These instructions also support using various keys and fixed or particular context values, for example:

 RETAB: use SP (cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack pointer) as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 context value to aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticate LR (cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 link register) using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 B-family instruction key and if aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication is successful continue execution at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticated LR value, but don't write cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticated value back to LR.

 BLRAAZ : use zero as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 context value to aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 contents of register Xn using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 A-family instruction key. If aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication is successful, continue execution at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticated Xn address and store PC+4 into LR (cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 link register) but don't write cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticated value of Xn back.

PAC primitives

In iOS 12 on A12 devices cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is some compiler support to use some of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 new PAC instructions to build new security primitives.

For example: as a mitigation against return-oriented-programming (ROP) function prologues and epilogues have changed from looking like this:

 SUB      SP, SP, #0x20
 STP      FP, LR, [SP,#0x10]
 ADD      FP, SP, #0x10
 ...
 LDP      FP, LR, [SP,#0x10]
 ADD      SP, SP, #0x20 ; ' '
 RET

to looking like this:

 PACIBSP
 SUB      SP, SP, #0x20
 STP      FP, LR, [SP,#0x10]
 ADD      FP, SP, #0x10
 ...
 LDP      FP, LR, [SP,#0x10]
 ADD      SP, SP, #0x20 ; ' '
 RETAB

PACIBSP uses cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value of SP at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function entry point as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 context value to add an aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 B-family instruction key to LR (cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 link register, containing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 return address.) LR is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n spilled to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack. At cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function, when SP should be equal to its value when cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function was entered, RETAB uses SP as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 context value again to verify LR's aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code after loading it from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack. If LR's code is valid, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n execution continues at that address.

What does this mean in practice? Since cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 B-family keys are unique per-process on iOS it means that from anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r process we cannot forge a fake return address which would pass cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication check in RETAB by running cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PACIBSP instruction ourselves. In addition, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 use of SP as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 context value means that even if we had cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ability to disclose stack memory we would only be able to reuse aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticated pointers when cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value of SP matches. It's important to observe here that this just breaks a particular technique commonly seen in public exploits; whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r use of that technique is a necessary part of exploitation is anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r question.

In general, almost all function pointers are now somehow making use of PAC: weakly protected pointers use an A-family key and a zero context value, while strongly protected pointers use a B-family key with a non-zero context derived from some runtime value.

Necessary compromises...

The per-process B-family keys are only used in a handful of situations. The more common use of A-family shared keys is a necessary compromise. Consider cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pages of memory containing C++ vtables in shared libraries. These pages are copy-on-write shared between processes. If each pointer in each vtable contained an B-family aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code, 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ý bet365se pages could no longer be copy-on-write shared between all processes, as each process would have unique vtables. This would introduce an extreme memory and performance overhead as much of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 shared cache would have to be copied and "reaucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticated" each time a new process were created.

The use of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 B-family keys for ROP mitigation is possible because a stack frame is never shared between processes (unless you're doing something really weird...). For ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r possible uses of PAC it's much harder to assert that a particular pointer will never escape cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 confines of a particular process, even in a COW way.

Exploiting memory corruption in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 presence of PAC

The attack scenario is important to consider when discussing exploitation and mitigations. The exploit I describe here assumes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker already has native code execution of some sort. Although cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 proof-of-concept exploit provided is a "malicious app", that is only one possible scenario. Similar primitives to those used by this exploit could also be implemented from a Safari WebContext exploit able to write shellcode to JIT memory, or even with only an arbitrary read/write primitive and an A-family PAC signing oracle.

(There is some usage of PAC in JavaScriptCore on A12 to try to provide some integrity while native code is being emitted; bypassing this is left as an exercise for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 reader ;) )

Given cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se attack scenarios, we can assume that an attacker is able to forge PAC codes which use A-family keys. Since cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se keys are shared between all processes, if we execute an instruction like PACIA in our attacker process, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 resulting PAC code will also be valid for identical inputs in anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r process.

New Mitigations; New Primitives
Using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 atomicity bug, we've built a heap corruption primitive targeting libxpc which we can trigger by sending an XPC dictionary to a target.

In my triple_fetch exploit from 2017, which also targeted a bug in libxpc, I corrupted an objective-C object's isa pointer. From cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re you get control of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 selector cache and from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re complete control of PC when a selector is called.

On A12 devices, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 objective-C selector cache now uses cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 B-family instruction key to aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticate entries in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 selector cache:

_objc_msgSend:
...
 LDP   X17, X9, [X12] ; X12 points into selector cache
                      ; X17 := fptr
; X9  := selector ptr
 CMP   X9, X1         ; does cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 cached selector ptr match?
 B.NE  no_match       ; no? try next one if more entries, ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rwise:
 EOR   X12, X12, X1   ; XOR cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 selector pointer into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 context ;)
 BRAB  X17, X12       ; yes? Branch With Aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication
                      ;      using B-family Instruction key
                      ;      and selector cache entry address
                      ;      as context

(The selector XOR is a recent addition to prevent an aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticated function pointer being reused for a different selector but in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same cache slot)

Without cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ability to forge or disclose B-family aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticated pointers we can't simply point to a fake selector cache. This breaks cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fake selector cache technique.

The trick I'm using here is that while cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 selector cache entries are "tied" to a particular cache by PAC, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 isa pointers (which point to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 objective-C class object) are not tied to particular objects. An objective-C object still has a "raw" isa class pointer as its first 8 bytes. This means we can still use a memory corruption primitive to replace an object's isa pointer with anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r type's isa pointer, allowing us to create a type confusion. We cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n just need to find a suitable replacement type such that an A-family aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticated function pointer will be read from it and called, as opposed to a fake selector cache. Since we can forge A-family aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticated pointers this will give us initial PC control.

As a place to start I began looking through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 various implementations of XPC object destruction in libxpc. These are cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 methods with names like __xpc_TYPE_dispose, called when xpc objects are freed.

For example, here's a snippet from __xpc_pipe_deserialize:

 PACIBSP
 STP      X20, X19, [SP,#-0x10+var_10]!
 STP      X29, X30, [SP,#0x10+var_s0]
 ADD      X29, SP, #0x10
 MOV      X19, X0
 LDR      W0, [X19,#0x24] ; name
 CBZ      W0, loc_180AFFED0
 BL       __xpc_mach_port_release

We could use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 isa overwrite technique to craft a fake xpc_pipe object such that this method would be called and we could cause an arbitrary mach port name to be passed to mach_port_deallocate. You could cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n use techniques such as that which I used in mach_portal, or Brandon Azad used in blanket to impersonate an arbitrary mach service.

Note that for that we wouldn't need to forge any PAC aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticated pointers.

Instead we're deliberately going to get PC control, so we need to read more methods. Here's cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 start of __xpc_file_transfer_dispose:

 PACIBSP
 STP      X20, X19, [SP,#-0x10+var_10]!
 STP      X29, X30, [SP,#0x10+var_s0]
 ADD      X29, SP, #0x10
 MOV      X19, X0
 LDR      W8, [X19,#0x58]
 CMP      W8, #1
 B.EQ     loc_180B06CDC
 LDR      X0, [X19,#0x40] ; aBlock
 CBZ      X0, loc_180B06C70
 BL       __Block_release_0


If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 qword at X0+0x40 is non-zero it will be passed to _Block_release, which is part of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 open-source libclosure package:

void _Block_release(const void *arg) {
 struct Block_layout *aBlock = (struct Block_layout *)arg;
 if (!aBlock) return;
 if (aBlock->flags & BLOCK_IS_GLOBAL) return;
 if (! (aBlock->flags & BLOCK_NEEDS_FREE)) return;

 if (latching_decr_int_should_deallocate(&aBlock->flags)) {
   _Block_call_dispose_helper(aBlock);
   _Block_destructInstance(aBlock);
   free(aBlock);
 }
}

Here we can see cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 argument is actually a Block_layout structure. The code checks some flags, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n decrements a reference count. If it decides that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 object should be freed it calls _Block_call_dispose_helper:

static void _Block_call_dispose_helper(struct Block_layout *aBlock)
{
 struct Block_descriptor_2 *desc = _Block_descriptor_2(aBlock);
 if (!desc) return;

 (*desc->dispose)(aBlock);
}

This clearly calls a function pointer from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 block structure. Let's look at this in assembly, here from 12.0:

 LDR     X8, [X19,#0x18] ; read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Block_descriptor_2 pointer from
                         ;   +0x18 in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 block
 LDR     X9, [X8,#0x18]! ; bump that pointer up by 0x18 and load cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365
                         ;   value cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re into x8
 AUTIA   X9, X8          ; aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticate X9 using A-family instruction
                         ;   key and X8 (cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer was
                         ;   read from) as context
 PACIZA  X9              ; add a new PAC code to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function pointer
                         ;   using A-family instruction key and a
                         ;   zero context
 MOV     X0, X19         ; pass cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 block as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first argument
 BLRAAZ  X9              ; branch with link register and aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticate
                         ;   using A-family instruction key and
                         ;   zero context

(this code has changed slightly in later versions but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 functionality we're using remains cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same)

This gives us a path from corrupting an objective-C object pointer to PC control which doesn't involve any B-family keys. A prerequisite is that we can place known data at a known location, since we will need to forge cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 context value here:

 AUTIA   X9, X8

which is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address from which X9 was read. For this I'm using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same mach_msg OOL_DESCRIPTOR spray technique which continues to work on iOS 12. Note that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory overhead for this is very low, as we are actually just sending multiple copies of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same anonymous region for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 spray.

Putting those steps togecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r, our strategy looks like this:

Build an XPC dictionary (inside a region of memory which we can target with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 non-atomic copy when we send it) which grooms cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 heap such that we can land cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bad strcpy buffer right before an xpc array backing buffer. Trigger cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 non-atomic move bug such that we can continue to write to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 serialized XPC dictionary while cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 target process is deserializing it, and use that to cause cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bad strcpy, overflowing into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first pointer in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 xpc array backing buffer.

Point that to a crafted XPC object contained in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 OOL_DESCRIPTOR heap spray which has an xpc_file_transfer isa pointer as its first member. When cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 xpc array is destroyed __xpc_file_transfer_dispose will be called which will follow a controlled pointer chain to call an A-family aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticated function pointer.

This diagram shows cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 layout in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker's address space of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 XPC dictionary inside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 non-atomic region:


The XPC dictionary contains duplicate keys which it uses as a primitive for grooming cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 heap and making holes. It attempts to groom a layout similar to this:


If everything goes to plan by flipping cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 flipper byte in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sender process we can race cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 strlen;malloc;strcpy in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 XPC deserialization code such that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 target first sees a short string (cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 length of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 undersized strcpy dest buffer, which malloc should slot right before cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 target xpc_array backing buffer if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 groom worked) 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 null byte is replaced by a non-null byte when read by strcpy meaning cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 copy will proceed off cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 undersized strcpy dest buffer and corrupt cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first entry in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 xpc_array's backing buffer, which is an array of pointers (or tagged pointers) to xpc objects.

We corrupt cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first pointer to instead point to a fake xpc_file_transfer object in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 heapspray which we try to place at 0x120200120:


When cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 xpc_array containing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 now-corrupted pointer is released it will release each of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 entries in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 array, causing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fake OS_xpc_file_transfer isa to be read leading to 0x120200120 being passed as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first (self) argument to __xpc_file_transfer_dispose. This code reads cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fake block pointer at 0x120200160, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n reads cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fake descriptor pointer at 0x1202000018 and finally performs a PAC aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer read from 0x120200098 using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 A-family instruction key and 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 pointer as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 context.

The exploit uses a small assembly stub to allow us to forge a valid pointer here:

 .globl  _pacia
 .align  2
 _pacia:
   pacia x0, x1
   ret

Filling our heapspray memory with a repeated page containing such a structure we can gain PC control :)

goto 10

Previously I might have looked to point cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 initial PC value to a stack pivot, allowing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 chaining togecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r of ROP gadgets by popping fake stack frames. The issue now, as we saw earlier, is that even if we gain control of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack pointer cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 spilled LR values (return addresses) are aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticated with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 B-family instruction key and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack pointer as context. This means we can't forge cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m from our attacking process.

Again, as with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 selector cache, this is just a mitigation against a particular technique, not something which is fundamentally required for exploitation.

The end goal here is to be able to move from controlling PC once to controlling it repeatedly, ideally with arbitrary, controlled values in a few argument registers so we can chain arbitrary function calls. Really nice to have would be cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ability to pass return values from one arbitrary function call as an argument to a later one.

Techniques which achieve functionality like this are sometimes referred to as JOP (Jump-Oriented-Programming) which is now used as a catch-all term for all techniques which chain togecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r multiple PC controls without using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack. All cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 gadgets I use here were found manually just with a few regexs in IDA Pro.

The first type of gadget I wanted was something which would call a function pointer in a loop, with some change in arguments each time. Since libxpc was already loaded into IDA, that's where I started looking. Here's a screenshot from IDA of _xpc_array_apply_f (it's easier to see cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 loop structure in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 graph view:)


This looks like a good loop primitive. The intended functionality here is to pass each element of an xpc_array to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function pointer supplied in X2. If we can reach here with a controlled value in X0 (a fake xpc_array) and X2 (function pointer), we can get cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function pointer called in a loop with a different, controlled value in X1 each time. Specifically it's going to read a length value from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fake xpc_array+0x18 cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n call cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function pointer repeatedly passing each element from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fake xpc_array backing buffer pointed to by X0+0x20 as X1 each time.

Gadget Collection

We need a few gadgets eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r side of this loop primitive. When we first get PC control X19 points to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 base of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 heap spray. We need to get from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re to control of PC, X0 and X2 in order to use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 loop gadget.

This instruction sequence inside libc++ gets us from X19 to X0, X1 and PC again:

18004816C:
 LDP    X0, X1, [X19,#0x48]   ; load X0 from [X19+0x48] and
                              ;      X1 from [X19+0x50]
 LDR    X8, [X0]              ; X0 is supposed to be a C++ object
                              ; pointer, so read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vtable pointer
 LDRAA  X9, [X8,#0x28]!       ; aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticate X8 (cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vtable pointer)
                              ; with a zero context value and
                              ; A-family data key. Add 0x28 to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365
                              ; aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticated vtable pointer and read
                              ; cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function pointer cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re into X9
                              ; cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n write cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 target address back
                              ; into X8 (so X8 points to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function
                              ; pointer in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vtable)
 MOVK   X8, #0xD96D,LSL#48    ; load cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 high 16-bits of X8 with a
                              ; constant representing a type-tag for
                              ; cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 inheritance hierarchy expected
                              ; at this callsite
 ADD    X2, SP, #0x50+var_40
 ADD    X4, SP, #0x50+var_48
 MOV    X3, X20
 BLRAA  X9, X8                ; branch and link with aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication
                              ; using A-family instruction key and X8
                              ; (address of vtable function pointer
                              ; | (type_tag << 48)

To use that we need to forge two uses of PAC A-family keys, which we can do. Note that each of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se gadgets end by calling function pointers read from memory which we control. This is how we are able to link cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m togecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r.

To reach our loop primitive we need to control X2 as well as X0, which we can get by chaining this sequence next:

18082B660:
 MOV     X22, X1
 MOV     X19, X0
 STR     X22, [SP,#0x50+var_48]
 MOV     W24, #0x16
 CBZ     X19, loc_18082BB3C
 CBZ     X22, loc_18082BB3C
 LDR     X8, [X19,#0x18]
 CBZ     X8, loc_18082B698
 LDR     X2, [X19,#0xC8]
 ADD     X1, SP, #0x50+var_48
 MOV     X0, X22
 BLRAAZ  X8

This also calls through a function pointer which uses PAC, but still an A-family key (with a zero context) which we can easily forge.

By pointing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fake xpc_array object into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 heap spray we can now repeatedly get cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same function pointer called with a different value in X1 each time. We now want to find a gadget which lets us turn that into a more controlled arbitrary function call primitive. Again we can reused some intended functionality.

I stumbled across IODispatchCalloutFromCFMessage a while ago while reading IOKit code; it's used under cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hood to power asynchronous notification callbacks in IOKit. The userspace process receives messages on a mach port receive right it owns, and from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 message reads a function pointer and arguments cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n calls cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function pointer with those arguments. I had filed it away as a potential exploitation technique for a mach port name management bug, but it also provides a nice primitive for this exploit.

The method signature is this:

void
IODispatchCalloutFromCFMessage(CFMachPortRef port __unused,
                              void *_msg,
                              CFIndex size __unused,
                              void *info __unused)

Note that all arguments apart from _msg are unused, so only X1 control is required to use this method. The function pointer to call (aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticated with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 A-family instruction key and zero context) and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 parameter are read from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 _msg pointer. There are some constraints: you can only pass four arguments, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 second one can only be 32-bits, but this is enough to start with.

If we set IODispatchCalloutFromCFMessage as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function pointer argument to _xpc_array_apply and make each element of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fake xpc_array be a pointer to a fake mach message matching cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 format expected by IODispatchCalloutFromCFMessage cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n we can chain togecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r an arbitrary number of basic function calls.

There are a few more gadget primitives which make writing a full payload easier:

retcapture
The prototype of IODispatchCalloutFromCFMessage says its return type is void, and reading cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 assembly we can see that actually cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 return value (X0) from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function pointer it calls will survive in X0 through to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end of IODispatchCalloutFromCFMessage, meaning in practice IODispatchCalloutFromCFMessage returns cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 values returned by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 called function pointer. This means we can wrap IODispatchCalloutFromCFMessage in anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r gadget which calls a controlled function with a controlled value in X1 and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n writes that return value to a memory address we control.

A bit of searching finds this inside libsystem_trace:

 PACIBSP
 STP      X20, X19, [SP,#-0x10+var_10]!
 STP      X29, X30, [SP,#0x10+var_s0]
 ADD      X29, SP, #0x10
 MOV      X19, X0
 LDP      X8, X1, [X19,#0x28]
 LDR      X0, [X8,#0x18]
 MOV      X8, X0
 LDR      X9, [X8,#0x10]!
 BLRAA    X9, X8
 LDR      X8, [X19,#0x20]
 LDR      X8, [X8,#8]
 STR      X0, [X8,#0x18]
 LDP      X29, X30, [SP,#0x10+var_s0]
 LDP      X20, X19, [SP+0x10+var_10],#0x20
 RETAB

This method takes a single argument from which, through a series of dereferences, it reads a function pointer to call as well as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 X1 argument to pass. It calls cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function pointer cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n writes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 return value from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 call into an address read from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input argument.

If we use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 initial arbitrary call gadget to call this, passing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 required descriptor in X1, we can use this to call cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 arbitrary call gadget again, but now cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 return value from that inner call will be written to a controlled memory address.

By carefully choosing that memory address to overlap with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 argument descriptors for later calls we can pass cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 return value from one arbitrary call as an argument to a later call.

memory_copy
 LDR    X9, [X0]
 STR    X9, [X3]
 B      end
end:
 MOV    X0, X8
 RET

This gadget can be called using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 arbitrary call gadget to read a 64-bit value from a controlled address and write it to anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r controlled address.

indirect_add
 LDR    X8, [X0, #0x18]
 ADD    X8, X8, X1
 STR    X8, [X3]
 MOV    W0, #0x0
 RET

This gadget can also be called using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 arbitrary call gadget and can be used to add an arbitrary value to a value read from a controlled memory address, and write that sum back to memory.

The exploit contains various macros which seek to aid combining cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se primitives into useful payloads. It might seem like this is quite a limited set of primitives, so let's demonstrate a practical use by building a payload to open a PF_KEY socket in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 target process and smuggle it back to ourselves so we can trigger CVE-2019-6213, a kernel heap overflow not reachable from inside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 app sandbox.

Stealing sock(et)s

Unix domain sockets are cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 canonical way to send file descriptors between processes on UNIX OSs. This is possible on iOS, indeed see P0 issue 1123 for a bug involving cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m. But we have an alternative:  XNU has cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 concept of a file_port, a mach port which wraps a file descriptor. We can use this to easily send a socket file descriptor from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 remote task back to ourselves.

remote space introspection

In earlier exploits like triple_fetch I sprayed mach port send rights into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 target cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n guessed cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir names in order to send mach messages back to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacking process. Apple have since introduced some randomization into mach port names. The generation number now wraps randomly at eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r 16, 32, 48 or 64. This makes guessing remote mach port names less reliable.

Given that we can chain togecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r arbitrary function calls, what if we just remotely enumerate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mach port namespace in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 target and find cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 name of a sprayed mach port send right in a more deterministic way?

Here's cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 prototype for mach_port_space_info:

kern_return_t mach_port_space_info(
 ipc_space_inspect_t task,
 ipc_info_space_t *space_info,
 ipc_info_name_array_t *table_info,
 mach_msg_type_number_t *table_infoCnt,
 ipc_info_tree_name_array_t *tree_info,
 mach_msg_type_number_t *tree_infoCnt);

For cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 given task port this method will return a descriptor for each mach port name in that task's mach port namespace, containing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 port's name, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 rights cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task has and so on.

It might seem at first glance like this method would be hard to call given cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 limitations of our loop-call gadget (only 4 arguments, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 second only 32-bit.) The insight here is that this function is just a MIG generated serialization function. The functionality is really reached by sending a mach message to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task port, something which can be achieved by calling mach_msg_send which only requires control of one argument.

By sending a mach message to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task_self port with a msgh_id value of 3223 and a valid reply port cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n receiving cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 reply via mach_msg_receive we can get an out-of-line descriptor containing all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task's mach port names. We can use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 indirect add and memory copy gadgets to read a sprayed mach port name from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mach_port_space_info reply message and send a message containing a file_port containing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PF_KEY socket to it.

The heap spray also contains two skeleton mach messages which cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 payload uses, one for mach_port_space_info and one for sending cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file_port back to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker. Here's a pseudo-code implementation of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 entire payload functionality; take a look at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 real payload in unPACker.c to see this pseudocode translated into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 JOP macros.

// make sure cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mach_port_space_info has
// cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 right name for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task port
*task_port = ret_wrapper(task_self_trap())

// get cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 thread's mig reply port
*reply_port = ret_wrapper(mig_get_reply_port())

// write those two values into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 correct places
memory_move(mach_port_space_info_msg.remote_port, task_port)
memory_move(mach_port_space_info_msg.local_port, reply_port)

// send cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mach_port_space_info request
mach_msg_send(mach_port_space_info_msg)

// fill in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 reply port name in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 reply message
memory_move(port_space_reply.local_port, reply_port)

// receive a reply
mach_msg_receive(port_space_reply)

// cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 port space is guaranteed to be at least as large
// as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 number of ports we sent, so add that number*4*7
// to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 received OOL desc pointer
add_indirect(port_space_reply.ool.address, 4*7*0x1000)

// now we can be pretty sure that this port name is
// a send right back to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker
memory_move(exfil_msg.remote_port, port_space_reply.ool.address)

//this socket write should go to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 correct place for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 next call:
*socket = ret_wrapper(socket(X,Y,Z))

// need to call fileport_makeport(fd, &port), so need arbitrary x1
// can get that via cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 TINY_RET_WRAPPER
*fileport = arbitrary_x1(fileport_makeport, socket, &fileport)

// write cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fileport into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exfil message
memory_move(exfil_msg.port_desc.name, fileport)

// send cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exfil message
mach_msg_send(exfil_msg)

In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sender we cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n wait to receive a message on a portset containing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sprayed send rights; if we receive a message before we timeout cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n we read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fileport from it, extract cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fd, and trigger cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PF_KEY kernel bug!

panic(cpu 1 caller 0xfffffff0156b8578): "a freed zone element has been modified in zone kalloc.80: expected 0 but found 0x5c539a7c41414141, bits changed 0x5c539a7c41414141, at offset 0 of 80 in element 0xffffffe00394b3e0, cookies 0x3f0011f52a19c140 0x53521b0207bb71b"
Debugger message: panic
Memory ID: 0xff
OS version: 16A366
Kernel version: Darwin Kernel Version 18.0.0: Tue Aug 14 22:07:18 PDT 2018; root:xnu-4903.202.2~1\/RELEASE_ARM64_T8020

You can download cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit targeting iOS 12.0 on iPhone Xs here.

Conclusions

It's rare that mitigations ship with documentation detailing exactly what cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir purpose is. Is it trying to make a certain exploitation technique less reliable? Is it trying to eradicate a vulnerability class? Is it trying to break an exploit chain?

PAC, as currently implemented, doesn't present much of a barrier for an attacker with local code execution and a memory corruption primitive looking to escalate privileges within userspace. This was also probably not cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attack model which PAC in iOS 12 was intended to defend against, but without any documentation from Apple we don't know for sure. It's important to emphasize that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 private data which most users want to protect is almost all, at some point, found in userspace.

It's also important to mention that this exploit was very contrived. Firstly, turning cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 virtual memory logic bug into memory corruption is probably cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 least interesting thing you could do with it. Finding ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r logical consequences caused by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 unexpected shared memory would be more interesting (maybe a double read of a string used as a selector?) but I just wanted a memory corruption primitive so I could experiment with PAC's resilience to memory corruption in a userspace context and I didn't have any ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r bugs.

Secondly, gaining PC control is probably also unnecessary. Again, this was done to demonstrate that it's still possible to chain arbitrary function calls togecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r quite easily even with PAC. Stealing resources such as file descriptors or mach ports from remote processes without direct PC control would also be quite possible.

It's hard to call something a PAC defeat without knowing what PAC is supposed to defend against, and it's hard to say that something like PAC "raises cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bar" without knowing whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r anyone really has to cross that bar anyway.

No comments:

Post a Comment