Thursday, January 9, 2020

Remote iPhone Exploitation Part 3: From Memory Corruption to JavaScript and Back -- Gaining Code Execution

Posted by Samuel Groß, Project Zero

This is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 third and last post in a series about a remote, interactionless iPhone exploit over iMessage. The first blog post introduced cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploited vulnerability, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 second blog post described a way to perform a heapspray, leaking cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 shared cache base address.

At this point, ASLR has been broken as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 shared cache’s base address is known and controlled data can be placed at a known address with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 heap spray. What remains is to exploit cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vulnerability one more time to gain code execution.

After a short introduction to some relevant ObjC internals, an exploit for devices without pointer aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication (PAC) will be outlined. It involves creating code pointers, so it no longer works with pointer aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication enabled. Afterwards, a different exploit that works against PAC and non-PAC devices will be presented. Finally, a technique to chain cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 presented attack with a kernel exploit, which involves implementing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel exploit in JavaScript, will be shown.

Objective-C for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Remote Code Executer


ObjC is a superset of C which has object oriented programming features added. ObjC adds, amongst ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rs, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 concepts of objects, classes with methods and properties, and inheritance to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 language. Most objects in ObjC ultimately inherit from NSObject, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 root object class. A simple snippet of ObjC code is shown next. It creates and initializes an instance of a Class, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n calls a method on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 instance.

    Bob* bob = [[Bob alloc] init];
    [bob doSomething];

ObjC relies heavily on reference counting for managing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 lifetime of objects. As such, every object has a refcount, eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r inline as part of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ISA word (see below) or out of line in a global table. Code that operates on an object cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n has to perform objc_retain and objc_release calls on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 object. These must eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r be placed manually by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 programmer or are inserted automatically by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 compiler if automatic reference counting (ARC) is enabled.

Internally, an object in ObjC is a chunk of memory (usually allocated through calloc) that always starts with an “ISA” value followed by instance variables/properties. The ISA value is a pointer-sized value containing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following information:
  • A pointer to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Class of which this object is an instance
  • An inline reference counter
  • A few additional flag bits

An ObjC Class describes its instances, but it is also an ObjC object itself. As such, Classes in ObjC are not only a compile time concept but also exist at runtime, enabling introspection and reflection. A Class contains cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following bits of information:
  • The ISA word (as it is an Objc Object itself), pointing to a dedicated metaclass
  • A pointer to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 superclass if any
  • A method cache to speed up method implementation lookups
  • The method table
  • The list of instance variables (name and offset) that each instance has

A Method in ObjC is basically a tuple:


where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Selector is a unique c-string containing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 name of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 method and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Implementation is a pointer to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 native function implementing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 method. 

With that, here is roughly what happens when cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following piece of ObjC code from above is compiled and executed: first, at compile time, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 three ObjC method calls are translated to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following pseudo-C code (assuming ARC is enabled):

Bob* bob = objc_msgSend(BobClass, "alloc");
// Refcount is already 1 at this point, so no need for a objc_retain()
bob = objc_msgSend(bob, "init");
objc_msgSend(bob, "doSomething");
...
objc_release(bob);

At runtime, objc_msgSend will cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n roughly do cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following:

  1. Dereference cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 object pointer to retrieve cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ISA value and extract cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Class pointer from it in turn
  2. Perform a lookup for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 requested method implementation in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Class’ method cache. This is done by essentially hashing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 selector’s address to obtain a table index, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n comparing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 entry’s selector with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 requested one
  3. If that fails, continue to look up cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 method in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 method table
  4. If a method Implementation was found, it is (tail-) called and passed any additional arguments that were passed to objc_msgSend. Ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rwise an exception is raised.

Note that since selectors are guaranteed to be unique by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ObjC runtime, comparison of two selectors is possible simply by comparing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer values. 

objc_release on 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 hand will decrement cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 object’s refcount (assuming no custom retain/release has been implemented for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 object by overwriting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 corresponding methods).
If decrementing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 object’s refcount sets it to zero, it invokes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 dealloc method (cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 destructor of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 object) and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n frees cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 object’s memory chunk.

Furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r information about ObjC internals can be found here, here, and in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 source/binary code.

Native Code Execution on non-PAC Devices


Given cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 above insights into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ObjC runtime internals and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 capabilities gained from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 second part of this series, it is now possible to implement a simple exploit to gain native code execution on devices without pointer aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication (PAC), namely cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 iPhone X and earlier.

Shown next is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code fragment from [NSSharedKeySet indexForKey:] which reads cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 candidate value from an address of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker’s choosing (as _keys is nullptr and index is controlled) and processes it. 

  1.       id candidate = self->_keys[index];
  2.       if (candidate != nil) {
  3.         if ([key isEqual:candidate]) {
  4.           return prevLength + index;
  5.         }
  6.       }

If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 key is a NSString, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first things that [NSString isEqual:] will do with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 given argument is calling [arg isNSString__] on it (see cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 implementation of [NSString isEqual:] in Foundation.framework). As such, native code execution can be achieved in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following way:

  1. Perform cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 heap spray as described in part 2. The heap spray should contain a fake object pointing to a fake class containing a fake method cache with an entry for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 method ‘isNSString__’ with a controlled IMP pointer. The heap spray should also contain a pointer to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fake object
  2. Trigger cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vulnerability to read that pointer and have it be passed to [key isEqual:]. This will in turn invoke ‘isNSString__’ on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 faked object, thus pointing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 instruction pointer to an address of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker’s choosing
  3. Perform a stack pivot and implement a payload in ROP

At this point, an attacker has succeeded in exploiting a device that does not support PAC (iPhone X and earlier). However, with PAC enabled, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 above attack is no longer possible as is explained in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 next section.

The Impact of PAC in Userspace


Brandon Azad has previously done some great research detailing how PAC works in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel. PAC in userspace is not too different. Next is a high level summary of how PAC works in userspace. For more information, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 reader is referred to llvm’s documentation of PAC.

Every code pointer (function pointer, ObjC method pointer, return address, …) is signed with a secret key as well as an optional 64-bit “context” value. The resulting signature is truncated to 24 bits which are cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n stored in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 upper part of a pointer, which are normally unused. Before jumping to a code pointer, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer’s signature is verified by recomputing it with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same key and context value and comparing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 result to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bits stored in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer. If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y match, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 signature bits are cleared, leaving a valid pointer to be dereferenced. If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y don’t match, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer becomes clobbered, thus leading to an access violation when it is subsequently dereferenced.

The main purpose of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 context value is to prevent pointer swapping attacks in which a signed pointer is copied from one location in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process to anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r. As an example, ObjC method Implementation pointers stored in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 method cache use 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 cache entry as context while return addresses on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack use 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ý bet365mselves in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stackframe as context. As such, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 two cannot be swapped.

To summarize, with PAC enabled and without a bug in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 implementation of PAC itself or a signing gadget (a piece of code that can legitimately be invoked and which will add a PAC signature to an arbitrary pointer and return cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 result to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker), it becomes impossible to fake code pointers like cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previously described exploit does. The bypass described next assumes such a “flawless” implementation.

One attack that is still viable despite PAC is to create fake instances of ObjC classes and call existing (legitimate) methods on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m. This is possible because PAC does not currently protect cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ISA value which identifies a class instance (this is likely due to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fact that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ISA value does not have enough spare bits for a signature). As such, knowing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 base address of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 dyld shared cache (which contains all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ObjC Class objects), it becomes possible to fake instances of any class in any library currently loaded in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process.

However, on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code path that is currently taken ([NSSharedKeySet indexForKey:], only a small number of selectors are ever sent to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 controlled object, for example ‘__isNSString’. This is likely not very useful as none of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 available implementations perform anything particularly interesting. Anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r code path yielding a different primitive thus has to be found.

A Useful Exploit Primitive


As described above, ObjC relies heavily on reference counting, and one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 most common operations performed on an object (besides objc_msgSend) is objc_release. Due to that, many memory corruption vulnerabilities can be turned into an objc_release call on a controlled object. This is also cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 case here, although some more effort is required as, by itself, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code used during [NSSharedKeySet indexForKey:] will not call objc_release on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker controlled object. As such, a new object graph is required once again to reach a different piece of code, in this case in [NSSharedKeyDictionary setObject:ForKey:], called during [NSSharedKeyDictionary initWithCoder:] as shown next.


  1. -[NSSharedKeyDictionary initWithCoder:coder] {
  2.     self->_keyMap = [coder decodeObjectOfClass:[NSSharedKeySet class] 
  3.                            forKey:@”Ns.skkeyset”];
  4.     self->_values = calloc([self->_keyMap count], 8);
  5.     NSArray* keys = [coder decodeObjectOfClasses:[...] 
  6.                            forKey:@”NS.keys”]];
  7.     NSArray* values = [coder decodeObjectOfClasses:[...] 
  8.                              forKey:@”NS.values”]];
  9.    
  10.     if ([keys count] != [values count]) { // return error }

  11.     for (int i = 0; i < [keys count]; i++) {
  12.         [self setObject:[values objectAtIndex:i] 
  13.               forKey:[keys objectAtIndex:i]];
  14.     }
  15. }

  1. -[NSSharedKeyDictionary setObject:obj forKey:key] {
  2.      uint32_t index = [self->_keyMap indexForKey:key];
  3.     if (index != -1) { 
  4.         id oldval = self->_values[index];
  5.         objc_retain(obj);
  6.         self->_values[index] = obj;
  7.         objc_release(oldval);
  8.     } 
  9. }

Note that in [NSSharedKeyDictionary initWithCoder:], cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is no check for nullptr after cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 call to calloc() in line 4. This is likely not a problem under normal circumstances as anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r allocation of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same size would have to succeed first (for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SharedKeySet) and its content (multiple gigabytes of data) would have to be sent over iMessage. However, it does become a problem in combination with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current vulnerability, as now a _numKey value of one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SharedKeySets is not yet validated at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 time this code runs, thus allowing an attacker to cause calloc() to fail without requiring ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r huge allocations to succeed first. Afterwards, [NSSharedKeyDictionary setObject:forKey:] can be tricked to again read an ObjC id from an attacker controlled address (line 4) and subsequently call objc_release on it (line 7). The object graph shown next triggers this case and performs an objc_release call on an attacker controlled value. The return value of calloc() has since been checked against nullptr, in which case unarchiving is aborted.



When cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 shown object graph is unarchived, SharedKeyDictionary2 will attempt to store cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value for “k2” in itself and will thus call [SharedKeySet2 indexForKey:"k2"]. This in turn will recurse to SharedKeySet1, which will also not find cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 key (as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 index fetched from its _rankTable is larger than _numKey) and recurse furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r. Finally, SharedKeySet3 will be able to lookup “k2” and return cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 currently accumulated offset, which is (1 + 0x41414140/8). SharedKeyDictionary2 will cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n access 0x41414148, call objc_release on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re and finally write cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 NSString “foobar” to that address. This now provides cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 arbitrary objc_release primitive required for furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r exploitation.

With that, it is now possible to get any legitimate ‘dealloc’ or ‘.cxx_desctruct’ (which are additional destructors automatically generated by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 compiler) method called. There are about 50,000 such functions in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 shared cache. Quite a lot of gadgets! To find interesting gadgets, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following IDAPython snippet can be used on a loaded dyld_shared_cache image. It enumerates cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 available destructors and writes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir name + decompiled code to disk:

for funcea in Functions():
    funcName = GetFunctionName(funcea)
    if ‘dealloc]’ in funcName or ‘.cxx_desctruct]’ in funcName:
        func = get_func(funcea)
        outfile.write(str(decompile(func)) + ‘\n’)

One approach to finding interesting dealloc “gadgets” is to grep cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 decompiled code for specific selectors that are being sent to ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r objects, furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r increasing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 amount of code that can be executed. One dealloc implementation that is particularly interesting is printed below:

-[MPMediaPickerController dealloc](MPMediaPickerController *self, SEL)
{
  v3 = objc_msgSend(self->someField, "invoke");
  objc_unsafeClaimAutoreleasedReturnValue(v3);
  objc_msgSend(self->someOcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rField, "setMediaPickerController:", 0);
  objc_msgSendSuper2(self, "dealloc");
}

This sends cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “invoke” selector to an object read from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 faked self object. “invoke” is for example implemented by NSInvocation objects, which are essentially bound functions: a triplet of (target object, selector, arguments) which, when sent cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “invoke” selector, will call cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stored selector on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 target object with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stored arguments. It is thus possible to call any ObjC method on a completely controlled object with arbitrary arguments. Quite a powerful primitive. In fact, already powerful enough to pop calc. The call:

    [UIApplication launchApplicationWithIdentifier:@"com.apple.calculator" suspended:NO]

...will do cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 job just fine :)

The final heap spray layout cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n looks roughly as depicted below. The heap spray must now be a bit bigger because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previously used address 0x110000000, which will now also be used as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size argument to calloc, will not cause calloc to fail on newer devices, so a higher address such as 0x140000000 is necessary. Note also that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 call to ‘setMediaPickerController’ in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 dealloc implementation can simply be survived by setting someOcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rField to zero, in which case objc_msgSend generally becomes a nop. NSInvocation objects are made up of a “frame”, a pointer to a buffer containing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 arguments for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 call, as well as storage for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 return value and a reference to a NSMethodSignature object containing information about cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 number and type of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 arguments. The latter two fields are omitted in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 diagram for brieviety.



To trigger cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 controlled ObjC method invocation from [NSSharedKeyDictionary setObject:obj forKey:key], cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address 0x140003ff8 must be accessed so that a pointer to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fake MPMediaPickerController is passed to objc_release.

The video below shows cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit in action. Note that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is no “respring” or similar crash behaviour happening as SpringBoard continues normally after executing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 payload.


While opening a calculator is nice, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ultimate goal of an exploit like cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 one presented here is likely to stage a kernel exploit. One option for that is to use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current capability to open a WebView and serve a classic browser exploit. However, that would likely require a separate browser vulnerability. The final question is thus whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r it is possible to directly chain a kernel exploit given cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current primitive. The remainder of this blog post will now explore this option.

SeLector Oriented Programming (SLOP)


The technique described here was not part of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 initial exploit PoC that was sent to Apple. As such, it was separately reported on September 13 as an exploitation technique (noting that exploitation techniques are not subject to Project Zero's 90 day deadlines)

Expanding on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current exploitation capability, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ability to call a single method on a controlled object with controlled arguments, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 next step is to gain cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ability to chain multiple method calls togecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r. Furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rmore, it should also be possible to use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 return values of method calls, for example as arguments to subsequent ones, and to be able to read and write process memory. The following technique, dubbed SLOP for SeLector Oriented Programming ;), allows this.

First, it is possible to chain multiple selector calls togecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r by creating a fake NSArray containing fake NSInvocation objects, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n calling [NSArray makeObjectsPerformSelector:@selector(invoke)] on it (through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “bootstrap” NSInvocation that is invoked during [MPMediaPickerController dealloc]). This will invoke each of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 NSInvocations, thus performing multiple independent ObjC method calls.

Next, by using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 [NSInvocation getReturnValue:ptr] method, it is possible to write cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 return value of a previous method call somewhere in memory, for example into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 arguments buffer of a following call. With that, it is possible to use return values of method calls as arguments for subsequent ones.

A simple SLOP chain cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n looks like this:



This chain is equivalent to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following ObjC code:

id result = [target1 doX];
[target2 doY:result];

For cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 final primitive, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ability to read and write process memory, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 [NSInvocation getArgument:atIndex] method can be used, of which simplified pseudocode is shown next:

void -[NSInvocation getArgument:(void*)addr atIndex:(uint)idx] {
  if (idx >= [self->_signature numberOfArguments]) {
    ...; // abort with exception
  }

  memcpy(addr, &self->_frame[idx], 8);
}

As cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 above method can be invoked using SLOP, it is possible to perform arbitrary memory reads and writes (with ’setArgument:atIndex’ instead) by creating fake NSInvocation objects where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 _frame member points to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 desired address. Conveniently, this also allows reading and writing at an offset from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer which facilitates accessing or corrupting members of some data structure. This will be an important primitive for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 next part.

With SLOP, it is now possible to execute arbitrary ObjC methods. As this happens outside of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sandbox in SpringBoard, it is already sufficient to for example gain access to user data. However, by itself, SLOP is likely not powerful enough to execute a kernel exploit, which is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 final step for a full device compromise. This is for example due to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 lack of (obvious) control-flow primitives that SLOP provides.

Chaining a Kernel Exploit


One approach for executing a kernel exploit is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n to use SLOP to pivot into a scripting context, for example into JavaScriptCore.framework, and implement cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel exploit in that scripting language. Pivoting into JavaScript from SLOP is easily possible using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following method calls:

   JSContext* ctx = [[JSContext alloc] init];
   [ctx evaluateScript:scriptContent];

In order to execute a kernel exploit in JavaScript, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following primitives will have to be bridged into JavaScript or recreated cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re:

  1. The ability to read and write cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current process
  2. The ability to call C functions and in particular syscalls

Gaining memory read/write in JavaScript is racá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r easy with a standard browser exploitation technique: corrupting JavaScript DataViews/TypedArrays. In particular, two DataView objects can be created in JS, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n returned to ObjC, where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first one is corrupted so that its backing memory pointer now points to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 second one. The resulting situation is shown in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 image below. Corrupting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 backing storage pointer is possible by using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory read/write primitive in SLOP as described above.



Subsequently, it is now possible to read from/write to arbitrary addresses from JavaScript by first corrupting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 backing storage pointer of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 second DataView through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first one, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n accessing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 second DataView’s storage. Conveniently, Gigacage, a mitigation in JavaScriptCore designed to hinder cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 abuse of ArrayBuffers and similar in such a way, is not active in JSC builds for Cocoa. This solves point 1 above.

Afterwards, point 2 can be achieved with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following two ObjC methods:
  • [CNFileServices dlsym::], which is just a thin wrapper around dlsym(3). With PAC enabled, dlsym will sign returned function pointers with context zero (as no sensible context value is available to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 caller) before returning cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m
  • [NSInvocation invokeUsingIMP:], which accepts an IMP, a function pointer signed with context zero, as argument and calls it with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 arguments from its frame buffer, which are thus fully controlled

Combining cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se two ObjC methods allows calling of arbitrary exported C functions with arbitrary arguments. Finally, it is also possible to bridge this capability into JavaScript: JavaScript core supports bridging ObjC objects by implementing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 JSExport protocol and specifying cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 methods that should be exposed to JS. ObjC methods bridged in that way are implemented by creating one NSInvocation object for every method and invoking it when cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 method is called in JS. Given cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory read/write capability, it is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n possible to corrupt this NSInvocation object to call a different method on a different object, for example [CNFileServices dlsym::] or [NSInvocation invokeUsingIMP:].

With all of that and a bit of wrapper code to expose cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 required functionality in a convenient way, a reimplementation of Ned Williamson’s trigger for CVE-2019-8605 aka SockPuppet looks like this (see crash_kernel.js for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 full code):

    let sonpx = memory.alloc(8);
    memory.write8(sonpx, new Int64("0x0000000100000001"));
    let minmtu = memory.alloc(8);
    memory.write8(minmtu, new Int64("0xffffffffffffffff"));

    let n0 = new Int64(0);
    let n4 = new Int64(4);
    let n8 = new Int64(8);

    while (true) {
        let s = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
        setsockopt(s, SOL_SOCKET, SO_NP_EXTENSIONS, sonpx, n8);
        setsockopt(s, IPPROTO_IPV6, IPV6_USE_MIN_MTU, minmtu, n4);
        disconnectx(s, n0, n0);
        // The next setsockopt will write into a freed buffer, so
        // give cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel some time to reclaim it first.
        usleep(1000);
        setsockopt(s, IPPROTO_IPV6, IPV6_USE_MIN_MTU, minmtu, n4);
        close(s);
    }

This demonstrates that it is possible to execute a kernel exploit from JavaScript after a successful remote exploit over iMessage and without any furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r vulnerabilities (e.g. a JavaScriptCore RCE exploit). 

The source code for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 proof-of-concept exploit can be found here.

Improving Messenger Security


The insights gained during cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploitation work led to numerous recommendations for hardening measures, most of which have already been mentioned throughout this series where appropriate and have also been communicated to Apple. As cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y could also be relevant for ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r messenger and mobile operating system vendors, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y are restated next.

  1. As much code as possible should be put behind user interaction, in particular when receiving messages from unknown senders.
  2. Communication side channels in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 interactionless attack surface such as automatic delivery receipts should be removed. If that is not possible, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 receipts should at least be sent in a way that prevents cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m from being abused to construct a crash oracle, for example by sending cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 server side or from a dedicated process
  3. Processes handling incoming data should be sandboxed as much as possible and should be disallowed any network interaction to stop an attacker from constructing a communication channel (and afterwards an info leak) through a memory corruption or logic bug. This would also prevent bugs such as CVE-2019-8646 from being easily exploitable.
  4. ASLR should be as strong as possible. In particular, both heap spraying and brute force guessing should be impractical on any remote attack surface. Similarly, on iOS, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ASLR of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 dyld shared cache region could be improved.
  5. Some probabilistic defenses (ASLR, and in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 future memory tagging) can be defeated if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker gets enough attempts (as is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 case here with imagent). As such, it seems desirable to limit cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 number of attempts an attacker gets whenever possible and to deploy monitoring that is able to detect failed exploitation attempts.

Since developing and reporting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 presented exploit to Apple on August 9, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following security relevant changes to iOS have taken place:

  1. A large part of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 NSKeyedUnarchiver attack surface in iMessage was blocked (by disallowing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 decoding of child classes), thus rendering this bug and ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rs unexploitable over iMessage
  2. The vulnerability exploited here was fixed and assigned CVE-2019-8641
  3. The decoding of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “BP” NSKeyedUnarchiver payload contained in incoming iMessages was moved into a sandboxed process (IMDPersistenceAgent). As such, an attacker exploiting an NSKeyedUnarchiver vulnerability over iMessage will now also need to break out of a sandbox.
  4. A new unsigned int field, _magic, was added to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 NSInvocation class. This field is set to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value of a per-process random global variable during initialization and asserted to be equal to it again during [NSInvocation invoke]. As such, it is no longer possible to create fake NSInvocation instances (which is a requirement for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 presented PAC bypass) without leaking this value beforehand. Furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r, it appears that efforts are being made to protect cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 selector and target fields of NSInvocation with PAC in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 future, additionally hindering abuse of NSInvocations even in case of an attacker with memory read/write capabilities.
  5. The [MPMediaPickerController dealloc] method, which was used to bootstrap cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC bypass by sending cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “invoke” selector to an attacker-controlled object, appears to have been removed. However, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are still ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r dealloc implementations that call “invoke” on an attacker controlled object left. These might be removed in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 future as well. As described above, it is in any case now more difficult to create fake NSInvocation instances. 

This list could be incomplete as it was compiled through reverse engineering and manual experimentation, as Apple do not currently share any details of how cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir issues were fixed with security researchers.

On a final note, while good sandboxing is critical, it should not be relied upon blindly. As an example to consider, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vulnerability exploited here would likely also be usable to escape any sandbox on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 device as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 targeted API is commonly exposed over IPC as well.

Conclusion


This blog post series demonstrated that, despite numerous exploit mitigations being deployed, it is still possible to exploit memory corruption vulnerabilities in a non-interactive setting such as mobile messaging services and without an additional remote infoleak vulnerability as is commonly deemed necessary. The exploited vulnerability has been fixed by Apple and a few furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r hardening measures have already been deployed. A key insight of this research was that ASLR, which in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ory should offer strong protection in this attack scenario, is not as strong in practice. In particular, ASLR can be defeated through heap spraying and through crash oracles, which can be constructed from commonly available side channels such as automatic delivery receipts. A second insight was that fairly arbitrary code, powerful enough to execute a kernel exploit, could be executed without ever creating or corrupting a code pointer, thus effectively bypassing PAC. Finally, based on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 developed exploit, numerous suggestions for attack-surface reduction, hardening, and exploit mitigations were presented. My hope is that this research will ultimately help all vendors by highlighting how small design decisions can have significant security consequences and to hopefully better protect cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir users from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se kinds of attacks.