On February 8th 2019 Apple released the iOS 12.1.4 update that fixed a previously disclosed security vulnerability in Facetime group conferences that was heavily discussed in media the week before. However with the same update Apple fixed a number of other vulnerabilities as documented in the usual place. While it is not uncommon for Apple to fix multiple security problems with the same update a tweet from Google's project zero made the public aware that two of these vulnerabilitis were apparently found being exploited in the wild.
Since then more than two weeks have passed and neither Google nor Apple have given out any details about this incident, which leaves the rest of the world in the dark about what exactly happened, how Google was able to catch a chain of iOS 0-day vulnerabilities in the wild and where exactly the vulnerabilities are located. As usual Apple security notes contain only very brief descriptions of what was fixed. So it is no surprise that all they disclose about these vulnerabilities is the following.
Foundation
Available for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation
Impact: An application may be able to gain elevated privileges
Description: A memory corruption issue was addressed with improved input validation.
CVE-2019-7286: an anonymous researcher, Clement Lecigne of Google Threat Analysis Group, Ian Beer of Google Project Zero, and Samuel Groß of Google Project Zero
IOKit
Available for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation
Impact: An application may be able to execute arbitrary code with kernel privileges
Description: A memory corruption issue was addressed with improved input validation.
CVE-2019-7287: an anonymous researcher, Clement Lecigne of Google Threat Analysis Group, Ian Beer of Google Project Zero, and Samuel Groß of Google Project Zero
This information is very unsatisfying and therefore we decided to have a look into what was actually fixed. Because we usually concentrate on the iOS kernel we tried to figure out what vulnerability is hiding behind CVE-2019-7287 by binary diffing the iOS 12.1.3 and the iOS 12.1.4 kernels.
Analysing iOS security patches has become a lot easier since the last time an iOS malware has been caught in the wild. With the release of iOS 10 Apple started to ship the iOS kernel in the firmware in decrypted form to be more open. But then they recently decided with iOS 12 to back paddle on this openness by stripping all symbols from the shipped kernels. However due to a mistake they shipped a fully symbolized iOS 12 kernel during the development stages that was immediately uploaded to Hexray's Lumina service. Without symbols analysing patches becomes a bit more difficult however in this case the functions in question even have strings in them that point to the problem directly.
Once you have extracted the two kernels from the firmware they can analysed for differences. We have used the open source binary diffing plugin Diaphora for IDA to perform this task. For our comparison we loaded the iOS 12.1.3 kernel into IDA, then waited for the autoanalysis to finish and then used Diaphora to dump the current IDA database into the SQLITE database format Diaphora uses. We repeated this process with the iOS 12.1.4 kernel and then told Diaphora to diff the two databases using its slow heuristics overnight. The result of this comparison showed only a very small amount of partially changed functions. When looking at these functions we believe the vulnerability is likely in the function ProvInfoIOKitUserClient::ucGetEncryptedSeedSegment. The reason why we believe this is because Apple introduced a new size check in this function. Have a look at the previous version of the function.
And now have a look at the fixed version in iOS 12.1.4. You can clearly see the newly introduced size check in the area marked as red with a clear error message attached to it.
The IOKit objects ProvInfoIOKit and ProvInfoIOKitUserClient are implemented in a driver called com.apple.driver.ProvInfoIOKit. Connections to driver cannot be created from the normal container sandbox that iOS applications run in. This means there is likely a sandbox escape involved in the full iOS exploitation chain that Google found. Alternatively the exploit chain could exploit one of the daemons that have legitimate access to this driver. A check of the sandbox profiles as shipped with iOS 12 reveals that there are three daemon sandboxes that are allowed to access this driver. These daemon sandboxes are:
- findmydeviced
- mobileactivationd
- identityserviced
Which route to this driver was taken by the original attackers we can only guess until Apple or Google finally decide to reveal this information to the public. All this assuming that our guess is right and the newly introduced size check is actually the fix for CVE-2019-7287.
Having pinpointed the newly introduced size check in ProvInfoIOKitUserClient::ucGetEncryptedSeedSegment the next step is to find out how this function can actually be called from the outside. As it turns out this function is directly exposed to userland via the externalMethod interface of the driver. A check of ProvInfoIOKitUserClient::getTargetAndMethodForIndex reveals that the driver offers 6 different external methods to userland. These methods are:
- ucGenerateSeed (obfuscated name: fpXqy2dxjQo7)
- ucGenerateInFieldSeed (obfuscated name: afpHseTGo8s)
- ucExchangeWithHoover (obfuscated name: AEWpRs)
- ucGetEntcryptedSeedSegment
- ucEncryptSUInfo
- ucEncryptWithWrapperKey
The interesting thing here is that three first external methods have obfuscated names in the leaked symbols. However all six routines have very explicit strings in them that reveal their name. Checking into the other external methods we were in for a surprise.
When looking into ucEncryptSUInfo and ucEncryptWithWrapperKey we were surprised to see that both these functions have also been changed. Both have also gotten new size checks. And both these functions did not show up in our Diaphora output. At some point we may want to go back and try to figure out why Diaphora did not see these functions as changed (or maybe they changed too much so that the different functions were not matched). When you look at these functions and the introduced size checks you will also see that directly after the size check there are calls do memmove.
When you look at the calls to memmove it seems that before the size checks were introduced the code fully trusted user supplied size fields in the incoming parameter structure. This likely lead to arbitrary sized heap memory corruptions. We will take a look into this in the next days to verify this educated guess.
Our research and therefore this blog post is far from finished. We only wanted to get this information out as soon as possible in order to first verify that we have pinpointed the right location before we invest further resources into maybe chasing down the wrong bug. Please check back in a few days to see if we have updated this post.
If you are interested in this kind of content please consider signing up for one of our upcoming trainings.
Stefan Esser