After our analysis of CVE-2016-4656 from last week new details have surfaced.

Intro After our analysis of the PEGASUS iOS kernel vulnerability CVE-2016-4656 from last week that you can read in part one of this blog post there have been new discoveries about the kernel vulnerability in question: It has been discovered that OSUnserializeBinary() has already been patched once in May of this year by Apple to fix a reported UAF vulnerability. That patch was issued to close the vulnerability CVE-2016-1828 that had been reported by Brandon Azad to Apple on 11th January 2016. A detailed write up about this vulnerability is available here. It was discovered that the UAF trigger we described is in code that was not around before iOS 9 and OS X 10.11. Because PEGASUS is reportedly compatible with earlier iOS versions and Apple even patched earlier OS X versions it makes it unlikely that the UAF trigger we described is the one that the real PEGASUS threat is using. After being notified about both these details we have decided to write a little update to our analysis incorporating this information.

The old code Let us take a step back and look at the code of OSUnserializeBinary() before iOS 9.0 and OS X 10.11. 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 if ( dict ) { if ( sym ) { DEBG ( "%s = %s

" , sym -> getCStringNoCopy (), o -> getMetaClass () -> getClassName ()); if ( o != dict ) ok = dict -> setObject ( sym , o ); o -> release (); sym -> release (); sym = 0 ; } else { sym = OSDynamicCast ( OSSymbol , o ); ok = ( sym != 0 ); } } As you can see from the code above: before the days of iOS 9.0 and OS 10.11 dictionary keys had to be OSSymbol objects and the OSString code path had not been added, yet. This means that the UAF trigger we explained in our analysis would not work on this older version of the code. Furthermore if you looked at this code more carefully and compared it in more detail to our analysis you notice that in the older version of the code the call to setObject() had only two parameters instead of three. This is because the code above is before the security fix for CVE-2016-1828. Let us now look at the code above in more detail and find out what codepath can trigger a UAF condition: Trigger 1 The first way to trigger a UAF condition in this code is what is known as CVE-2016-1828: Line 398 will set key k1 of the dictionary to object o1 This will increase the reference counter of o1 and k1 by one (to 2) In line 399 object o1 is released which decreases the reference counter to 1 In line 400 object k1 (sym) is released which decreases the reference counter to 1 At this point everything is fine because there are still references hold to both objects in the dictionary When the next object o2 is deserialized and inserted into the dictionary, while reusing the same key k1 the setObject() method in line 398 will replace o1 with o2 in the dictionary. During the replacement the reference counter of o1 is decreased by one to 0. At his point o1 is freed from memory. If the deserializer is trying to create a reference to this object again it is a UAF. Trigger 2 The second way to trigger a UAF condition in this code is what is likely the real trigger used by PEGASUS (CVE-2016-4656): Line 398 will not call setObject() if the object o we are trying to insert is a reference to the dict itself When setObject() is not executed the reference counters of o and sym will never have been increased Line 399 will decrease the reference counter of o to some value greater or equal to 1 (because it was a reference to dict) Line 400 will decrease the reference counter of sym to most likely 0 (if the symbol was e.g. a strange random string) At this point the OSSymbol object sym is destroyed Any attempt to create a reference to the object sym after this point will be a UAF. As you can see the code before iOS 9 and OS X 10.11 already had two separate but very related UAF trigger codepaths in it. This means together with the third codepath described in part one of this blog post there have been THREE UAF trigger codepaths in only 20 lines of code.

Hack.Lu We got invited by the Hack.lu conference to present our findings about CVE-2016-4656 and how to exploit the different triggers in October 2016 in Luxembourg.