tag:blogger.com,1999:blog-20297004265059539712024-03-19T10:26:41.490+02:00Bits, Please!laginimainebhttp://www.blogger.com/profile/00867710965331025912noreply@blogger.comBlogger14125tag:blogger.com,1999:blog-2029700426505953971.post-49238957349313279472016-06-30T15:00:00.001+03:002016-07-02T12:26:47.745+03:00Extracting Qualcomm's KeyMaster Keys - Breaking Android Full Disk EncryptionAfter covering a TrustZone kernel vulnerability and exploit in the <a href="http://bits-please.blogspot.co.il/2016/05/qsee-privilege-escalation-vulnerability.html">previous blog post</a>, I thought this time it might be interesting to explore some of the implications of code-execution within the TrustZone kernel. In this blog post, I'll demonstrate how TrustZone kernel code-execution can be used to effectively break Android's <b>F</b>ull <b>D</b>isk <b>E</b>ncryption (<b>FDE</b>) scheme. We'll also see some of the inherent issues stemming from the design of Android's FDE scheme, even without any TrustZone vulnerability.<br />
<br />
I've been in contact with Qualcomm regarding the issue prior to the release of this post, and have let them review the blog post. As always, they've been very helpful and fast to respond. Unfortunately, it seems as though fixing the issue is not simple, and might require hardware changes. <br />
<br />
If you aren't interested in the technical details and just want to read the conclusions - feel free to jump right to the "<b>Conclusions</b>" section. In the same vein, if you're only interested in the code, jump directly to the "<b>Code</b>" section.<br />
<br />
<i>[UPDATE: I've made a factual mistake in the original blog post, and have corrected it in the post below. Apparently Qualcomm are <u>not</u> able to sign firmware images, only OEMs can do so. As such, they cannot be coerced to create a custom TrustZone image. I apologise for the mistake.]</i><br />
<br />
And now without further ado, let's get to it!<br />
<br />
<h2>
Setting the Stage </h2>
<br />
A couple of months ago the highly-publicised case of <a href="http://www.cnbc.com/2016/03/29/apple-vs-fbi-all-you-need-to-know.html">Apple vs. FBI</a> brought attention to the topic of privacy - especially in the context of mobile devices. Following the <a href="https://en.wikipedia.org/wiki/2015_San_Bernardino_attack">2015 San Bernardino terrorist attack</a>, the FBI seized a mobile phone belonging to the shooter, Syed Farook, with the intent to search it for any additional evidence or leads related to the ongoing investigation. However, despite being in possession of the device, the FBI were unable to unlock the phone and access its contents. <br />
<br />
This may sound puzzling at first. "Surely if the FBI has access to the phone, could they not extract the user data stored on it using forensic tools?". Well, the answer is not that simple. You see, the device in question was an iPhone 5c, running iOS 9.<br />
<br />
As you may well know, starting with iOS 8, Apple has automatically enabled <b>F</b>ull <b>D</b>isk <b>E</b>ncryption (<b>FDE</b>) using an encryption key which is <u>derived from the user's password</u>. In order to access the data on the device, the FBI would have to crack that encryption. Barring any errors in cryptographic design, this would most probably be achieved by cracking the user's password.<br />
<br />
"So why not just brute-force the password?". That sounds like a completely valid approach - especially since most users are notoriously bad at choosing strong passwords, even more so when it comes to mobile devices.<br />
<br />
However, the engineers at Apple were not oblivious to this concern when designing their FDE scheme. In order to try and mitigate this kind of attack, they've designed the encryption scheme so that the generated encryption key is <u>bound to the hardware of the device.</u> <br />
<br />
In short, each device has an immutable 256-bit unique key called the UID, which is randomly generated and fused into the device's hardware at the factory. The key is stored in a way which completely prevents access to it using software or firmware (it can only be set as a key for the AES Engine), meaning that <u>even Apple cannot extract it from the device</u> once it's been set. This device-specific key is then used in combination with the provided user's password in order to generate the resulting encryption key used to protect the data on the device. This effectively 'tangles' the password and the UID key.<br />
<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSwX9RRQEBrc7o-VbaIxUwT8WRMfghfuDXENjNaOFTxlrSVlcUoUTDIl9ahyvp0O6SwS5p2f539Qp8kvzoRYXB3bKQHG8gbucCPmVVpCZLSz9hlS8XgVe_RYk0Uh1wD3L6pIwJz-Pa6dv8/s1600/Screenshot+from+2016-06-18+01%253A29%253A24.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSwX9RRQEBrc7o-VbaIxUwT8WRMfghfuDXENjNaOFTxlrSVlcUoUTDIl9ahyvp0O6SwS5p2f539Qp8kvzoRYXB3bKQHG8gbucCPmVVpCZLSz9hlS8XgVe_RYk0Uh1wD3L6pIwJz-Pa6dv8/s1600/Screenshot+from+2016-06-18+01%253A29%253A24.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Apple's FDE KDF</td></tr>
</tbody></table>
Binding the encryption key to the device's hardware allows Apple to make the job <u>much harder</u> for would-be attackers. It essentially forces attackers to use the device for each cracking attempt. This, in turn, allows Apple to introduce a whole array of defences that would make cracking attempts on the device unattractive.<br />
<br />
For starters, the key-derivation function shown above is engineered in such a way so that it would take a substantial amount of time to compute on the device. Specifically, Apple chose the function's parameters so that a single key derivation would take approximately 80 milliseconds. This delay would make cracking short alphanumeric passwords slow (~2 weeks for a 4-character alphanumeric password), and cracking longer passwords completely infeasible.<br />
<br />
In order to further mitigate brute-force attacks on the device itself, Apple has also introduced an incrementally increasingly delay between subsequent password guesses. On the iPhone 5c, this delay was facilitated completely using software. Lastly, Apple has allowed for an option to completely erase all of the
information stored on the device after 10 failed password attempts. This
configuration, coupled with the software-induced delays, made cracking
the password on the device itself rather infeasible as well.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNPVh1uUS891Q-LfvcdAscDbdyfJeXllUXgSxyDTQObASG_1eXhIAYC392bDlSM3MEEVD-sWalB02bZlhkF9G6RP9gfWKlBGa4uHYNKY2u8Pv32ZKUj9lMjyHauDOA1BmbL0JagNgVIWG-/s1600/Screenshot+from+2016-06-18+02%253A05%253A42.png" imageanchor="1"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNPVh1uUS891Q-LfvcdAscDbdyfJeXllUXgSxyDTQObASG_1eXhIAYC392bDlSM3MEEVD-sWalB02bZlhkF9G6RP9gfWKlBGa4uHYNKY2u8Pv32ZKUj9lMjyHauDOA1BmbL0JagNgVIWG-/s1600/Screenshot+from+2016-06-18+02%253A05%253A42.png" /></a></div>
<br />
With this in mind, it's a lot more reasonable that the FBI were unable to crack the device's encryption.<br />
<br />
Had they been able to extract the UID key, they could have used as much (specialized) hardware as needed in order to rapidly guess many passwords, which would most probably allow them to eventually guess the correct password. However, seeing as the UID key cannot be extracted by means of software or firmware, that option is ruled out.<br />
<br />
As for cracking the password on the device, the software-induced delays between password attempts and the possibility of obliterating all the data on the device made that option rather unattractive. That is, unless they could bypass the software protections... However, this is where the story gets rather irrelevant to this blog post, so we'll keep it at that.<br />
<br />
If you'd like to read more, you can check out <a href="https://blog.trailofbits.com/2016/02/17/apple-can-comply-with-the-fbi-court-order/">Dan Guido's superb post about the technical aspects of Apple v. FBI</a>, or <a href="http://blog.cryptographyengineering.com/2014/10/why-cant-apple-decrypt-your-iphone.html">Matthew Green's great overview on Apple's FDE</a>, or better yet, the <a href="http://www.apple.com/business/docs/iOS_Security_Guide.pdf">iOS Security Guide</a>.<br />
<br />
Going back to the issue at hand - we can see that Apple has cleverly designed their FDE scheme in order to make it very difficult to crack. Android, being the mature operating system that it is, was not one to lag behind. In fact, Android has also offered full disk encryption, which has been enabled by default since Android 5.0.<br />
<br />
So how does Android's FDE scheme fare? Let's find out.<br />
<br />
<h2>
Android Full Disk Encryption</h2>
<br />
Starting with Android 5.0, Android devices automatically protect all of the user's information by enabling full disk encryption.<br />
<br />
Android FDE is based on a Linux Kernel subsystem called <a href="https://en.wikipedia.org/wiki/Dm-crypt">dm-crypt</a>, which is widely deployed and researched. Off the bat, this is already good news - <i>dm-crypt</i> has withstood the test of time, and as such seems like a great candidate for an FDE implementation. However, while the encryption scheme may be robust, the system is only as strong as the key being used to encrypt the information. Additionally, mobile devices tend to cause users to choose poorer passwords in general. This means the key derivation function is hugely important in this setting.<br />
<br />
So how is the encryption key generated?<br />
<br />
This process is described in great detail in the <a href="https://source.android.com/security/encryption/">official documentation of Android FDE</a>, and in even greater detail in <a href="https://nelenkov.blogspot.co.il/2014/10/revisiting-android-disk-encryption.html">Nikolay Elenkov's blog, "Android Explorations"</a>. In short, the device generates a randomly-chosen 128-bit master key (which we'll refer to as the Device Encryption Key - DEK) and a 128-bit randomly-chosen salt. The DEK is then protected using an elaborate key derivation scheme, which uses the user's provided unlock credentials (PIN/Password/Pattern) in order to derive a key which will ultimately encrypt the DEK. The encrypted DEK is then stored on the device, inside a special <i>unencrypted</i> structure called the "<i>crypto footer</i>".<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBPKYqfboFv-E6715vvFT1njHbNZK7ncRasnQRmk-bwDo68DX5dg34VD84ZA9QtlW3ot16_HfIQPf72sTIeNOz5xlrrR0NuFUlPKjyT8u-lI6DtMgoMyCgExc8uRgSbxhWlrtp_a66ZaTx/s1600/Screenshot+from+2016-06-18+19%253A33%253A10.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBPKYqfboFv-E6715vvFT1njHbNZK7ncRasnQRmk-bwDo68DX5dg34VD84ZA9QtlW3ot16_HfIQPf72sTIeNOz5xlrrR0NuFUlPKjyT8u-lI6DtMgoMyCgExc8uRgSbxhWlrtp_a66ZaTx/s1600/Screenshot+from+2016-06-18+19%253A33%253A10.png" /></a></div>
<br />
The encrypted disk can then be decrypted by simply taking the user's provided credentials, passing them through the key derivation function, and using the resulting key to decrypt the stored DEK. Once the DEK is decrypted, it can be used to decrypt user's information.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-0knA7Ik5orGVJ3TWyqw35ccRBan9fhtNTevdpL4m4-vUhP2fQ-zUa6Fw09e9r7GdPpA-bTTz5nT5cSq7tsz-xNRzGrSiMchaiO_1VMerDFgXoIshJo2Q53mVRnH11k10xaXPJWZx5AkE/s1600/Screenshot+from+2016-06-18+19%253A46%253A00.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-0knA7Ik5orGVJ3TWyqw35ccRBan9fhtNTevdpL4m4-vUhP2fQ-zUa6Fw09e9r7GdPpA-bTTz5nT5cSq7tsz-xNRzGrSiMchaiO_1VMerDFgXoIshJo2Q53mVRnH11k10xaXPJWZx5AkE/s1600/Screenshot+from+2016-06-18+19%253A46%253A00.png" /></a></div>
<br />
<br />
However, this is where it gets interesting! Just like Apple's FDE scheme, Android FDE seeks to prevent brute-force cracking attacks; both on the device and especially off of it.<br />
<br />
Naturally, in order to prevent on-device cracking attacks, Android introduced delays between decryption attempts and an option to wipe the user's information after a few subsequent failed decryption attempts (just like iOS). But what about preventing off-device brute-force attacks? Well, this is achieved by introducing a step in the key derivation scheme which <u>binds the key to the device's hardware</u>. This binding is performed using Android's <a href="https://source.android.com/security/keystore/">Hardware-Backed Keystore</a> - KeyMaster.<br />
<br />
<h2>
KeyMaster</h2>
<br />
The KeyMaster module is intended to assure the protection of cryptographic keys generated by applications. In order to guarantee that this protection cannot be tampered with, the KeyMaster module runs in a <b>T</b>rusted <b>E</b>xecution <b>E</b>nvironment (<b>TEE</b>), which is completely separate from the Android operating system. In keeping with the TrustZone terminology, we'll refer to the Android operating system as the "Non-Secure World", and to the TEE as the "Secure World".<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbgdbttUqZv-_T-Uyh0lP52zCZqsyh8XBnuWlfE6JU-fSLfBR9tWVH_aUF254w1pD0xWj8nBlpapwVUSBYCSHk0dUeCSmQwgYnqqRD4wf2dedRRex0efznyfOphKfEQHNNOLhTxkp7GUA_/s1600/access-to-keymaster.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbgdbttUqZv-_T-Uyh0lP52zCZqsyh8XBnuWlfE6JU-fSLfBR9tWVH_aUF254w1pD0xWj8nBlpapwVUSBYCSHk0dUeCSmQwgYnqqRD4wf2dedRRex0efznyfOphKfEQHNNOLhTxkp7GUA_/s1600/access-to-keymaster.png" /></a></div>
<br />
Put simply, the KeyMaster module can be used to generate encryption keys, and to perform cryptographic operations on them, without ever revealing the keys to the <i>Non-Secure World</i>.<br />
<br />
Once the keys are generated in the KeyMaster module, they are encrypted using a hardware-backed encryption key, and returned to <i>Non-Secure World</i>. Whenever the <i>Non-Secure World</i> wishes to perform an operation using the generated keys, it must supply the encrypted "<i>key blob</i>" to the KeyMaster module. The KeyMaster module can then decrypt the stored key, use it to perform the wanted cryptographic operation, and finally return the result to the <i>Non-Secure World</i>.<br />
<br />
Since this is all done without ever revealing the cryptographic keys used to protect the key blobs to the <i>Non-Secure World</i>, this means that all cryptographic operations performed using <i>key blobs</i> must be handled by the KeyMaster module, directly on the device itself. <br />
<br />
With this in mind, let's see exactly how KeyMaster is used in Android's FDE scheme. We'll do so by taking a closer look at the hardware-bound key derivation function used in Android's FDE scheme. Here's a short schematic detailing the KDF (based on a similar schematic created by Nikolay Elenkov):<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfEjF90nTQszIRIxyfeB1_Mngb5gMB0cU5EQlp0u1JudqYXUcpRJMnQXxMUasIpf1eBBGPWYv1YPfNBkXanC1BD7RNEbxLIwRLh7t4e42pOb7KP33FNm9SH_0_0V_tm86iCPiUIQJFZTrF/s1600/Screenshot+from+2016-06-29+18%253A11%253A55.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfEjF90nTQszIRIxyfeB1_Mngb5gMB0cU5EQlp0u1JudqYXUcpRJMnQXxMUasIpf1eBBGPWYv1YPfNBkXanC1BD7RNEbxLIwRLh7t4e42pOb7KP33FNm9SH_0_0V_tm86iCPiUIQJFZTrF/s1600/Screenshot+from+2016-06-29+18%253A11%253A55.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Android FDE's KDF</td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZJZhuXjGSdB8HF7TWWenqUEVvlU1OYg_pN_xGw73y0zK3pdw048ARN09ufjsnlWBrlhdJiV7htlxmezyr18yDNgnXd3gGvfazS3MEP6R-PuFYIdkLP9foI_gSF9CF1x7HLqybzjkP1f5J/s1600/Screenshot+from+2016-06-29+18%253A06%253A22.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
As you can see, in order to bind the KDF to the hardware of the device, an additional field is stored in the <i>crypto footer </i>- a KeyMaster-generated <i>key blob</i>. This key blob contains a KeyMaster-encrypted RSA-2048 private key, which is used to sign the encryption key in an intermediate step in the KDF - thus requiring the use of the KeyMaster module in order to produce the intermediate key used decrypt the DEK in each decryption attempt.<br />
<br />
Moreover, the <i>crypto footer</i> also contains an additional field that doesn't serve any direct purpose in the decryption process; the value returned from running <i>scrypt</i> on the final intermediate key (IK3). This value is referred to as the "scrypted_intermediate_key" (<i>Scrypted IK</i> in the diagram above). It is used to verify the validity of the supplied FDE password in case of errors during the decryption process. This is important since it allows Android to know when a given encryption key is valid but the disk itself is faulty. However, knowing this value still shouldn't help the attacker "reverse" it to retrieve the IK3, so it still can't be used to help attackers aiming to guess the password off the device.<br />
<br />
As we've seen, the Android FDE's KDF is "bound" to the hardware of the device by the intermediate KeyMaster signature. But how secure <i>is</i> the KeyMaster module? How are the key blobs protected? Unfortunately, this is hard to say. The implementation of the KeyMaster module is provided by the SoC OEMs and, as such, is completely undocumented (essentially a black-box). We could try and rely on the official Android documentation, which states that the KeyMaster module: "...offers an opportunity for Android devices to provide <i>hardware-backed</i>, <i>strong
security</i> services...". But surely that's not enough.<br />
<br />
So... Are you pondering what I'm pondering? <br />
<br />
<h2>
Reversing Qualcomm's KeyMaster </h2>
<br />
As we've seen in the previous blog posts, Qualcomm provides a Trusted Execution Environment called <b>QSEE</b> (<b>Q</b>ualcomm <b>S</b>ecure <b>E</b>xecution <b>E</b>nvironment). The QSEE environment allows small applications, called "Trustlets", to execute on a dedicated secured processor within the "Secure World" of TrustZone. One such QSEE trustlet running in the "Secure World" is the <i>KeyMaster</i> application. As we've already seen <a href="https://bits-please.blogspot.com/2016/05/qsee-privilege-escalation-vulnerability.html">how to reverse-engineer QSEE trustlets</a>, we can simply apply the same techniques in order to reverse engineer the KeyMaster module and gain some insight into its inner workings. <br />
<br />
First, let's take a look at the <a href="http://androidxref.com/5.1.1_r6/xref/hardware/qcom/keymaster/keymaster_qcom.h#70">Android source code</a> which is used to interact with the KeyMaster application. Doing so reveals that the trustlet only supports four different commands:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkzh1HHY8UwRdDaOMIU-lZEY1NaCrn3pyc9zeST-MsgN-QL0OWl_Z1zWJI78fQcZNyadx4EkggERTklC552hXXKXQi3HsXl4WkDSHiClX4jXpnKGRM53HUSWjVwbnQ9yOMEVs0YQPAw17d/s1600/Screenshot+from+2016-06-18+23%253A23%253A52.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkzh1HHY8UwRdDaOMIU-lZEY1NaCrn3pyc9zeST-MsgN-QL0OWl_Z1zWJI78fQcZNyadx4EkggERTklC552hXXKXQi3HsXl4WkDSHiClX4jXpnKGRM53HUSWjVwbnQ9yOMEVs0YQPAw17d/s1600/Screenshot+from+2016-06-18+23%253A23%253A52.png" /></a></div>
As we're interested in the protections guarding the generated key blobs, let's take a look at the <i>KEYMASTER_SIGN_DATA</i> command. This command receives a previously encrypted key blob and <i>somehow</i> performs an operation using the encapsulated cryptographic key. Ergo, by reverse-engineering this function, we should be able to deduce how the encrypted key blobs are decapsulated by the KeyMaster module.<br />
<br />
The command's signature is exactly as you'd imagine - the user provides an encrypted key blob, the signature parameters, and the address and length of the data to be signed. The trustlet then decapsulates the key, calculates the signature, and writes it into the shared result buffer.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKN31s6WWT0hWFXtzJp7AfWza71qQbczR4LgXDfDnIdv4G9TPBxVlYS20TKAdeskq4Opdw2cEIqljQDNlUYsGtVaxYbawM-Q2bxlX8kSYot0rlREIF2hWR1N_QlkNP9qfcpRZau4CwtPyv/s1600/Screenshot+from+2016-06-18+23%253A49%253A58.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKN31s6WWT0hWFXtzJp7AfWza71qQbczR4LgXDfDnIdv4G9TPBxVlYS20TKAdeskq4Opdw2cEIqljQDNlUYsGtVaxYbawM-Q2bxlX8kSYot0rlREIF2hWR1N_QlkNP9qfcpRZau4CwtPyv/s1600/Screenshot+from+2016-06-18+23%253A49%253A58.png" /></a></div>
<br />
As luck would have it, the key blob's structure is actually defined in the <a href="http://androidxref.com/5.1.1_r6/xref/hardware/qcom/keymaster/keymaster_qcom.h#54">supplied header files</a>. Here's what it looks like:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUJmcPXEwGNR1PAZAi5Ijz8Rpu7Jss_4xFrQmDWMTkjW0-Xj-fOcxLvIwZzvxFjn89FZuT64Fe5234OzyP8M_hm4pLIEJBGf6RBo5C7pWgxhopsrqRjln3hYcLaqrS3SXfVXc-eFKgswWj/s1600/Screenshot+from+2016-06-18+23%253A37%253A47.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUJmcPXEwGNR1PAZAi5Ijz8Rpu7Jss_4xFrQmDWMTkjW0-Xj-fOcxLvIwZzvxFjn89FZuT64Fe5234OzyP8M_hm4pLIEJBGf6RBo5C7pWgxhopsrqRjln3hYcLaqrS3SXfVXc-eFKgswWj/s1600/Screenshot+from+2016-06-18+23%253A37%253A47.png" /></a></div>
<br />
Okay! This is pretty interesting.<br />
<br />
First, we can see that the key blob contains the unencrypted modulus and public exponent of the generated RSA key. However, the private exponent seems to be encrypted in some way. Not only that, but the whole key blob's authenticity is verified by using an HMAC. So where is the encryption key stored? Where is the HMAC key stored? We'll have to reverse-engineer the KeyMaster module to find out.<br />
<br />
Let's take a look at the KeyMaster trustlet's implementation of the <i>KEYMASTER_SIGN_DATA</i> command. The function starts with some boilerplate validations in order to make sure the supplied parameters are valid. We'll skip those, since they aren't the focus of this post. After verifying all the parameters, the function maps-in the user-supplied data buffer, so that it will be accessible to the "Secure World". Eventually, we reach the "core" logic of the function:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMQK5wpNWb9950-mawhLtx-01LVlufxHrgFqH88hK_PNDKz7eUG5ii2EOVTdutCcRMaOOZmKMy13UWburTaeN7qxLVr1gdJkbX3dkX9lQYiFiQtI-iLsvKwUjG6pLZKGvBx-Z30KPaqaK7/s1600/Screenshot+from+2016-06-19+00%253A19%253A50.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMQK5wpNWb9950-mawhLtx-01LVlufxHrgFqH88hK_PNDKz7eUG5ii2EOVTdutCcRMaOOZmKMy13UWburTaeN7qxLVr1gdJkbX3dkX9lQYiFiQtI-iLsvKwUjG6pLZKGvBx-Z30KPaqaK7/s1600/Screenshot+from+2016-06-19+00%253A19%253A50.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_nkYRimW2zfFH0pgE0Xa6_9UH24rNTCEUkljlz_4f5S_mtGLK704zN9BOrYzbr0xf9o0ihKfzh-HuiIYsjAY9iafhxONFAQ0BrTDENBsPrsqnoMvZss-gDsApv8rAOkg1k7cjK3cqyGk4/s1600/Screenshot+from+2016-06-19+00%253A12%253A43.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
Okay, we're definitely getting somewhere!<br />
<br />
First of all, we can see that the code calls some function which I've taken the liberty of calling <i>get_some_kind_of_buffer</i>, and stores the results in the variables <i>buffer_0</i> and <i>buffer_1</i>. Immediately after retrieving these buffers, the code calls the <i>qsee_hmac</i> function in order to calculate the HMAC of the first 0x624 bytes of the user-supplied key blob. This makes sense, since the size of the key blob structure we've seen before is exactly 0x624 bytes (without the HMAC field).<br />
<br />
But wait! We've already seen the <i>qsee_hmac</i> function before - in the Widevine application. Specifically, we know it receives the following arguments:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3AKvDCQUUn31gatGp0o4SKzU0bGfneuI_4vo_wdwaYDsYnjwfaFWrhSz5SHZPStNmDhzH17H3Q2Q2zS7iaYOwnEHugPffuWVkntrK9i3RauZ1yMEbbxAv1UBk7RfBeIuyKil85KTjCk3E/s1600/Screenshot+from+2016-06-19+00%253A26%253A05.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3AKvDCQUUn31gatGp0o4SKzU0bGfneuI_4vo_wdwaYDsYnjwfaFWrhSz5SHZPStNmDhzH17H3Q2Q2zS7iaYOwnEHugPffuWVkntrK9i3RauZ1yMEbbxAv1UBk7RfBeIuyKil85KTjCk3E/s1600/Screenshot+from+2016-06-19+00%253A26%253A05.png" /></a></div>
<br />
The variable that we've called <i>buffer_1</i> is passed in as the fourth argument to <i>qsee_hmac</i>. This can only mean one thing... It is in fact the HMAC key!<br />
<br />
What about <i>buffer_0</i>? We can already see that it is used in the function <i>do_something_with_keyblob</i>. Not only that, but immediately after calling that function, the signature is calculated and written to the destination buffer. However, as we've previously seen, the private exponent is <u>encrypted</u> in the key blob. Obviously the RSA signature cannot be calculated until the private exponent is decrypted... So what does <i>do_something_with_keyblob </i>do? Let's see:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEja1PmVtPXXeNArEpHv0pAV8H2nD_C7UD2rkfyYQPBaTqg_6vyd_j47JGqrnOzxdLILobMlbI0LmPvE0uWFgvGph0DMKt5g4nUwLFyAZLmWGvsSBVIVXL9mzXSNAmmWNTcv9fkAyA5-mwsx/s1600/Screenshot+from+2016-06-19+00%253A35%253A16.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEja1PmVtPXXeNArEpHv0pAV8H2nD_C7UD2rkfyYQPBaTqg_6vyd_j47JGqrnOzxdLILobMlbI0LmPvE0uWFgvGph0DMKt5g4nUwLFyAZLmWGvsSBVIVXL9mzXSNAmmWNTcv9fkAyA5-mwsx/s1600/Screenshot+from+2016-06-19+00%253A35%253A16.png" /></a></div>
<br />
Aha! Just as we suspected. The function <i>do_something_with_keyblob</i> simply decrypts the private exponent, using <i>buffer_0</i> as the encryption key!<br />
<br />
Finally, let's take a look at the function that was used to retrieve the HMAC and encryption keys (now bearing a more appropriate name):<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQr8iHDjwWA2lM_oUkTCngKv1AXEMokJ9OgQlobS908xTvZ0ftgKpYQoOSWX_n-h38n9jsDgDIjKNSHKvVF_FSXxz5lGTrsmg1qsSGu4pQKSl0O0_LCywE7CfXSZxMlzdU9uHBtjZ3Cmaj/s1600/Screenshot+from+2016-06-19+00%253A47%253A56.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQr8iHDjwWA2lM_oUkTCngKv1AXEMokJ9OgQlobS908xTvZ0ftgKpYQoOSWX_n-h38n9jsDgDIjKNSHKvVF_FSXxz5lGTrsmg1qsSGu4pQKSl0O0_LCywE7CfXSZxMlzdU9uHBtjZ3Cmaj/s1600/Screenshot+from+2016-06-19+00%253A47%253A56.png" /></a></div>
<br />
As we can see in the code above, the HMAC key and the encryption key are <i>both</i> generated using some kind of key derivation function. Each key is generated by invoking the KDF using a pair of hard-coded strings as inputs. The resulting derived key is then stored in the KeyMaster application's global buffer, and the pointer to the key is returned to the caller. Moreover, if we are to trust the provided strings, the internal key derivation function uses an <i>actual</i> hardware key, called the <i>SHK</i>, which would no doubt be hard to extract using software...<br />
<br />
...But this is all irrelevant! The decapsulation code we have just reverse-engineered has revealed a very important fact.<br />
<br />
Instead of creating a scheme which <i>directly</i> uses the hardware key without ever divulging it to software or firmware, the code above performs the encryption and validation of the key blobs using keys which are <u>directly available to the TrustZone software</u>! Note that the keys are also <u>constant</u> - they are directly derived from the SHK (which is fused into the hardware) and from two "hard-coded" strings.<br />
<br />
Let's take a moment to explore some of the implications of this finding.<br />
<br />
<h2>
Conclusions</h2>
<h2>
</h2>
<ul>
<li><b>The key derivation is not hardware bound.</b> Instead of using a real hardware key which cannot be extracted by software (for example, the SHK), the KeyMaster application uses a key <u>derived from the SHK</u> and <u>directly available to TrustZone</u>. </li>
<li><b>OEMs can comply with law enforcement to break Full Disk Encryption.</b> Since the key is available to TrustZone, OEMs could simply create and sign a TrustZone image which extracts the KeyMaster keys and flash it to the target device. This would allow law enforcement to easily brute-force the FDE password off the device using the leaked keys.</li>
<li><b>Patching TrustZone vulnerabilities does not necessarily protect you from this issue.</b> Even on patched devices, if an attacker can obtain the encrypted disk image (e.g. by using forensic tools), they can then "downgrade" the device to a vulnerable version, extract the key by exploiting TrustZone, and use them to brute-force the encryption. Since the key is derived directly from the SHK, and the SHK <u>cannot be modified</u>, this renders all down-gradable devices directly vulnerable.</li>
<li><b>Android FDE is only as strong as the TrustZone kernel or KeyMaster.</b> Finding a TrustZone kernel vulnerability or a vulnerability in the KeyMaster trustlet, directly leads to the disclosure of the KeyMaster keys, thus enabling off-device attacks on Android FDE. </li>
</ul>
<br />
During my communication with Qualcomm I voiced concerns about the usage of a software-accessible key derived from the SHK. I suggested using the SHK (or another hardware key) directly. As far as I know, the SHK <u>cannot be extracted from software</u>, and is only available to the cryptographic processors (similarly to Apple's UID). Therefore, using it would thwart any attempt at off-device brute force attacks (barring the use of specialized hardware to extract the key).<br />
<br />
However, reality is not that simple. The SHK is used for many different purposes. Allowing the user to directly encrypt data using the SHK would compromise those use-cases. Not only that, but the KeyMaster application is widely used in the Android operating-system. Modifying its behaviour could "break" applications which rely on it. Lastly, the current design of the KeyMaster application doesn't differentiate between requests which use the KeyMaster application for Android FDE and other requests for different use-cases. This makes it harder to incorporate a fix which only modifies the KeyMaster application. <br />
<br />
Regardless, I believe this issue underscores the need for a solution that entangles the full disk encryption key with the device's hardware in a way which cannot be bypassed using software. Perhaps that means redesigning the FDE's KDF. Perhaps this can be addressed using additional hardware. I think this is something Google and OEMs should definitely get together and think about.<br />
<br />
<h2>
Extracting the KeyMaster Keys</h2>
<h2>
</h2>
<h2>
</h2>
Now that we've set our sights on the KeyMaster keys, we are still left with the challenge of extracting the keys directly from TrustZone.<br />
<br />
Previously on the zero-to-TrustZone series of blog posts, we've discovered an exploit which allowed us to achieve <a href="https://bits-please.blogspot.com/2016/05/qsee-privilege-escalation-vulnerability.html">code-execution within QSEE</a>, namely, within the Widevine DRM application. However, is that enough?<br />
<br />
Perhaps we could read the keys directly from the KeyMaster trustlet's memory from the context of the hijacked Widevine trustlet? Unfortunately, the answer is no. Any attempt to access a different QSEE application's memory causes an XPU violation, and subsequently crashes the violating trustlet (even when switching to a kernel context). What about calling the same KDF used by the KeyMaster module to generate the keys from the context of the Widevine trustlet? Unfortunately the answer is no once again. The KDF is only present in the KeyMaster application's code segment, and QSEE applications cannot modify their own code or allocate new executable pages.<br />
<br />
Luckily, we've also previously discovered an additional <a href="https://bits-please.blogspot.com/2016/06/trustzone-kernel-privilege-escalation.html">privilege escalation from QSEE to the TrustZone kernel</a>. Surely code execution within the TrustZone kernel would allow us to hijack any QSEE application! Then, once we control the KeyMaster application, we can simply use it to leak the HMAC and encryption keys and call it a day.<br />
<br />
Recall that in the <a href="https://bits-please.blogspot.com/2016/06/trustzone-kernel-privilege-escalation.html">previous blog post</a> we reverse-engineered the mechanism behind the invocation of system calls in the TrustZone kernel. Doing so revealed that most system-calls are invoked indirectly by using a set of globally-stored pointers, each of which pointing to a different table of supported system-calls. Each system-call table simply contained a bunch of consecutive 64-bit entries; a 32-bit value representing the syscall number, followed by a
32-bit pointer to the syscall handler function itself. Here is one such table:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6rzwoGK7sAm5NRfwyavj7mYtdn_yoaqTKhcGBTHswaerKdC22-_3LkWwjsOip-yxL7LStUhIULcgDSeY3FfpmHMDsirH0Yk-hHAGgVWQfcfEqnc_jmsAxj_EVebdti_pTEPQWts-KITWf/s1600/Screenshot+from+2016-06-12+16%253A43%253A49.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6rzwoGK7sAm5NRfwyavj7mYtdn_yoaqTKhcGBTHswaerKdC22-_3LkWwjsOip-yxL7LStUhIULcgDSeY3FfpmHMDsirH0Yk-hHAGgVWQfcfEqnc_jmsAxj_EVebdti_pTEPQWts-KITWf/s1600/Screenshot+from+2016-06-12+16%253A43%253A49.png" /></a></div>
<br />
Since these tables are used by all QSEE trustlets, they could serve as a highly convenient entry point in order to hijack the code execution within the KeyMaster application!<br />
<br />
All we would need to do is to overwrite a system-call handler entry in the table, and point it to a function of our own. Then, once the KeyMaster application invokes the target system-call, it would execute our own handler instead of the original one! This also enables us not to worry about restoring execution after executing our code, which is a nice added bonus. <br />
<br />
But there's a tiny snag - in order to direct the handler at a function of our own, we need some way to allocate a chunk of code which will be <i>globally available</i> in the "Secure World". This is because, as mentioned above, different QSEE applications cannot access each other's memory segments. This renders our <a href="https://bits-please.blogspot.com/2016/06/trustzone-kernel-privilege-escalation.html">previous method</a> of overwriting the code segments of the Widevine application useless in this case. However, as we've seen in the past, the TrustZone Kernel's code segments (which are accessible to all QSEE application when executing in kernel context) are protected using a special hardware component called an XPU. Therefore, even when running within the TrustZone kernel and disabling access protection faults in the ARM MMU, we are still unable to modify them.<br />
<br />
This is where some brute-force comes in handy... I've written a small snippet of code that quickly iterates over all of the TrustZone Kernel's code segments, and <i>attempt</i>s to modify them. If there is any (mistakenly?) XPU-unprotected region, we will surely find it. Indeed, after iterating through the code segments, one rather large segment, ranging from addresses 0xFE806000 to 0xFE810000, appeared to be unprotected!<br />
<br />
Since we don't want to disrupt the regular operation of the TrustZone kernel, it would be wise to find a small code-cave in that region, or a small chunk of code that would be harmless to overwrite. Searching around for a bit reveals a small bunch of logging strings in the segment - surely we can overwrite them without any adverse effects:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjN820703s2lHPwHkJ-yKemK2MHs_-8Ia6wto4ugjprppT4lpTSXMFTW0ABhcVFc41HYX6rDB7T4rkLq1e_dUHV1vNWAJImNl-KukJ5IOFcvYCt-77b0J_HVnCcCvA45dkhvkWJD6-XIbFe/s1600/Screenshot+from+2016-06-19+02%253A32%253A48.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjN820703s2lHPwHkJ-yKemK2MHs_-8Ia6wto4ugjprppT4lpTSXMFTW0ABhcVFc41HYX6rDB7T4rkLq1e_dUHV1vNWAJImNl-KukJ5IOFcvYCt-77b0J_HVnCcCvA45dkhvkWJD6-XIbFe/s1600/Screenshot+from+2016-06-19+02%253A32%253A48.png" /></a></div>
Now that we have a modifiable code cave in the TrustZone kernel, we can proceed to write a small stub that, when called, will exfiltrate the KeyMaster keys directly from the KeyMaster trustlet's memory!<br />
<br />
Lastly, we need a simple way to cause the KeyMaster application to execute the hijacked system-call. Remember, we can easily send commands to the KeyMaster application which, in turn, will cause the KeyMaster application to call quite a few system-calls. Reviewing the KeyMaster's key-generation command reveals that one good candidate to hijack would be the "<i>qsee_hmac</i>" system-call:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZbp_lmaCsTIFkAOXjXccv2Kp0jsYkR-47XTdd8ppuYYgRlqWEz2W8HVdm6Nfi2UZ2mW5nM1JT8cb8TEBxkRNnh7erLvcKx75R3uVLklZhnONmelRjtTPnxro5guu59SH1GjAL4fTJarWl/s1600/Screenshot+from+2016-06-23+14%253A54%253A42.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZbp_lmaCsTIFkAOXjXccv2Kp0jsYkR-47XTdd8ppuYYgRlqWEz2W8HVdm6Nfi2UZ2mW5nM1JT8cb8TEBxkRNnh7erLvcKx75R3uVLklZhnONmelRjtTPnxro5guu59SH1GjAL4fTJarWl/s1600/Screenshot+from+2016-06-23+14%253A54%253A42.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">KeyMaster's "Generate Key" Flow</td></tr>
</tbody></table>
Where <i>qsee_hmac</i>'s signature is:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQkzYyN30NLHO7t8pUmU5j9XMgrqB5e09oYfhFop6RBB4yxUXwKzwQ8z0rAawRS_i2tOx20MgaK9sNd03lGcrWxXa9XwVZ32HkrAYzX7ZQByAhyphenhyphenHCCkceR3nmrhUuwIStyBh1Cr2IzeJiG/s1600/Screenshot+from+2016-06-23+13%253A38%253A54.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQkzYyN30NLHO7t8pUmU5j9XMgrqB5e09oYfhFop6RBB4yxUXwKzwQ8z0rAawRS_i2tOx20MgaK9sNd03lGcrWxXa9XwVZ32HkrAYzX7ZQByAhyphenhyphenHCCkceR3nmrhUuwIStyBh1Cr2IzeJiG/s1600/Screenshot+from+2016-06-23+13%253A38%253A54.png" /></a></div>
<br />
This is a good candidate for a few reasons:<br />
<ol>
<li>The "data" argument that's passed in is a buffer that's <u>shared with the non-secure world</u>. This means whatever we write to it can easily retrieved after returning from the "Secure World".</li>
<li>The <i>qsee_hmac</i> function is not called very often, so hijacking it for a couple of seconds would probably be harmless.</li>
<li>The function receives the address of the HMAC key as one of the arguments. This saves us the need to find the KeyMaster application's address dynamically and calculate the addresses of the keys in memory.</li>
</ol>
Finally, all our shellcode would have to do is to read the HMAC and encryption keys from the KeyMaster application's global buffer (at the locations we saw earlier on), and "leak" them into the shared buffer. After returning from the command request, we could then simply fish-out the leaked keys from the shared buffer. Here's a small snippet of THUMB assembly that does just that:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlYdeEYkm9gaKr22rmdPXmiYGFukLo_K5sifyn2-XU_AyKNP10lJF52EfeV-Aa_JWGVslKVGrgm8xyL2uEQVBgWXwgoGMVCu50pWw8HFkuxNcMtgxlgHv8Uo2bfeQufzKLDYWC0XxetraW/s1600/Screenshot+from+2016-06-29+18%253A27%253A09.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlYdeEYkm9gaKr22rmdPXmiYGFukLo_K5sifyn2-XU_AyKNP10lJF52EfeV-Aa_JWGVslKVGrgm8xyL2uEQVBgWXwgoGMVCu50pWw8HFkuxNcMtgxlgHv8Uo2bfeQufzKLDYWC0XxetraW/s1600/Screenshot+from+2016-06-29+18%253A27%253A09.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Shellcode which leaks KeyMaster Keys</td></tr>
</tbody></table>
<br />
<ol>
</ol>
<h2>
</h2>
<h2>
Putting it all together</h2>
<br />
Finally, we have all the pieces of the puzzle. All we need to do in order to extract the KeyMaster keys is to: <br />
<ul>
<li>Enable the DACR in the TrustZone kernel to allow us to modify the code cave.</li>
<li>Write a small shellcode stub in the code cave which reads the keys from the KeyMaster application.</li>
<li>Hijack the "<i>qsee_hmac</i>" system-call and point it at our shellcode stub.</li>
<li>Call the KeyMaster's key-generation command, causing it to trigger the poisoned system-call and exfiltrate the keys into the shared buffer.</li>
<li>Read the leaked keys from the shared buffer. </li>
</ul>
Here's a diagram detailing all of these steps: <br />
<ul>
</ul>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbUXg-PJ7EZ5wBbZ3xlpKVdcNphCefrHIXWHzDC2ZuBYeOE9JTU5OcDLeizYDvExVql4_nCn7nbLN303Z7YQSDnHtx6xyk9XENU_qsDUgBTwVS4nILVF6WDT_cB5J7hQhggOcX4NnzB6TU/s1600/Screenshot+from+2016-06-29+21%253A43%253A24.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbUXg-PJ7EZ5wBbZ3xlpKVdcNphCefrHIXWHzDC2ZuBYeOE9JTU5OcDLeizYDvExVql4_nCn7nbLN303Z7YQSDnHtx6xyk9XENU_qsDUgBTwVS4nILVF6WDT_cB5J7hQhggOcX4NnzB6TU/s1600/Screenshot+from+2016-06-29+21%253A43%253A24.png" /></a></div>
<br />
<ul>
</ul>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<ul>
</ul>
<h2>
The Code</h2>
<br />
Finally, as always, I've provided the full source code for the attack described above. The code builds upon the two previously disclosed issues in the zero-to-TrustZone series, and allows you to leak the KeyMaster keys directly from your device! After successfully executing the exploit, the KeyMaster keys should be printed to the console, like so:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiR5dSCGZ8bAlkttIXPW_QeMnSh7Kw9MyEc1wqXbkPz1f9m8GNQqBMjnzmf6UfXt-I9Ben2bnTiNvkp_hddtxwXZfaGgB34f8UM0Yv-7VRoW-TrAk2wA3WUgNxdMiBrfTSBbXMzw5AsZaFR/s1600/Screenshot+from+2016-06-29+17%253A16%253A23.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiR5dSCGZ8bAlkttIXPW_QeMnSh7Kw9MyEc1wqXbkPz1f9m8GNQqBMjnzmf6UfXt-I9Ben2bnTiNvkp_hddtxwXZfaGgB34f8UM0Yv-7VRoW-TrAk2wA3WUgNxdMiBrfTSBbXMzw5AsZaFR/s1600/Screenshot+from+2016-06-29+17%253A16%253A23.png" /></a></div>
<br />
<br />
You can find the full source code of the exploit here:<br />
<br />
<a href="https://github.com/laginimaineb/ExtractKeyMaster">https://github.com/laginimaineb/ExtractKeyMaster</a><br />
<br />
I've also written a set of python scripts which can be used to brute-force Android full disk encryption off the device. You can find the scripts here:<br />
<br />
<a href="https://github.com/laginimaineb/android_fde_bruteforce">https://github.com/laginimaineb/android_fde_bruteforce</a><br />
<br />
Simply invoke the python script <i>fde_bruteforce.py</i> using:<br />
<ul>
<li>The <i>crypto footer</i> from the device</li>
<li>The leaked KeyMaster keys</li>
<li>The word-list containing possible passwords</li>
</ul>
Currently, the script simply enumerates each password from a given word-list, and attempts to match the encryption result with the "scrypted intermediate key" stored in the <i>crypto footer</i>. That is, it passes each word in the word-list through the Android FDE KDF, <i>scrypt</i>s the result, and compares it to the value stored in the <i>crypto footer</i>. Since the implementation is fully in python, it is rather slow... However, those seeking speed could port it to a much faster platform, such as <a href="https://hashcat.net/hashcat/">hashcat</a>/<a href="https://hashcat.net/oclhashcat/">oclHashcat</a>.<br />
<br />
Here's what it looks like after running it on my own Nexus 6, encrypted using the password "secret":<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg24CCvRz-KoKWETst3IhLziI_ukDr4I4G3HO6R1ww3mwXs2HyTlezRw_hxdww4zQrfxzHgVBJ5XGwpQJktdSLXCbebAjrl9jRqBtV4HfBVpvZFTZ1p2F006SSRPvIVj92NYKZUuHgyAc3C/s1600/Screenshot+from+2016-06-18+19%253A18%253A03.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg24CCvRz-KoKWETst3IhLziI_ukDr4I4G3HO6R1ww3mwXs2HyTlezRw_hxdww4zQrfxzHgVBJ5XGwpQJktdSLXCbebAjrl9jRqBtV4HfBVpvZFTZ1p2F006SSRPvIVj92NYKZUuHgyAc3C/s1600/Screenshot+from+2016-06-18+19%253A18%253A03.png" /></a></div>
<br />
Lastly, I've also written a script which can be used to decrypt already-generated KeyMaster key blobs. If you simply have a KeyMaster<i> key blob</i> that you'd like to decrypt using the leaked keys, you can do so by invoking the script <i>km_keymaster.py</i>, like so:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEheSNLC-_6OT9EjImN8TXH7ny8AWrm7bPgPVJVnd7bzvuded-ir4WjZrgLDWZBOPFijXwWytqGpiWjQWMBiLjdXGs6Xo4VFWtUIYwNt_aBefs_R2I2-N2L03PoU43iOMCXHR6wSEScifDHl/s1600/Screenshot+from+2016-06-29+17%253A33%253A03.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEheSNLC-_6OT9EjImN8TXH7ny8AWrm7bPgPVJVnd7bzvuded-ir4WjZrgLDWZBOPFijXwWytqGpiWjQWMBiLjdXGs6Xo4VFWtUIYwNt_aBefs_R2I2-N2L03PoU43iOMCXHR6wSEScifDHl/s1600/Screenshot+from+2016-06-29+17%253A33%253A03.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidqkNg4US0PkRYKmR0GLnBfkK9i5jiS4ByGlqTEmSe1pestQsi5LAaaYJkM9tH8M8y-fIYEVvp-ajmlA7s1SQirVEvfF1bM0nabDhTrm9YNwFrrPYGlGKTlU2l7VP3nAMehpa1zW4UNSzo/s1600/decrypted.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<h2>
Final Thoughts</h2>
<h2>
</h2>
Full disk encryption is used world-wide, and can sometimes be instrumental to ensuring the privacy of people's most intimate pieces of information. As such, I believe the encryption scheme should be designed to be as "bullet-proof" as possible, against all types of adversaries. As we've seen, the current encryption scheme is far from bullet-proof, and can be hacked by an adversary or even broken by the OEMs themselves (if they are coerced to comply with law enforcement).<br />
<br />
I hope that by shedding light on the subject, this research will motivate OEMs and Google to come together and think of a more robust solution for FDE. I realise that in the Android ecosystem this is harder to guarantee, due to the multitude of OEMs. However, I believe a concentrated effort on both sides can help the next generation of Android devices be truly "uncrackable".laginimainebhttp://www.blogger.com/profile/00867710965331025912noreply@blogger.com1378tag:blogger.com,1999:blog-2029700426505953971.post-36651616754285482952016-06-15T14:27:00.000+03:002016-06-15T14:37:03.366+03:00TrustZone Kernel Privilege Escalation (CVE-2016-2431)In this blog post we'll continue our journey from zero permissions to code execution in the TrustZone kernel. Having previously <a href="http://bits-please.blogspot.com/2016/05/qsee-privilege-escalation-vulnerability.html">elevated our privileges to QSEE</a>, we are left with the task of exploiting the TrustZone kernel itself.<br />
<br />
"Why?", I hear you ask.<br />
<br />
Well... There are quite a few interesting things we can do solely from the context of the TrustZone kernel. To name a few:<br />
<ul>
<li>We could hijack any QSEE application directly, thus exposing all of it's internal secrets. For example, we could directly extract the stored real-life fingerprint or various secret encryption keys (more on this in the next blog post!).</li>
<li>We could disable the hardware protections provided by the SoC's XPUs, allowing us to read and write directly to all of the DRAM. This includes the memory used by the peripherals on the board (such as the modem).</li>
<li><a href="http://bits-please.blogspot.com/2016/02/unlocking-motorola-bootloader.html">As we've previously seen</a>, we could blow the QFuses responsible for various device features. In certain cases, this could allow us to unlock a locked bootloader (depending on how the lock is implemented). </li>
</ul>
So now that we've set the stage, let's start by surveying the attack surface!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdFxR_zYqlk-SZPavZFtEUBzJ8GLh4LS7etosBfx5HwLWFBWPF3tKMZCbWkkq3PALX1-IEhj25KcSXKH7d6wwUxnm4-WevyBJexAfb50UgdpI6M4PmfezbDTy4xSYiGQjJtfkLebf_dqP2/s1600/minas_tirith_2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="561" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdFxR_zYqlk-SZPavZFtEUBzJ8GLh4LS7etosBfx5HwLWFBWPF3tKMZCbWkkq3PALX1-IEhj25KcSXKH7d6wwUxnm4-WevyBJexAfb50UgdpI6M4PmfezbDTy4xSYiGQjJtfkLebf_dqP2/s640/minas_tirith_2.png" width="576" /></a></div>
<br />
<a name='more'></a><br />
<br />
<h2>
</h2>
<h2>
Attack Surface</h2>
<h2>
</h2>
<br />
<b>Q</b>ualcomm's <b>S</b>ecure <b>E</b>nvironment <b>O</b>perating <b>S</b>ystem (QSEOS), like most operating systems, provides services to the applications running under it by means of system-calls. <br />
<br />
As you know, operating systems must take great care to protect themselves from malicious applications. In the case of system-calls, this means the operating system mustn't trust any information provided by an application and should always validate it. This forms a "trust-boundary" between the operating system itself and the running applications.<br />
<br />
So... This sounds like a good place to start looking! Let's see if the TrustZone kernel does, in fact, cover all the bases.<br />
<br />
In the "Secure World", just like the "Normal World", user-space applications can invoke system-calls by issuing the "SVC" instruction. All system-calls in QSEE are invoked via a single function, which I've dubbed "qsee_syscall":<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhc60b5hkOZTAjGorFHz3SgSusDZXs0fcwOHgMJMHqfDEchW-0Rx3OVrYn4w47xkH7X_0cI32svjexKwFEzkuxbYlg-SVmgya1lPVFKB1Z6mHVTaYiL132gNc4zj_7wB6NV9yhHMo-9isgj/s1600/Screenshot+from+2016-06-06+21%253A19%253A37.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhc60b5hkOZTAjGorFHz3SgSusDZXs0fcwOHgMJMHqfDEchW-0Rx3OVrYn4w47xkH7X_0cI32svjexKwFEzkuxbYlg-SVmgya1lPVFKB1Z6mHVTaYiL132gNc4zj_7wB6NV9yhHMo-9isgj/s1600/Screenshot+from+2016-06-06+21%253A19%253A37.png" /></a></div>
As we can see, the function is a simple wrapper which does the following:<br />
<ul>
<li>Stores the syscall number in <i>R0</i></li>
<li>Stores the arguments for the syscall in <i>R4</i>-<i>R9</i></li>
<li>Invokes the SVC instruction with the code 0x1400<i> </i></li>
<li>Returns the syscall result via <i>R0</i></li>
</ul>
So we know how syscalls are invoked, now let's look for the code in the TrustZone kernel which is used to handle <i>SVC</i> requests. Recall that when executing an SVC instruction in the "Secure World", similarly to
the "Normal World", the "Secure World" must register the address of the vector to
which the processor will jump when such an instruction is invoked. <br />
<br />
Unlike <i>SMC</i> instructions (used to request "Secure World" services from the "Normal World"), which use the <a href="http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0433b/CIHGAFJH.html">MVBAR</a> (<b>M</b>onitor <b>V</b>ector <b>B</b>ase <b>A</b>ddress <b>R</b>egister) register to provide the vector's base address, <i>SVC</i> instructions simply use the "Secure" version of the <a href="http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0344k/Babfbcae.html">VBAR</a> (<b>V</b>ector <b>B</b>ase <b>A</b>ddress <b>R</b>egister).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgt-Sr-rO1e9YZP6Dwgb7B9IbWgXZFZomwT5iwcMatHi1ftI6ChqZjgwB4QQcuikwFQaCoXZh-JR-dNfczuLLKYIiOsPUFhBnFpuBnjzA7gCQBWUUImMgy7RVJzes4zB2at8cD0ecMRDQ9F/s1600/Screenshot+from+2016-06-06+21%253A46%253A16.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgt-Sr-rO1e9YZP6Dwgb7B9IbWgXZFZomwT5iwcMatHi1ftI6ChqZjgwB4QQcuikwFQaCoXZh-JR-dNfczuLLKYIiOsPUFhBnFpuBnjzA7gCQBWUUImMgy7RVJzes4zB2at8cD0ecMRDQ9F/s1600/Screenshot+from+2016-06-06+21%253A46%253A16.png" /></a></div>
<br />
Accessing the VBAR is done using the MRC/MCR opcodes, with the following operands:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-U_AfBl1863igpY-QG9e7PM5v55yTBRbmdwYz6nKeT6Ihb4Y74rYZY_5eZf37n1XKCGuE05rEhsJNyCofnA0Mv5hEmLEDQeF6JlU2JUud5SrtkM9lu6NztEs4vuInnBly7fNBw1gl_g7T/s1600/Screenshot+from+2016-06-06+22%253A06%253A36.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-U_AfBl1863igpY-QG9e7PM5v55yTBRbmdwYz6nKeT6Ihb4Y74rYZY_5eZf37n1XKCGuE05rEhsJNyCofnA0Mv5hEmLEDQeF6JlU2JUud5SrtkM9lu6NztEs4vuInnBly7fNBw1gl_g7T/s1600/Screenshot+from+2016-06-06+22%253A06%253A36.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
So
this means we can simply search for an MCR opcode with the following
operands in the TrustZone kernel, and we should be able to find the
address of secure copy of the VBAR. Indeed, searching for the opcode in the TrustZone image returns the
following match:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQPrUasauZHiUjNN5Uo4m9VLxXsjWt7Cz7ZXXV320EOuta2dqQHRuoC6szFn4Ds34sj65XA5cLUjjUU9Aj-nRbzF410ubJeS1mig9c-TfWwf9nG5PmzaFR8t6VOPCNvaLJh-4ARLS7Acs8/s1600/Screenshot+from+2016-06-06+22%253A10%253A08.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQPrUasauZHiUjNN5Uo4m9VLxXsjWt7Cz7ZXXV320EOuta2dqQHRuoC6szFn4Ds34sj65XA5cLUjjUU9Aj-nRbzF410ubJeS1mig9c-TfWwf9nG5PmzaFR8t6VOPCNvaLJh-4ARLS7Acs8/s1600/Screenshot+from+2016-06-06+22%253A10%253A08.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
According to the <a href="http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0425/BABHHDII.html">ARM documentation</a>, the "Secure Vector" has the following structure:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-E0KvoNyQm6jc7TZl_hA4OrIvB7j6Ib0ZHa9hfMNjja-clxZjJW6DE3AQITgc-xA0q2uHZ0-NQlV3_sSua4wMKyE0XZIL5xBddIWWfyiWBGObmZZ6vbbj1Kvx_UChKmCn12TJlcKuzunu/s1600/Screenshot+from+2016-06-06+22%253A12%253A05.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-E0KvoNyQm6jc7TZl_hA4OrIvB7j6Ib0ZHa9hfMNjja-clxZjJW6DE3AQITgc-xA0q2uHZ0-NQlV3_sSua4wMKyE0XZIL5xBddIWWfyiWBGObmZZ6vbbj1Kvx_UChKmCn12TJlcKuzunu/s1600/Screenshot+from+2016-06-06+22%253A12%253A05.png" /></a></div>
<br />
At this point we can start tracing the execution from the SVC handler in the vector table.<br />
<br />
The code initially does some boilerplate preparations, such as saving the passed arguments and context, and finally gets to the main entry point which is used to actually handle the requested system-call. Qualcomm have helpfully left a single logging string in this function containing it's original name "app_syscall_handler", so we'll use that name as well. Let's take a look at the function's high-level graph overview:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuGeRc_r7RblgHcasj1Jox-lYUCaaYIjdze8vPpcRoMzdrtT1amM3xQqAJIKx1EOgx8QKi4-UyP3uQIbuuPg2Qjq46HbL1vDfPtRPOAwumoZwFarIM3mnnA-uonYsS8j6ekZxM8lerLVFZ/s1600/Screenshot+from+2016-06-12+16%253A20%253A34.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="86" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuGeRc_r7RblgHcasj1Jox-lYUCaaYIjdze8vPpcRoMzdrtT1amM3xQqAJIKx1EOgx8QKi4-UyP3uQIbuuPg2Qjq46HbL1vDfPtRPOAwumoZwFarIM3mnnA-uonYsS8j6ekZxM8lerLVFZ/s640/Screenshot+from+2016-06-12+16%253A20%253A34.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">app_syscall_handler graph overview</td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivaTD7QKa-NV4YOBf5wqDicj3ItlZROJZCCYgH7jTb7lezS2lLDWaWd8dyDw7gdyPaSkgNlCChTXYc9ogA5rRY5_BXqgpuErVjSkyO50-pVlGnB4EzM6-W33CZ5uMUv7C-4591qvEDCf8l/s1600/Screenshot+from+2016-06-12+16%253A20%253A34.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivaTD7QKa-NV4YOBf5wqDicj3ItlZROJZCCYgH7jTb7lezS2lLDWaWd8dyDw7gdyPaSkgNlCChTXYc9ogA5rRY5_BXqgpuErVjSkyO50-pVlGnB4EzM6-W33CZ5uMUv7C-4591qvEDCf8l/s1600/Screenshot+from+2016-06-12+16%253A20%253A34.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
...Okay... That's a lot of code.<br />
<br />
However, on closer inspection, the graph seems very <i>shallow</i>, so while there are a lot of different code-paths, they are all relatively simple. In fact, the function is simply a large switch-case, which uses the syscall command-code supplied by the user (in <i>R0</i>) in order to select which syscall should be executed.<br />
<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimaX1hKitG7fVBheZaiQy-CuA646rFocoscg_kzLOFxhvKH798heIY3ZKgM0VKVZQN5ghQALvXcjvj5RpTSUtvA9u3X5bmSipwBK4jHDGyKqNYaqDOpraTnjBVFYYLfB7rqK7btQ2ga7Xm/s1600/Screenshot+from+2016-06-12+16%253A34%253A28.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimaX1hKitG7fVBheZaiQy-CuA646rFocoscg_kzLOFxhvKH798heIY3ZKgM0VKVZQN5ghQALvXcjvj5RpTSUtvA9u3X5bmSipwBK4jHDGyKqNYaqDOpraTnjBVFYYLfB7rqK7btQ2ga7Xm/s1600/Screenshot+from+2016-06-12+16%253A34%253A28.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">snippet from <i>app_syscall_handler</i>'s switch-case</td></tr>
</tbody></table>
<br />
But something's obviously missing! Where are the validations on the arguments passed in by the user? <i>app_syscall_handler</i> does no such effort, so this means the validation can only possibly be in the syscalls themselves... Time to dig deeper once more!<br />
<br />
As you can see in the screenshot above, most of the syscalls aren't directly invoked, but rather indirectly called by using a set of globally-stored pointers, each pointing to a different table of supported system-calls. I've taken to using the following (imaginative) names to describe them:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGcQjVNdMnPkwX0hxPqp-psGKWKj_Crs44UiclrpEuLQbh_XPudnRUTSaWlqJpmJ6cJ87GarcLFxiolGZ9pqOwIpogafS7tGTqfzUnK5vkWTJh-vCXudmmdVoqGERwuV_04ipaBOTGV3Sk/s1600/Screenshot+from+2016-06-12+16%253A39%253A56.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGcQjVNdMnPkwX0hxPqp-psGKWKj_Crs44UiclrpEuLQbh_XPudnRUTSaWlqJpmJ6cJ87GarcLFxiolGZ9pqOwIpogafS7tGTqfzUnK5vkWTJh-vCXudmmdVoqGERwuV_04ipaBOTGV3Sk/s1600/Screenshot+from+2016-06-12+16%253A39%253A56.png" /></a></div>
<br />
Cross-referencing these pointers reveals the locations of the actual system-call tables to which they point. The tables' structure is very simple - each entry contains a 32-bit number representing the syscall number within the table, followed by a pointer to the syscall handler function itself. Here is one such table:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7H6r9coay7vlDG0zEqdnns6XXJYRl5weGhC6sLPhxCbRliWik3tu6l9I52h4A2foK5sh8iflathVB9uJUXcuPsfr78rs14l1SNDywj8LEfNqyNwEf7ONEhkjDPD3aVIizxcQcDOsVOhU1/s1600/Screenshot+from+2016-06-12+16%253A43%253A49.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7H6r9coay7vlDG0zEqdnns6XXJYRl5weGhC6sLPhxCbRliWik3tu6l9I52h4A2foK5sh8iflathVB9uJUXcuPsfr78rs14l1SNDywj8LEfNqyNwEf7ONEhkjDPD3aVIizxcQcDOsVOhU1/s1600/Screenshot+from+2016-06-12+16%253A43%253A49.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><br /></td></tr>
</tbody></table>
As you can see, there is some logic behind the "grouping" of each set of syscalls. For example, the sixth table (above) contains only syscalls relating to memory management (although, admittedly, most tables are more loosely cobbled together).<br />
<br />
Finally, let's take a look at a simple syscall which <i>must</i> perform validation in order to function correctly. A good candidate would be a syscall which receives a pointer as an argument, and subsequently writes data to that pointer. Obviously, this is incredibly dangerous, and would therefore require extra validation to make sure the pointer is strictly within the memory regions belonging to the QSEE application.<br />
<br />
Digging through the widevine application, we find the following syscall:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbQcf8dwbRD1fcOsUaMln3hvVhWvQ4IZ6KlGBXlnJUjuxEEJBIeDmTbKuZ1oVVdVA2OxZMZhJfskOWwA-C9LHqqRP0tX2RuV5hkmwfphEKFUdXBLYQNCegx3sWhMP5Yfjxd8bXr0LnHMrr/s1600/Screenshot+from+2016-06-12+17%253A07%253A07.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbQcf8dwbRD1fcOsUaMln3hvVhWvQ4IZ6KlGBXlnJUjuxEEJBIeDmTbKuZ1oVVdVA2OxZMZhJfskOWwA-C9LHqqRP0tX2RuV5hkmwfphEKFUdXBLYQNCegx3sWhMP5Yfjxd8bXr0LnHMrr/s1600/Screenshot+from+2016-06-12+17%253A07%253A07.png" /></a></div>
This syscall receives four arguments:<br />
<ul>
<li>A pointer to a "cipher" object, which has previously been initialized by calling "<i>qsee_cipher_init</i>"</li>
<li>The type of parameter which is going to be retrieved from the cipher object</li>
<li>The address to which the read parameter will be written</li>
<li>An unknown argument</li>
</ul>
Of course, QSEE applications always play nice and set the output pointer to a sensible address, but what's actually going on under the hood in the TrustZone kernel? Well, we now know enough to pop the literary hood and check out for ourselves. Going through <i>app_syscall_handler'</i>s switch-case, we find the syscall table and offset of the kernel implementation of "<i>qsee_cipher_get_param</i>", leading us to the actual implementation of <i>qsee_cipher_get_param</i>:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_iI3G0sf9YGMoXxkpWnBHrBYHY34_V7E5ixLvuR9QmhK-sCxIPLAeCJezW2Q4-C5EWHqDMKNoluNskjk5uOJ9FvMeNPXwwCtszM10WkQpMTRHsSter7Xh8w1oSWaXv18oL3YJLpVN3Ozg/s1600/qsee_cipher_get_param_.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_iI3G0sf9YGMoXxkpWnBHrBYHY34_V7E5ixLvuR9QmhK-sCxIPLAeCJezW2Q4-C5EWHqDMKNoluNskjk5uOJ9FvMeNPXwwCtszM10WkQpMTRHsSter7Xh8w1oSWaXv18oL3YJLpVN3Ozg/s1600/qsee_cipher_get_param_.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHx0zdgbO24x4CHUeda8xpo0g3mB9xjfut19G4YqZVMujaOWL0QDRaQMndFMwc59DG0UlRwgVevxnmU9xBWEd8pyNa5DENDyabATTdwX626jHK0zBy5yWMXbM61DrCm4N3cM639SvOurTN/s1600/qsee_cipher_get_param_.png" imageanchor="1"><br /></a></div>
This is our lucky day! Apparently the TrustZone kernel blindly trusts nearly all the parameters passed in by the user. Although the function does perform some sanity checks to make sure the given pointers are not NULL and the param_type is within the allowed range, it automatically trusts the user-supplied "output" argument. More importantly, we can see that if we use the parameter type 3, the function will write a single byte from our cipher to the supplied pointer! <br />
<br />
Note that this was more than just a stroke of luck - taking a peek at the implementation of all the other syscalls reveals that the TrustZone kernel does not perform <i><u>any</u></i> validation on QSEE-supplied arguments (more specifically, it freely uses any given pointers), meaning that at the time <u>all</u> syscalls were vulnerable.<br />
<br />
For the sake of our exploit, we'll stick to <i>qsee_cipher_get_param</i>, since we've already started reviewing it.<br />
<br />
<h2>
Full Read-Write</h2>
<br />
As always, before we start writing an exploit, let's try and improve our primitives. This is nearly always worth our while; the more time we spend on improving the primitives, the cleaner and more robust our exploit will be. We might even end up saving time in the long-run.<br />
<br />
Right now we have an uncontrolled-write primitive - we can write some <u>uncontrolled</u> data from our cipher object to a <u>controlled</u> memory location. Of course, it would be much easier if we were able to control the written data as well.<br />
<br />
Intuitively, since "<i>qsee_cipher_get_param</i>" is used to read a parameter from a cipher object, it stands to reason that there would be a matching function which is used to set the parameter. Indeed, searching for "<i>qsee_cipher_set_param</i>" in the widevine application confirms our suspicion:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEaCFJwl5351e5L3CcuxApxa_nO1EXhy-55ZIWf75GYPMq3jELiZiPyXBhD6t3Z6K_0MtQhSU7jMcTAoQoKh6dZIUdvwdANHnuFIf4MRZ_iR44i5a4Hj4ww2VYmlrYeGLmyByVIG0etNRn/s1600/Screenshot+from+2016-06-12+17%253A59%253A26.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEaCFJwl5351e5L3CcuxApxa_nO1EXhy-55ZIWf75GYPMq3jELiZiPyXBhD6t3Z6K_0MtQhSU7jMcTAoQoKh6dZIUdvwdANHnuFIf4MRZ_iR44i5a4Hj4ww2VYmlrYeGLmyByVIG0etNRn/s1600/Screenshot+from+2016-06-12+17%253A59%253A26.png" /></a></div>
<br />
Let's take a look at the implementation of this syscall:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiO4y8_-gpGzZ2Ij-Pr98X1Q8_kSAOyPI-_tnE3mhFxerOxeKgMHifbmVzMk8AN7Tnb422MGG22Iy9Nd9r256Z4B9Kt4TF8i_Jk7rIp2BiwredU-tzsWtWld1X0ZZMvuaJm8KYfwVh-yAjU/s1600/qsee_cipher_set_param.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiO4y8_-gpGzZ2Ij-Pr98X1Q8_kSAOyPI-_tnE3mhFxerOxeKgMHifbmVzMk8AN7Tnb422MGG22Iy9Nd9r256Z4B9Kt4TF8i_Jk7rIp2BiwredU-tzsWtWld1X0ZZMvuaJm8KYfwVh-yAjU/s1600/qsee_cipher_set_param.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgeNNoJVUF1YaMD61P1yyy333x9cr88oQLEN2msHrGGmP0G6_5906RDkmhRezGDJ9aoQMms_ng8nEyhQzf9e2KW-Vw_1NK6ibL4wKlJkNNVe-e2SVLF08fpD1Ldypikfa9qsUM_QTHUAFsm/s1600/qsee_cipher_set_param_smaller.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
Great!<br />
<br />
It looks like we can set the parameter's value by using the same <i>param_type</i> value (3), and supplying a pointer to a controlled memory region within QSEE which will contain the byte we would later like to write. The TrustZone kernel will happily store the value we supplied in the <i>cipher</i> object, allowing us to later write that value to any address by calling <i>qsee_cipher_get_param</i> with our target pointer.<br />
<br />
Putting this together, we now have relatively clean write-what-where primitive. Here's a run-down of our new primitive: <br />
<ul>
<li>Initialize a cipher object using <i>qsee_cipher_init</i></li>
<li>Allocate a buffer in QSEE</li>
<li>Write the wanted byte to our allocated QSEE buffer</li>
<li>Call <i>qsee_cipher_set_param</i> using our QSEE-allocated buffer as the param_value argument</li>
<li>Call <i>qsee_cipher_get_param</i>, but supply the target address as the <i>output</i> argument</li>
</ul>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlJqC6LDbeivEc27133hCXm52oK8NWSq6gjyCT41jtuYj4mHBdBxuKmBVqay6Tg_qHuagBL5NoG59QUr0MtVGGCbcLEzAZ1Mkv4jeaGdhQkSkJEulOpsZFt7bLTzejDdC2ZvARryPAQ4RB/s1600/Screenshot+from+2016-06-12+20%253A34%253A04.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlJqC6LDbeivEc27133hCXm52oK8NWSq6gjyCT41jtuYj4mHBdBxuKmBVqay6Tg_qHuagBL5NoG59QUr0MtVGGCbcLEzAZ1Mkv4jeaGdhQkSkJEulOpsZFt7bLTzejDdC2ZvARryPAQ4RB/s1600/Screenshot+from+2016-06-12+20%253A34%253A04.png" /></a></div>
<br />
<ul>
</ul>
You might have also noticed that we could use the inverse of this in order to get an arbitrary read primitive. All we would need to do is call <i>qsee_cipher_set_param</i> supplying the address we'd like to read as the <i>param_value</i> argument - this'll cause the TrustZone kernel to read the value at that address and store it in our cipher object. Then, we can simply retrieve that value by calling <i>qsee_cipher_get_param</i>.<br />
<br />
<h2>
Writing an Exploit</h2>
<br />
Using the primitives we just crafted, we finally have full read-write access to the TrustZone kernel. All that's left is to achieve code-execution within the TrustZone kernel in a controllable way.<br />
<br />
The first obvious choice would be to write some shellcode into the TrustZone kernel's code segments and execute it. However, there's a tiny snag - the TrustZone kernel's code segments in newer devices are protected by special memory protection units (called XPUs), which prevent us for directly modifying the kernel's code (along with many different protected memory regions). We <i>could</i> still modify the kernel's code (more information in the next blog post!), but it would be much harder...<br />
<br />
...However, we <i>have</i> already come across a piece of dynamically allocated code in the "Secure World" - the QSEE applications themselves!<br />
<br />
So here's a plan - if we could ignore the access-protection bits on the code pages of the QSEE applications (since they are all marked as read-execute), we should be able to directly modify them from the context of the TrustZone kernel. Then, we could simply jump to the our newly-created code from the context of the kernel in order to execute any piece of code we'd like.<br />
<br />
Luckily, ignoring the access-protection bits can actually be done without modifying the translation table at all, by using a convenient feature of the ARM MMU called
"domains".<br />
<br />
In the ARM translation table, each entry has
a field which lists its permissions, as well as a 4-bit field denoting the
"domain" to which the translation belongs. <br />
<br />
Within the ARM MMU, there is a register called the <b>DACR </b>(<b>D</b>omain <b>A</b>ccess <b>C</b>ontrol <b>R</b>egister).
This 32-bit register has 16 pairs of bits, one pair for each domain,
which are used to specify whether faults for read access, write access,
both, or neither, should be generated for translations of the given
domain.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjg1-0p4l3sPTunD3qpkUPVx5R7ReJAzl86MxOwkCgwY0tzy4rL4va7z8KBA9SlKyQbsOXS4iI1Q8oRlRimk6ZvCzbkM4crGYbcfnP7_vY2UoQQMDAd1-quigQmqPhao_enc0gwl2oQk3oT/s1600/Screenshot+from+2015-08-08+22%253A11%253A54.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="104" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjg1-0p4l3sPTunD3qpkUPVx5R7ReJAzl86MxOwkCgwY0tzy4rL4va7z8KBA9SlKyQbsOXS4iI1Q8oRlRimk6ZvCzbkM4crGYbcfnP7_vY2UoQQMDAd1-quigQmqPhao_enc0gwl2oQk3oT/s640/Screenshot+from+2015-08-08+22%253A11%253A54.png" width="640" /></a></div>
<br />
Whenever
the processor attempts to access a given memory address, the MMU first
checks if the access is possible using the access permissions of the
given translation for that address. If the access is allowed, no fault
is generated.<br />
<br />
Otherwise, the MMU checks if the bits
corresponding to the given domain in the DACR are set. If so, the fault
is suppressed and the access is allowed.<br />
<br />
This means
that simply setting the DACR's value to 0xFFFFFFFF will cause
the MMU to enable access to any mapped memory address, for both read and
write access, without generating a fault (and more importantly, without
having to modify the translation table).<br />
<br />
Moreover, the TrustZone kernel already has a piece of code that is used to set the value of the DACR, which we can simply call using our own value (0xFFFFFFFF) in order to fully set the DACR.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEheLu3ClV-w0O6nn7tYaRmWP15Cl6x-bW9gt2r33nj-1AYLwHegOZctGNPjituhIdTe7SiebAkGSMokY3TGpUcwFXO4D3xbJeRitfnxjxwSkXE-HMbVRjSAV1sl2NqwCyWSTLpMk8ZUmLXk/s1600/Screenshot+from+2016-06-13+00%253A24%253A43.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEheLu3ClV-w0O6nn7tYaRmWP15Cl6x-bW9gt2r33nj-1AYLwHegOZctGNPjituhIdTe7SiebAkGSMokY3TGpUcwFXO4D3xbJeRitfnxjxwSkXE-HMbVRjSAV1sl2NqwCyWSTLpMk8ZUmLXk/s1600/Screenshot+from+2016-06-13+00%253A24%253A43.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">TrustZone kernel function which sets the DACR</td></tr>
</tbody></table>
<br />
All that said and done, we're still missing a key component in our exploit! All we have right now is read/write access to the TrustZone kernel, we still need a way to execute arbitrary functions within the TrustZone kernel and restore execution. This would allow us to change the DACR using the gadget above and subsequently write and execute shellcode in the "Secure World".<br />
<br />
<h2>
Hijacking Syscalls</h2>
<br />
As we've seen, most QSEE system-calls are invoked <u>indirectly</u> by using a set of globally-stored pointers, each of which pointing to a corresponding system-call table.<br />
<br />
While the system-call tables themselves are located in a memory region that is protected by an XPU, the <u>pointers</u> to these tables are not protected in any way! This is because they are only populated during runtime, and as such must reside in a modifiable memory region.<br />
<br />
This little tidbit actually makes it much simpler for us to hijack code execution in the kernel in a controllable manner!<br />
<br />
All we need to do is allocate <u>our own</u> "fake" system-call table. Our table would be identical to the real system-call table, apart from a single "poisoned" entry, which would point to a function of our choice (instead of pointing to the original syscall handler).<br />
<br />
It should be noted that since we don't want to cause any adverse effects for other QSEE applications, it is important that we choose to modify an entry corresponding to an unused (or rarely used) system call.<br />
<br />
Once we've crafted the "fake" syscall table, we can simply use our write primitive in order to modify the global syscall table pointer to point to our newly created "fake" table.<br />
<br />
Then, whenever the "poisoned" system-call is invoked from QSEE, our function will be executed within the context of the TrustZone kernel! Not only that, but <i>app_syscall_handler </i>will also conveniently make sure the return value from our executed code will be returned to QSEE upon returning from the SVC call.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDTC6Jyb1cfm99Ra2eh2p4Hq6iirLNitPIecUoGloD_qE-Bw5U0WB7QxjadOouZTOycSwzSoOmoLBsQwS5ICQo-lCYeE11XNXB5i3itfCx7Mr82C0Bf1qbskvBeWR2GL9anGCErOtNUKLg/s1600/Screenshot+from+2016-06-13+01%253A24%253A19.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDTC6Jyb1cfm99Ra2eh2p4Hq6iirLNitPIecUoGloD_qE-Bw5U0WB7QxjadOouZTOycSwzSoOmoLBsQwS5ICQo-lCYeE11XNXB5i3itfCx7Mr82C0Bf1qbskvBeWR2GL9anGCErOtNUKLg/s1600/Screenshot+from+2016-06-13+01%253A24%253A19.png" /></a></div>
<br />
<br />
<h2>
Putting it all together</h2>
<br />
By now we have all the pieces we need to write a simple exploit which writes a chunk of shellcode in the "Secure World", executes that shellcode in the context of the TrustZone kernel, and restores execution.<br />
<br />
Here's what we need to do:<br />
<ul>
<li>Allocate a "fake" syscall table in QSEE</li>
<li>Use the write primitive to overwrite the syscall table pointer to point to our crafted "fake" syscall table</li>
<li>Set the single "poison" syscall entry in the "fake" syscall table to
point to the DACR-modifying function in the TrustZone kernel</li>
<li>Invoke the "poison" syscall in order to call the DACR-modifying function in the TrustZone kernel - thus setting the DACR to 0xFFFFFFFF</li>
<li>Use the write gadget to write our shellcode directly to a code page in QSEE belonging to our QSEE application</li>
<li>Invalidate the instruction cache (to avoid conflicts with the newly written code)</li>
<li>Set the single "poison" syscall entry in the "fake" syscall table to
point to the written shellcode</li>
<li>Invoke the "poison" syscall in order to jump to our newly-written shellcode from the context of the TrustZone kernel!</li>
</ul>
Here's a small illustration detailing all of these steps:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4_CIFtrn-yDUw0JVpLOx5L6oTVas0wSedu4sS_dVx1DhdhOzjFl_ypmzmUMkypq1ydJ0O7RlxwXBDPMfy1mSabIQ_uu5oY0KQtb7oBm6E_RpjK5ugj8aA7ZhKmpEZjke5xSw3Sr7afCMf/s1600/Screenshot+from+2016-06-15+14%253A20%253A39.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4_CIFtrn-yDUw0JVpLOx5L6oTVas0wSedu4sS_dVx1DhdhOzjFl_ypmzmUMkypq1ydJ0O7RlxwXBDPMfy1mSabIQ_uu5oY0KQtb7oBm6E_RpjK5ugj8aA7ZhKmpEZjke5xSw3Sr7afCMf/s1600/Screenshot+from+2016-06-15+14%253A20%253A39.png" /></a></div>
<br />
<h2>
Playing With The Code</h2>
<br />
As always, the full exploit source code is available here:<br />
<br />
<a href="https://github.com/laginimaineb/cve-2016-2431">https://github.com/laginimaineb/cve-2016-2431</a><br />
<br />
The exploit builds upon the <a href="https://bits-please.blogspot.com/2016/05/qsee-privilege-escalation-vulnerability.html">previous QSEE exploit</a>, in order to achieve QSEE code-execution. If you'd like to play around with it, you might want to use the following two useful functions:<br />
<ul>
<li><i>tzbsp_execute_function </i>- calls the given function with the given arguments within the context of the TrustZone kernel.</li>
</ul>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTtRWqKOCf8ad8Fw2pDE8Wokm_Pi4xPXkSVlfj9RFTeG4oP1GC_XE0e0eyBs5QULlWyjtpmqfkKnRrCRRm5gRQFOUqNtEF3feU6waIBGv8jfhoyhtxLxauVU_N8s29DNaCHfp2O7LnPVZb/s1600/Screenshot+from+2016-06-13+01%253A45%253A07.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTtRWqKOCf8ad8Fw2pDE8Wokm_Pi4xPXkSVlfj9RFTeG4oP1GC_XE0e0eyBs5QULlWyjtpmqfkKnRrCRRm5gRQFOUqNtEF3feU6waIBGv8jfhoyhtxLxauVU_N8s29DNaCHfp2O7LnPVZb/s1600/Screenshot+from+2016-06-13+01%253A45%253A07.png" /></a></div>
<br />
<ul>
<li><i>tzbsp_load_and_exec_file</i> - Loads the shellcode from a given file and executes it within the context of the TrustZone kernel.</li>
</ul>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfqiYVHF0ylO0nlp_OyrXSOm_ODrXU6wuOXlysLydmO8HEO5m0pDh4yEiRi2JJXNdmD2i68XqcdnLZ7-0kLQa3BDNvVb4evrtndz4Sei7A5d157X5_OeD1EG0_2bbqzZQuCfhM3vxQZvfF/s1600/Screenshot+from+2016-06-13+01%253A46%253A26.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfqiYVHF0ylO0nlp_OyrXSOm_ODrXU6wuOXlysLydmO8HEO5m0pDh4yEiRi2JJXNdmD2i68XqcdnLZ7-0kLQa3BDNvVb4evrtndz4Sei7A5d157X5_OeD1EG0_2bbqzZQuCfhM3vxQZvfF/s1600/Screenshot+from+2016-06-13+01%253A46%253A26.png" /></a></div>
<br />
I've also included a small shell script called "build_shellcode.sh", which can be used to build the shellcode supplied in the file "shellcode.S" and write it into a binary blob (which can then be loaded and executed using the function above).<br />
<br />
Have fun!<br />
<br />
<h2>
Timeline</h2>
<h2>
</h2>
<h2>
</h2>
<ul>
<li>13.10.2015 - Vulnerability disclosed and minimal PoC sent</li>
<li>15.10.2015 - Initial response from Google</li>
<li>16.10.2015 - Full exploit sent to Google</li>
<li>30.03.2016 - CVE assigned</li>
<li>02.05.2016 - Issue patched and released in the Nexus public bulletin</li>
</ul>
As far as I know, this vulnerability has been present in <u>all devices and all versions</u> of QSEOS, until it was finally patched in 02.05.2016. This means that effectively up to that point, obtaining code-execution within QSEE was equivalent to having code-execution within the TrustZone kernel (i.e., fully controlling nearly every aspect of the device).<br />
<br />
As there was no public research into QSEE up to that point, this issue wasn't discovered. Hopefully in the future further research into QSEE and TrustZone in general will help uncover similar issues and make the security boundary between QSEOS and QSEE stronger.<br />
<ul>
</ul>
laginimainebhttp://www.blogger.com/profile/00867710965331025912noreply@blogger.com247tag:blogger.com,1999:blog-2029700426505953971.post-32445100750012510772016-05-05T22:11:00.001+03:002016-05-06T02:01:26.514+03:00War of the Worlds - Hijacking the Linux Kernel from QSEEAfter seeing a full QSEE vulnerability and exploit in the <a href="http://bits-please.blogspot.co.il/2016/05/qsee-privilege-escalation-vulnerability.html">previous blog post</a>, I thought it might be nice to see some QSEE shellcode in action.<br />
<br />
As we've previously discussed, QSEE is extremely privileged - not only can it interact directly with the TrustZone kernel and access the hardware-secured TrustZone file-system (SFS), but it also has some direct form of access to the system's memory.<br />
<br />
In this blog post we'll see how we can make use of this direct memory access in the "Secure World" in order to hijack the Linux Kernel running in the "Normal World", without even requiring a kernel vulnerability.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaL_ge4-IJ7fQcVPz8TFGlUXSFAlPpRld2_6LvARjD5Vxek_oE1jFtSt4F669tn9TX-TXVoZ6RNDo5cp768gEt5t4h5XNGCYRQVFUMrR7El0H11hlxBaDgmPT47UWaugFsQbDTAf3vmuKg/s1600/war_of_worlds.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaL_ge4-IJ7fQcVPz8TFGlUXSFAlPpRld2_6LvARjD5Vxek_oE1jFtSt4F669tn9TX-TXVoZ6RNDo5cp768gEt5t4h5XNGCYRQVFUMrR7El0H11hlxBaDgmPT47UWaugFsQbDTAf3vmuKg/s400/war_of_worlds.png" width="381" /></a></div>
<br />
<br />
<a name='more'></a><br />
<h2>
Interacting with QSEE</h2>
<h2>
</h2>
As we've seen in the previous blog post, when a user-space Android application would like to interact with a trustlet running in QSEE, it must do so by using a special Linux Kernel device, "qseecom". This device issues SMC calls which are handled by QSEOS, and are passed on to the requested trustlet in order to be handled.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJQBp748kMwDuCxp46dlApsOicWtdGQNxv3PV_PpW_KWeKjLVfzri0gxvUaNAtQwdhVVamN4MrygEpI6lRmzmtsBjw-U9OVR1xOwDlNPS7M_XFnvmgqp9qjv5ViOIGuVJMgyCjA95VpnXd/s1600/Screenshot+from+2016-04-29+01%253A09%253A43.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJQBp748kMwDuCxp46dlApsOicWtdGQNxv3PV_PpW_KWeKjLVfzri0gxvUaNAtQwdhVVamN4MrygEpI6lRmzmtsBjw-U9OVR1xOwDlNPS7M_XFnvmgqp9qjv5ViOIGuVJMgyCjA95VpnXd/s1600/Screenshot+from+2016-04-29+01%253A09%253A43.png" /></a></div>
Each command issued to a trustlet has a pair of associated input and output buffers, which are usually used to convey all the information to and from the "Normal World" and the trustlet.<br />
<br />
However, there are some special use-cases in which a faster mode of communication is required - for example, when decrypting (or encrypting) large DRM-protected media files, the communication cost must be as small as possible in order to enable "smooth" playback.<br />
<br />
Moreover, some devices include trustlets which are meant to assure the device's integrity (mostly in corporate settings). For example, Samsung provides "<b>T</b>rustZone-based <b>I</b>ntegrity <b>M</b>easurement <b>A</b>rchitecture" (<b>TIMA</b>) - a framework used to assure device integrity. According to Samsung, TIMA performs (among other things) periodic measurements of the "Normal World" kernel, and verifies that they match the original factory kernel. <br />
<br />
So... Trustlets need fast communication with the "Normal World" and also need some ability to inspect the system's memory - sounds dangerous! Let's take a closer look.<br />
<br />
<h2>
Sharing (Memory) is Caring</h2>
<br />
Continuing our research on the "widevine" trustlet, let's take a look at the command used to DRM-encrypt a chunk of memory:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiU4UjPY0JdcjWdw6T1PW4K2es5kh7SX5g00XQuHK6lBQZ4z0Qok8QPPvP65h94NcP1mTP0C4N-Ii0q3-KoM9QKf2DRN9i-i7S7PL7MCkUT4ySjj83YoHGnBAGLhhrNsL7YrUxn7ji98tC5/s1600/Screenshot+from+2016-05-05+16%253A28%253A21.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiU4UjPY0JdcjWdw6T1PW4K2es5kh7SX5g00XQuHK6lBQZ4z0Qok8QPPvP65h94NcP1mTP0C4N-Ii0q3-KoM9QKf2DRN9i-i7S7PL7MCkUT4ySjj83YoHGnBAGLhhrNsL7YrUxn7ji98tC5/s1600/Screenshot+from+2016-05-05+16%253A28%253A21.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9lCM5Yu0iw5q5wq5DSvjRzLP6LEYQN5solvMSZZu0_Rd_V6euXykEsnCOZSCnHze4SBAhvQ8KoWOVb9t88pLgHx7MpM4PfeD8-KPYFyLdmm4nUcH0h958nahoGVgHKsraAz9IsrLmWzZD/s1600/Screenshot+from+2016-05-05+15%253A58%253A36.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<br />
As we can see above, the function receives two pointers denoting the "input" and "output" buffers, respectively. These can be any arbitrary buffers provided by the user, so it stands to reason that some preparation would be needed in order to access them. Indeed, we can see that the preparation is done by calling "cacheflush_register", and, once the encryption process is done, the buffers are released by calling "cacheflush_deregister".<br />
<br />
Upon closer inspection, "cacheflush_register" and "cacheflush_deregister" are simple wrappers around a couple of QSEE syscalls (each):<br />
<br />
<table align="center"><tbody>
<tr><th style="border-bottom: 1pt solid black; text-align: center;">cacheflush_register</th><th style="border-bottom: 1pt solid black; text-align: center;"> cacheflush_deregister </th><th style="text-align: center;"><br /></th></tr>
<tr class="alt"><td style="text-align: center;">qsee_register_shared_buffer</td><td style="text-align: center;">qsee_prepare_shared_buf_for_nosecure_read</td><td style="text-align: center;"><br /></td></tr>
<tr><td style="text-align: center;">qsee_prepare_shared_buf_for_secure_read</td><td style="text-align: center;">qsee_deregister_shared_buffer</td><td style="text-align: center;"><br /></td></tr>
</tbody></table>
<br />
So what do these syscalls do?<br />
<br />
Looking at the relevant handling code in QSEOS reveals that these names are a little misleading - in fact, "qsee_prepare_shared_buf_for_secure_read" merely invalidates the given range in data cache (so that QSEE will observe the updated data), and similarly "qsee_prepare_shared_buf_for_nosecure_read" clears the given range from the data cache (so that the "Normal World" will see the changes made by QSEE). <br />
<br />
As for "qsee_register_shared_buffer" - this syscall is used to actually map the given ranges into QSEE. Let's see what it does:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjAnHqMEsKPzHP5npr8Xahurl_Yp5Lhm3iqyxTQCCuG7ACT-u9h2v0PMDNJKBigamgrwgblhFoblBwarUpsZeJV9vw7HONDreTLVdm2RzpWZuvxyuf3pwtsBICPvTQ6V990YiwpWj89XYdF/s1600/Screenshot+from+2016-05-05+19%253A44%253A43.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjAnHqMEsKPzHP5npr8Xahurl_Yp5Lhm3iqyxTQCCuG7ACT-u9h2v0PMDNJKBigamgrwgblhFoblBwarUpsZeJV9vw7HONDreTLVdm2RzpWZuvxyuf3pwtsBICPvTQ6V990YiwpWj89XYdF/s1600/Screenshot+from+2016-05-05+19%253A44%253A43.png" /></a></div>
<br />
After some sanity checks, the function checks whether the given memory region is within the "Secure World". If that's the case, it could be that the trustlet is trying to attack the TrustZone kernel by mapping-in and modifying memory regions used by TZBSP or QSEOS. Since that would be extremely dangerous, only a select few (six) specific regions within the "Secure World" can be mapped into QSEE. If the given address range is not within any of these special "tagged" regions, the operation is denied.<br />
<br />
However - for any address in the "Normal World", there are no extra checks made! This means that QSEOS will happily allow us to use "qsee_register_shared_buffer" in order to map in any physical address in the "Normal World".<br />
<br />
...Are you pondering what I'm pondering?<br />
<br />
<h2>
Hijacking the Linux Kernel</h2>
<br />
Since QSEE has read-write access to all of the "Normal World"'s memory (all it needs to do is ask), we should theoretically be able to locate the running Linux Kernel in the "Normal World" directly in physical memory and inject code into it.<br />
<br />
As a fun exercise, let's create a QSEE shellcode that doesn't require any kernel symbols - this way it can be used in any QSEE context in order to locate and hijack the running kernel.<br />
<br />
Recall that after booting the device, the bootloader uses the data specified in the Android boot image in order to extract the Linux Kernel into a given physical address and execute it: <br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXlhcaj7k6nDzYYjmvAnODdSTgxZregpaG5_Eb948LB7CrUmylA09xaPeWI7lj_l3-C3aT4oAPnYmCxogrRRfXr-WVHViI1LjVKaf9XKHHFxnIMf-GJ2hKkRbnvMFeoPM_Bgzca4O0H0ef/s1600/Screenshot+from+2016-05-05+20%253A23%253A47.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXlhcaj7k6nDzYYjmvAnODdSTgxZregpaG5_Eb948LB7CrUmylA09xaPeWI7lj_l3-C3aT4oAPnYmCxogrRRfXr-WVHViI1LjVKaf9XKHHFxnIMf-GJ2hKkRbnvMFeoPM_Bgzca4O0H0ef/s1600/Screenshot+from+2016-05-05+20%253A23%253A47.png" /></a></div>
<br />
The physical load address of the Linux Kernel is then available to any process via the world-readable file /proc/iomem:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4chcHNGQrCYxUTLxD56EgKtwf_y6S3oIDZ1OMHtJ3dY3O1q2e01Y8VH-tZ8qWWtE1tYUwcderPNvtKiJ8T98qjGBMqx4JirSwR0CVPwBSr1cwUhzJzHuB9we4CEbIovjvnsYFAgq2kadD/s1600/Screenshot+from+2016-05-05+20%253A51%253A08.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4chcHNGQrCYxUTLxD56EgKtwf_y6S3oIDZ1OMHtJ3dY3O1q2e01Y8VH-tZ8qWWtE1tYUwcderPNvtKiJ8T98qjGBMqx4JirSwR0CVPwBSr1cwUhzJzHuB9we4CEbIovjvnsYFAgq2kadD/s1600/Screenshot+from+2016-05-05+20%253A51%253A08.png" /></a></div>
<br />
However, simply knowing where the kernel is loaded does not absolve us from the need to find kernel symbols - there is a large amount of kernel images and an equally large amount of symbols per kernel. As such, we need some way to find the all of the kernel's symbols dynamically using the running kernel's memory. However, all is not lost - remember that the Linux Kernel keeps a list of all kernel symbols internally (!), and allows kernel modules to lookup these symbols using a special lookup function - "kallsyms_lookup_name". So how does this work?<br />
<br />
<a href="http://bits-please.blogspot.co.il/2015/08/effectively-bypassing-kptrrestrict-on.html">As we've previously seen</a> - the names in the kernel's symbol table are compressed using a 256-entry huffman coding generated at build time. The huffman table is stored within the kernel's image, alongside the descriptors for each symbol denoting the indices in the huffman table used to decompress it's name. And, of course, the actual addresses for all of the symbols are similarly stored in the kernel's image.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiV973ycrtH4SiuXrtKzO0PRTRw2EDKHmQoGyvyPGksKwiCZ-BRueqmx2RoaEkjG9Bxukc0bQe6Evn0SKVxFJk398topss1eCmhRKTGvhg5VDA8hEEHP2KStpfY_KU25wMNWVCNeD_o7fCJ/s1600/Screenshot+from+2016-05-05+21%253A15%253A50.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiV973ycrtH4SiuXrtKzO0PRTRw2EDKHmQoGyvyPGksKwiCZ-BRueqmx2RoaEkjG9Bxukc0bQe6Evn0SKVxFJk398topss1eCmhRKTGvhg5VDA8hEEHP2KStpfY_KU25wMNWVCNeD_o7fCJ/s1600/Screenshot+from+2016-05-05+21%253A15%253A50.png" /></a></div>
<br />
<br />
In order to access all the information in the symbol table, we must first find it within the kernel's image.<br />
<br />
As luck would have it, the first region of the symbol table - the "Symbol Address Table", always begins with two pointers to the kernel's virtual load address (which can be easily calculated from the kernel's physical load address since there's no KASLR). Moreover, the symbol addresses in the table are monotonically <span class="_Tgc">nondecreasing addresses within the kernel's virtual address range - a fact which we can use to confirm our suspicion whenever we find two such consecutive pointers to the kernel's virtual load address.</span><br />
<span class="_Tgc"><br /></span>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDlUR6lsAwinNrj41Vc62tF68qbOd_41Ee7k54PN_Jzx2AfPwe7c8oPqRV56ggMVxGjbU1VBOOCyUi3oWj7w-R6-x5hGCfTqDMTJAgzB8UyTsjD32BEeuGkZPcS8xN09zR02vrCUqV2-1T/s1600/Screenshot+from+2015-08-25+16%253A45%253A56.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDlUR6lsAwinNrj41Vc62tF68qbOd_41Ee7k54PN_Jzx2AfPwe7c8oPqRV56ggMVxGjbU1VBOOCyUi3oWj7w-R6-x5hGCfTqDMTJAgzB8UyTsjD32BEeuGkZPcS8xN09zR02vrCUqV2-1T/s1600/Screenshot+from+2015-08-25+16%253A45%253A56.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Symbol Address Table</td></tr>
</tbody></table>
<span class="_Tgc"><br /></span>
<span class="_Tgc">Now that we can find the symbol table within the kernel's image, all we need to do is implement the decompression scheme in order to be able to iterate over it and lookup any symbol. Great!</span><br />
<span class="_Tgc"></span><br />
<br />
Using the method above to find the kernel's symbol table, we can now locate and hijack any kernel function from QSEE. Following the tradition from the <a href="http://bits-please.blogspot.co.il/2015/08/android-linux-kernel-privilege.html">previous kernel exploits</a>, let's hijack an easily accessible function pointer from a very rarely-used network protocol - PPPOLAC.<br />
<br />
The function pointers relating to this protocol are stored in the following kernel structure:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgicG5o3wJMoFJjDGYmfP8AbzIl9hBLdE6lgL-ZEtfVq8MLRq7KxK9PPyAsX0dR1pmrflCsWztsP1mAnNJbmoSEUQH_cBTh95271J1OfSr7cza7QV1qJcmmvr9r2SQ0j3Mh4eWLQE2sA9q/s1600/Screenshot+from+2015-08-16+02%253A07%253A43.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgicG5o3wJMoFJjDGYmfP8AbzIl9hBLdE6lgL-ZEtfVq8MLRq7KxK9PPyAsX0dR1pmrflCsWztsP1mAnNJbmoSEUQH_cBTh95271J1OfSr7cza7QV1qJcmmvr9r2SQ0j3Mh4eWLQE2sA9q/s1600/Screenshot+from+2015-08-16+02%253A07%253A43.png" /></a></div>
Overwriting the "release" pointer in this structure would cause the kernel to execute our crafted function pointer whenever a PPPOLAC socket is closed.<br />
<br />
<h2>
Putting it all together </h2>
<br />
Now that we have all the pieces, all we need to do to gain code execution within the Linux Kernel is to:<br />
<ul>
<li><a href="http://bits-please.blogspot.co.il/2016/05/qsee-privilege-escalation-vulnerability.html">Achieve QSEE code execution</a></li>
<li>Map-in all the kernel's memory in QSEE using "qsee_register_shared_buffer"</li>
<li>Find the kernel's symbol table</li>
<li>Lookup the "pppolac_proto_ops" symbol in the symbol table</li>
<li>Overwrite any function pointer to our user-supplied function address </li>
<li>Flush the changes made in QSEE using "qsee_prepare_shared_buf_for_nosecure_read"</li>
<li>Cause the kernel to call our user-supplied function by using a PPPOLAC socket</li>
</ul>
I've written some QSEE code which performs all of these steps and exports an easy-to-use interface to allow kernel code execution, like so:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6Vi2aVMfcrYPAsQZPMrZn2t8GdM0uNDSTEOZAI4R45_1_ot2TYRr-3bYrHvMXIBz7T7wMnq7f018Fv9BxXrvNodrWuMD_FNk0fvfrok8ugiitw_oSe2Hg2FFAjUF6IoK-V13XH-Ti4TJ8/s1600/Screenshot+from+2016-05-05+21%253A55%253A09.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6Vi2aVMfcrYPAsQZPMrZn2t8GdM0uNDSTEOZAI4R45_1_ot2TYRr-3bYrHvMXIBz7T7wMnq7f018Fv9BxXrvNodrWuMD_FNk0fvfrok8ugiitw_oSe2Hg2FFAjUF6IoK-V13XH-Ti4TJ8/s1600/Screenshot+from+2016-05-05+21%253A55%253A09.png" /></a></div>
<br />
As always, you can find the full code here:<br />
<br />
<a href="https://github.com/laginimaineb/WarOfTheWorlds">https://github.com/laginimaineb/WarOfTheWorlds</a><br />
<br />
I should note that the code currently only reads memory one DWORD at a time, making it quite slow. I didn't bother to speed it up, but any and all improvements are more than welcome (for example, reading large chunks of memory at a time would be much faster).<br />
<br />
In the next blog post, we'll continue our journey from zero-to-TrustZone, and attempt to gain code execution within the TrustZone kernel.laginimainebhttp://www.blogger.com/profile/00867710965331025912noreply@blogger.com335tag:blogger.com,1999:blog-2029700426505953971.post-2858979035553160922016-05-02T15:23:00.000+03:002016-05-05T14:40:49.998+03:00QSEE privilege escalation vulnerability and exploit (CVE-2015-6639)In this blog post we'll discover and exploit a vulnerability which will allow us to gain code execution within Qualcomm's Secure Execution Environment (QSEE). I've responsibly disclosed this vulnerability to Google and it has been fixed - for the exact timeline, see the "Timeline" section below.<br />
<br />
<h2>
The QSEE Attack Surface</h2>
<h2>
</h2>
As we've seen in the <a href="http://bits-please.blogspot.co.il/2016/04/exploring-qualcomms-secure-execution.html">previous blog post</a>, Qualcomm's TrustZone implementation enables the "Normal World" operating system to load trusted applications (called trustlets) into a user-space environment within the "Secure World", called QSEE.<br />
<br />
This service is provided to the "Normal World" by sending specific SMC calls which are handled by the "Secure World" kernel. However, since SMC calls cannot be invoked from user-mode, all communication between the "Normal World" and a trustlet must pass through the "Normal World" operating system's kernel.<br />
<br />
Having said that, regular user-space processes within the "Normal World" sometimes need to communicate with trustlets which provide specific services to them. For example, when playing a DRM protected media file, the process in charge of handling media within Android, "mediaserver", must communicate with the appropriate DRM trustlet in order to decrypt and render the viewed media file. Similarly, the process in charge of handling cryptographic keys, "keystore", needs to be able to communicate with a special trustlet ("keymaster") which provides secure storage and operation on cryptographic keys.<br />
<br />
So if communicating with trustlets requires the ability to issue SMCs, and this cannot be done from user-mode, then how do these processes actually communicate with the trustlets?<br />
<br />
The answer is by using a Linux kernel device, called "qseecom", which enables user-space processes to perform a wide range of TrustZone-related operations, such as loading trustlets into the secure environment and communicating with loaded trustlets.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqC64vsHsxQxqJ0VWGTWOTQf9Dn0HVy6ebkQ9BAC-9I4361Mr17grQFoMKnz55Imgevy56zKh6s2bKOrxiEIxpq8X_q4K2iiyFwO8jK3ouqGg1AbngWBhAowcSaEejPYWGgwSAayEpjTUA/s1600/Screenshot+from+2016-04-29+01%253A09%253A43.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqC64vsHsxQxqJ0VWGTWOTQf9Dn0HVy6ebkQ9BAC-9I4361Mr17grQFoMKnz55Imgevy56zKh6s2bKOrxiEIxpq8X_q4K2iiyFwO8jK3ouqGg1AbngWBhAowcSaEejPYWGgwSAayEpjTUA/s1600/Screenshot+from+2016-04-29+01%253A09%253A43.png" /></a></div>
<br />
<br />
However! Although necessary, this is very dangerous; communication with TrustZone exposes a large (!) attack surface - if any trustlet that can be loaded on a particular device contains a vulnerability, we can exploit it in order to gain code execution within the trusted execution environment. Moreover, since the trusted execution environment has the ability to map-in and write to all physical memory belonging to the "Normal World", it can also be used in order to infect the "Normal World" operating system's kernel <u>without there even being a vulnerability in the kernel</u> (simply by directly modifying the kernel's code from the "Secure World").<br />
<br />
Because of the dangers outlined above, the access to this device is restricted to the minimal set of processes that require it. A <a href="http://bits-please.blogspot.co.il/2015/08/android-linux-kernel-privilege.html">previous dive</a> into the permissions required in order to access the driver has shown that only four processes are able to access "qseecom":<br />
<ul>
<li>surfaceflinger (running with "system" user-ID)</li>
<li>drmserver (running with "drm" user-ID)</li>
<li>mediaserver (running with "media" user-ID)</li>
<li>keystore (running with "keystore" user-ID)</li>
</ul>
<br />
This means that if we manage to get a hold of any of these four processes, we would then be able to directly attack any trustlet of our choice, directly bypassing the Linux kernel in the process! In fact, this is exactly what we'll do - but we'll get to that later in the series.<br />
<br />
For this blog post, let's assume that we already have code-execution within the "mediaserver" process, thus allowing us to directly focus on the attack surface provided by trustlets. Here's an illustration to help visualise the path of the exploit chain we'll cover during the series and the focus of this post:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiN3day8HgS0EQifQMHa8M_yZj5_yGt_Z9A6olCq6CaniaWEuFhiKA7dEAmHJtQ2kgXatAAwNgDkl8BgNRkSF3MlUdJn4EQ3AZvLzZK15Pe3A-onVwC_HjFH-TJfmhcZAFxaWiO8mlNJN4E/s1600/minas_tirith.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="561" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiN3day8HgS0EQifQMHa8M_yZj5_yGt_Z9A6olCq6CaniaWEuFhiKA7dEAmHJtQ2kgXatAAwNgDkl8BgNRkSF3MlUdJn4EQ3AZvLzZK15Pe3A-onVwC_HjFH-TJfmhcZAFxaWiO8mlNJN4E/s640/minas_tirith.png" width="576" /></a></div>
<br />
<br />
<h2>
Vulnerability Scope</h2>
<br />
I haven't been able to confirm the exact scope of this issue. I've statically checked quite a few devices (such as the Droid Turbo, Nexus 6, Moto X 2nd Gen), and they were all vulnerable. In fact, I believe the issue was very wide-spread, and may have affected most Qualcomm-based devices at the time.<br />
<br />
So why was this issue so prevalent? As we'll see shortly, the vulnerability is contained in a trustlet and so does not rely on the TrustZone kernel (which tends to change substantially between SoCs), but rather on code which is designed to be able to execute in the same manner on many different devices. As such, all devices containing the trustlet were made vulnerable, regardless of their SoC.<br />
<br />
Also note that on some devices the vulnerable code was present but appeared slightly different (it may have been an older version of the same code). Those devices are also vulnerable, although the indicators and strings you might search for could be slightly different. This means that if you're searching for the exact strings mentioned in this post and don't find them, don't be dissuaded! Instead, reverse-engineer the trustlet using the tools from the <a href="http://bits-please.blogspot.co.il/2016/04/exploring-qualcomms-secure-execution.html">previous blog post</a>, and check for yourself. <br />
<br />
<h2>
Enter Widevine</h2>
<br />
Previously, we decided to focus our research efforts on the "widevine" trustlet, which enables playback of DRM encrypted media using Widevine's DRM platform. This trustlet seems like a good candidate since it is moderately complex (~125KB) and very wide-spread (according to <a href="http://www.widevine.com/supported_platforms.html">their website</a>, it is available on <u>over 2 billion</u> devices).<br />
<br />
After assembling the raw trustlet, we are left with an ELF file, waiting to be analysed. Let's start by taking a look at the function registered by the trustlet in order to handle incoming commands:<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-W00pU-rrElxAdtiHLKUywywtDWE5nRW-i4o1Y7GYjdw48TqcNA3sVx3I9LrgcchMq0YyoE0ZWGnTjKWmLNXdhba8mht-_LVb5DzxL8UJG4kPZlurZ6A-jiq-0SqyMwzxPqi-iZElmkjQ/s1600/Screenshot+from+2016-04-29+17%253A30%253A00.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-W00pU-rrElxAdtiHLKUywywtDWE5nRW-i4o1Y7GYjdw48TqcNA3sVx3I9LrgcchMq0YyoE0ZWGnTjKWmLNXdhba8mht-_LVb5DzxL8UJG4kPZlurZ6A-jiq-0SqyMwzxPqi-iZElmkjQ/s1600/Screenshot+from+2016-04-29+17%253A30%253A00.png" /></a></div>
<br />
As we can see, the first 32-bit value in the command is used to specify the command code, the high-word of which is used to sort the commands into four different categories.<br />
<br />
Taking a peek at each of the category-handling functions reveals that the categories are quite rich - all in all, there are about 70 different supported commands - great! However, going over 70 different commands would be a pretty lengthy process - perhaps we can find a shortcut that'll point us in the right direction? For example, maybe there's a category of commands that were accidentally left in even though they're not used on production devices?<br />
<br />
Since the libraries which are used to interact with the trustlets are also proprietary, we can't look through the source code to find the answers. Instead, I wrote a small IDAPython script to lookup all the references to "QSEECom_send_cmd", the function used to send commands to trustlets, and check what the "command-code" value is for each reference. Then I simply grouped the results into the categories above, producing the following results:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxuzBokpbw_RkhxImciuqlwF0mKN6_lF7zuMldIcL_ctlV32W4v5I4CNTVQNilYd9EBUF_GqzypGoZBHVZbWwQz1_pMcySwazDO1r6_IzPT2eg_PCgjmMbRKHB0QTYFCoqeCLfgHuYTRAc/s1600/Screenshot+from+2016-04-30+17%253A40%253A49.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxuzBokpbw_RkhxImciuqlwF0mKN6_lF7zuMldIcL_ctlV32W4v5I4CNTVQNilYd9EBUF_GqzypGoZBHVZbWwQz1_pMcySwazDO1r6_IzPT2eg_PCgjmMbRKHB0QTYFCoqeCLfgHuYTRAc/s1600/Screenshot+from+2016-04-30+17%253A40%253A49.png" /></a></div>
<br />
So... Nobody is using 5X commands. Suspicious!<br />
<br />
Sifting through the functions in the 5X category, we reach the following command:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguX4mSjBrqsR5Ji96un-KMii3NiDRBVDr4bULWGu3PzeIwVcO1vS-0AEbCekG7y4yxTwBTh3LJlQvRr8BO8lK3ZAb1Bevc5-CSFdisxPmFoVe9V2dwAbDq3_YjKUNROxTRgudv7VIy-LGj/s1600/prdiagmaint.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguX4mSjBrqsR5Ji96un-KMii3NiDRBVDr4bULWGu3PzeIwVcO1vS-0AEbCekG7y4yxTwBTh3LJlQvRr8BO8lK3ZAb1Bevc5-CSFdisxPmFoVe9V2dwAbDq3_YjKUNROxTRgudv7VIy-LGj/s1600/prdiagmaint.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgV_za4uej0vTEV5cJRdPKZbHlOvy26ZwWSjkYYVuRw_Ob_yAZ8I1LCaKUgrwCZkdCNCqYxjp7jqpUsW8lr0MG3WlWIMW9xNL7YBNNDcGT6xqd1mHzlJ2-RgCBBSVoM4HZzmVaqm-eMyr15/s1600/Screenshot+from+2016-04-30+17%253A49%253A43.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<br />
Pretty straight-forward: copies the data from our request buffer into a "malloc"-ed buffer (note that the length field here is not controlled by us, but is derived from the real buffer length passed to QSEOS). Then, the function's flow diverges according to a flag in our request buffer. Let's follow the flow leading to "PRDiagVerifyProvisioning":<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6QGXr6qdquINtDUyrbQThsW_I-agIKY3FM2UR3ugAZaj0Jm_2stWj2K9CqScQagEl57-Nxww8be13SGlXOnUWJl9ymybKm-nKAX5vq92zEtFRCjYS7sWaQY8ieRN3bWYlx_5yQhruea95/s1600/prdiagprov.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6QGXr6qdquINtDUyrbQThsW_I-agIKY3FM2UR3ugAZaj0Jm_2stWj2K9CqScQagEl57-Nxww8be13SGlXOnUWJl9ymybKm-nKAX5vq92zEtFRCjYS7sWaQY8ieRN3bWYlx_5yQhruea95/s1600/prdiagprov.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhP8k-rhAaifmm9JDQWaq95uW1nE-5cRaVALcOsAJBn5sgp6Xdinn8HPjXLopT1UtELYdNN2v39MFXGHl1BZd2J7Nk0GX5ctAGFnyeDwCJUx-ayM7UuwR8i35uBVQMNo-xYJY8pLn_icCt6/s1600/prdiagprov.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
Finally, we found a vulnerability!<br />
<br />
After some simple validation (such as checking that the first DWORD in the command buffer is indeed zero), the function checks the value of the fourth DWORD in our crafted command buffer. As we can see above, setting that value to zero will lead us to a code-path in which a fully-controlled copy is performed from our command buffer into some global buffer, using the third DWORD as the length argument. Since this code-path <u>only</u> performs the vulnerable <i>memcpy</i> and nothing else, it is much more convenient to deal with (since it doesn't have unwanted side-effects), so we'll stick to this code-path (rather than the one above it, which seems more complex).<br />
<br />
Moreover, you might be wondering what is the "global buffer" that's referred to in the function above. After all, it looks a little strange - it isn't passed in to the function at any point, by is simply referred to "magically", by using the register R9.<br />
<br />
Remember how the trustlets that we analysed in the previous blog post had a large read-write data segment? This is the data segment in which all the modifiable data of the trustlet is stored - the stack, the heap and the global variables. In order to quickly access this segment from any location in the code, Qualcomm decided to use the platform-specific R9 register as a "global register" whose value is never modified, and which always points to the beginning of the aforementioned segment. According to the <a href="http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042f/IHI0042F_aapcs.pdf">ARM AAPCS</a>, this is actually valid behaviour:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-2v9eSq9FHkmEQT4PCih8TO4-l_hKyper7HzenfDE-OvPFgGCMzsctcSNwl0HdPGs2FVTVDNLtbpMCgm5Kp8viFqgXdvF-YEyUNQRC0sMQ3P1G_YeWVAF9NT78718SGL6oO2KbWS5WbbS/s1600/Screenshot+from+2016-04-30+18%253A53%253A47.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="82" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-2v9eSq9FHkmEQT4PCih8TO4-l_hKyper7HzenfDE-OvPFgGCMzsctcSNwl0HdPGs2FVTVDNLtbpMCgm5Kp8viFqgXdvF-YEyUNQRC0sMQ3P1G_YeWVAF9NT78718SGL6oO2KbWS5WbbS/s640/Screenshot+from+2016-04-30+18%253A53%253A47.png" width="640" /></a></div>
<br />
<h2>
What now?</h2>
<br />
Now that we have a primitive, it's time to try and understand which pieces of data are controllable by our overflow. Again, using a short IDAPython script, we can search for all references to the "global buffer" (R9) which reside <u>after</u> the overflown buffer's start address (that is, after offset 0x10FC). Here are the results:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvJS8LmmInpR09k36lCB5-9n2hHrVR5mdhtUvOHe5YzYFnoFwbaLIUkPgRy67wveUHu58Z-XKdNZTQK6yOkuLVuCvEsRx2iXRXrOC_9Jwqv9WipwbRgfnW1IXEiM18iPQ2YC1rYZeWgQcx/s1600/Screenshot+from+2016-04-30+19%253A51%253A06.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvJS8LmmInpR09k36lCB5-9n2hHrVR5mdhtUvOHe5YzYFnoFwbaLIUkPgRy67wveUHu58Z-XKdNZTQK6yOkuLVuCvEsRx2iXRXrOC_9Jwqv9WipwbRgfnW1IXEiM18iPQ2YC1rYZeWgQcx/s1600/Screenshot+from+2016-04-30+19%253A51%253A06.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgafSPZNGuJ-CZZn5Dp4YKoyvCv7u1eT6y6QIT6VOh2UeRjv2Fj4QKP1FszeXpjYnGaSDa0ZFzsFpK9TL6Y8bFXySMVqd8ddDV3V9mWmnzOPmcnw9_B4c0f6S4om9HYpwuwLN9ucW_-zthj/s1600/Screenshot+from+2016-04-30+19%253A46%253A12.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
Disappointingly, nearly all of these functions don't perform any "meaningful" operations of the controllable pieces of data. Specifically, the vast majority of these functions simply store file-system paths in those memory locations, which imply no obvious way to hijack control flow.<br />
<br />
<h2>
Primitive Technology</h2>
<br />
Since there aren't any function pointers or immediate ways to manipulate control flow directly after the overflown buffer, we'll need to upgrade our buffer overflow primitive into a stronger primitive before we can gain code execution.<br />
<br />
Going through the list of functions above, we come across interesting block of data referred to by several functions:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAJd7zWUg37NivhCIdcCbOM3JO5BWsN2BWtpV2PSkzOzirMitro0nHax8ubq666nkM_7I98kAT-kYwGWn4icEpFxHXAeCcCrf3VfVpKqvUF2BiPpS5XcwTVGETL3dT9IvaHd5ykuqClVt1/s1600/Screenshot+from+2016-05-01+01%253A55%253A35.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAJd7zWUg37NivhCIdcCbOM3JO5BWsN2BWtpV2PSkzOzirMitro0nHax8ubq666nkM_7I98kAT-kYwGWn4icEpFxHXAeCcCrf3VfVpKqvUF2BiPpS5XcwTVGETL3dT9IvaHd5ykuqClVt1/s1600/Screenshot+from+2016-05-01+01%253A55%253A35.png" /></a></div>
As you can see above, the block of 0x32 DWORDs, starting at offset 0x169C, are used to store "sessions". Whenever a client sends commands to the Widevine trustlet, they must first create a new "session", and all subsequent operations are performed using the specific session identifier issued during the session's creation. This is needed, for example, in order to allow more than a single application to decrypt DRM content at the same time while having completely different internal states.<br />
<br />
In any case, as luck would have it, the sessions are complex structures - hinting that they may be used in order to subtly introduce side-effects in our favour. They are also within our line-of-fire, as they are stored at an offset greater than that of the overflown buffer. But, unfortunately, the 0x32 DWORD block mentioned above only stores the <u>pointers</u> to these session objects, not the objects themselves. This means that if we want to overwrite these values, they must point to addresses which are accessible from QSEE (otherwise, trying to access them will simply result in the trustlet crashing).<br />
<br />
<br />
<h2>
Finding Ourselves</h2>
<h2>
</h2>
<br />
In order to craft legal session pointers, we'll need to find out where our trustlet is loaded. Exploring the relevant code reveals that QSEOS goes to great lengths in order to protect trustlets from the "Normal World". This is done by creating a special memory region, referred to as "secapp-region", from which the trustlet's memory segments are carved. This area is also protected by an MPU, which prevents the "Normal World" from accessing it in any way (attempting to access those physical addresses from the "Normal World" causes the device to reset).<br />
<br />
On the other hand, trustlets reside within the secure region and can obviously access their own memory segments. Not only that, but in fact trustlets can access all allocated memory within the "secapp" region, even memory belonging to other trustlets! However, any attempt to access unallocated memory within the region results in the trustlet immediately crashing.<br />
<br />
...Sounds like we're beginning to form a plan! <br />
<br />
We can use the overflow primitive in order to overwrite a session pointer to a location within the "secapp" region. Now, we can find a command which causes a read attempt using our poisoned session pointer. If the trustlet crashes after issuing the command, we guessed wrong (in that case, we can simply reload the trustlet). Otherwise, we found an allocated page in the "secapp" region.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtYADrQPlcctsbxWcl7BYZmMf5hN2THxQ7kkUwoxI17FTo5tnWuzQfrvshpwKZJdET0lTmCvzBWwkVdTCROcmclZcOw9dANnx-7SSyb0mLnTiIPNKxiUGfTvxoop4ExAUGFxawmkjJFdQW/s1600/Screenshot+from+2016-05-01+12%253A21%253A53.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtYADrQPlcctsbxWcl7BYZmMf5hN2THxQ7kkUwoxI17FTo5tnWuzQfrvshpwKZJdET0lTmCvzBWwkVdTCROcmclZcOw9dANnx-7SSyb0mLnTiIPNKxiUGfTvxoop4ExAUGFxawmkjJFdQW/s1600/Screenshot+from+2016-05-01+12%253A21%253A53.png" /></a></div>
<br />
But... How do we know which trustlet that page belongs to?<br />
<br />
We already have a way to differentiate between allocated and unallocated pages. Now, we need some way to distinguish between pages based on their <u>contents</u>.<br />
<br />
Here's an idea - let's look for a function that behaves differently based on the read value in a the session pointer:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQgEg6wPFUlXp3GXFwLXE4Q9INExluARRjOIsisQ2YIMhGFGScnZMBrBwufxID7WZELWyItvWPYP48fGmUMOGRdyYsloag894xFVz1YcM36QXtFnZwy6ISUerf2QrkuUDNG_Jg27KtDdDO/s1600/Screenshot+from+2016-05-01+12%253A29%253A41.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="349" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQgEg6wPFUlXp3GXFwLXE4Q9INExluARRjOIsisQ2YIMhGFGScnZMBrBwufxID7WZELWyItvWPYP48fGmUMOGRdyYsloag894xFVz1YcM36QXtFnZwy6ISUerf2QrkuUDNG_Jg27KtDdDO/s1600/Screenshot+from+2016-05-01+12%253A29%253A41.png" width="800" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLnxPYNjOZBUm3W1J6XaLSH2dxVhcgQhWzvoGZt6RJwMw50EBRI5B99adE2aRiZToJMQk3nD5-CBF4KqWjdkPBRWldt0SBJruuXPcfhxNoNBSmQshNPvWoAMvWsbhLcC5ndRY_hl62dsZ3/s1600/Screenshot+from+2016-05-01+12%253A26%253A29.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
Okay! This function tries to access the data at <i>session_pointer + 0xDA</i>. If that value is equal to one, it will return the value 24, otherwise, it will return 35.<br />
<br />
This is just like finding a good watermelon; by "tapping" on various memory locations and listening to the "sound" they make, we can deduce something about their contents. Now we just need to give our trustlet a unique "sound" that we can identify by tapping on it.<br />
<br />
Since we can only listen to differences between one and non-one values, let's mark our trustlet by creating a unique pattern containing ones and zeros within it. For example, here's a pattern which doesn't occur in any other trustlet:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2jb3RBvS_XYvuf_OqIUBJxWMziBsugEW7U02svrs3x8P49fMFVxcAFpEfDY5oRvW8iIfnSIX0C3-JJhTIlBfpCs3n5qGwZHAERxbTxQMgrpx1xKrYRR1gDc8Ti2RLhaA1DL9-R-1MniHq/s1600/pattern.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2jb3RBvS_XYvuf_OqIUBJxWMziBsugEW7U02svrs3x8P49fMFVxcAFpEfDY5oRvW8iIfnSIX0C3-JJhTIlBfpCs3n5qGwZHAERxbTxQMgrpx1xKrYRR1gDc8Ti2RLhaA1DL9-R-1MniHq/s1600/pattern.png" /></a></div>
<br />
Now, we can simply write this pattern to the trustlet's data segment by using over overflow primitive, effectively giving it its own distinct "sound". <br />
<br />
Finally, we can repeat the following strategy until we find the trustlet: <br />
<ul>
<li>Randomly tap a memory location in the "secapp" region:</li>
<ul>
<li>If it sounds "hollow" (i.e., the trustlet crashes) - there's nothing there, so reload our trustlet</li>
<li>Otherwise, tap the sequence of locations within the page which should contain our distinct marking pattern. If it sounds like the pattern above, we found our trustlet</li>
</ul>
</ul>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFtBlX7BTUvl8XBmx2no1iwgFE9tExnrf1lqoFKAC8bguJXNXzqAGN5llGziWj3VYL8a8WOcTIs76FBxYnfWUqqWL9eiPdRMQkf7umjfOX6TynjlCkTvE-TtY8v1iDvW9_nXoYXuqb8JGR/s1600/Screenshot+from+2016-05-01+13%253A53%253A07.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFtBlX7BTUvl8XBmx2no1iwgFE9tExnrf1lqoFKAC8bguJXNXzqAGN5llGziWj3VYL8a8WOcTIs76FBxYnfWUqqWL9eiPdRMQkf7umjfOX6TynjlCkTvE-TtY8v1iDvW9_nXoYXuqb8JGR/s1600/Screenshot+from+2016-05-01+13%253A53%253A07.png" /></a></div>
<br />
Of course, inspecting the allocation scheme used by QSEOS could allow us to speed things further by only checking relevant memory locations. For example, QSEOS seems to allocate trustlets consecutively, meaning that simply scanning from the end of the "secapp" region to its beginning using increments of <a href="https://en.wikipedia.org/wiki/Nyquist_frequency">half the trustlet's size</a> will guarantee a successful match.<br />
<br />
<br />
<h2>
A (messy) write primitive</h2>
<br />
Now that we have a way to find the trustlet in the secure region, we are able to craft "valid" session pointers, which point to locations within the trustlet. Next up, we need to find a way to create a write primitive. So... are there any functions which write controllable data into a session pointer?<br />
<br />
Surprisingly, nearly all functions that do write data to the session pointer <u>do not</u> allow for arbitrary control over the data being written. Nonetheless, one function looks like it could be of some help:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8oL2KQVTKgtj81lmjt2LyPDaEuMNN2fGENS2WlqEXLpWcuL3rz_PhlNE_DnFJbuiKP3DR5NtyUCSfTZPXH3zqFPRyf3Mn6UZwknNbfqeCrXgIl_-6CpbTgxBTLZK0WZA-1CiK7LOHa0jQ/s1600/Screenshot+from+2016-05-01+14%253A05%253A34.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8oL2KQVTKgtj81lmjt2LyPDaEuMNN2fGENS2WlqEXLpWcuL3rz_PhlNE_DnFJbuiKP3DR5NtyUCSfTZPXH3zqFPRyf3Mn6UZwknNbfqeCrXgIl_-6CpbTgxBTLZK0WZA-1CiK7LOHa0jQ/s1600/Screenshot+from+2016-05-01+14%253A05%253A34.png" /></a></div>
This function generates a random DWORD to be used as a "nonce", then checks if enough time elapsed since the previous time it was called. If so, it adds the random value to the session pointer by calling "addNonceToCache".<br />
<br />
First of all, since the "time" field is saved in the global buffer after our overflown buffer, we can easily clear it using our overflow primitive, thus removing the time limitation and allowing us to call the function as frequently as we'd like. Also, note that the generated nonce's random value is written into a buffer which is returned to the user - this means that after a nonce is generated, the caller also learns the value of the nonce.<br />
<br />
Let's take a peek at how the nonces are stored in the session pointer:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBS6MZxX-O0l9GunhTtdIG0QgAwzc3ONQ_ZXb9_OCV4oLzrKmVo0ZzpNAjkNG1gUMc9nTRoW9t0lFZ-00RZz5eJJaVBh-t09pa4_gMyFBf7s1FSqMwfUCXrkdIhXmXdXOiAL4b9TWrxkH5/s1600/Screenshot+from+2016-05-01+14%253A17%253A49.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBS6MZxX-O0l9GunhTtdIG0QgAwzc3ONQ_ZXb9_OCV4oLzrKmVo0ZzpNAjkNG1gUMc9nTRoW9t0lFZ-00RZz5eJJaVBh-t09pa4_gMyFBf7s1FSqMwfUCXrkdIhXmXdXOiAL4b9TWrxkH5/s1600/Screenshot+from+2016-05-01+14%253A17%253A49.png" /></a></div>
<br />
So there's an array of 16 nonces in the session object - starting at offset 0x88. Whenever a nonce is added, all the previous nonce values are "rolled over" one position to the right (discarding the last nonce), and the new nonce is written into the first location in the nonce array. <br />
<br />
See where we're going with this? This is actually a pretty powerful write primitive (albeit a little messy)!<br />
<br />
Whenever we want to write a value to a specific location, we can simply set the session pointer to point to that location (minus the offset of the nonces array). Then, we can start generating nonces, until the least-significant byte (this is a little-endian machine) in the generated nonce matches the byte we would like to write. Then, once we get a match, we can increment the session pointer by one, generate the next byte, and so forth.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixecuy8kY8fCkOjy9vZeULQk2R49pjICoADV1HpFqq5slrtm27VXi3EmCS9Mlquksz60kY_4NHys66rTz0M0JJ7AVyvEoAMBAxGAZjpmj6vy6_nBWE2iPj_8HKWqODBZxWl_BK1ciVE2L1/s1600/Screenshot+from+2016-05-01+14%253A58%253A24.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixecuy8kY8fCkOjy9vZeULQk2R49pjICoADV1HpFqq5slrtm27VXi3EmCS9Mlquksz60kY_4NHys66rTz0M0JJ7AVyvEoAMBAxGAZjpmj6vy6_nBWE2iPj_8HKWqODBZxWl_BK1ciVE2L1/s1600/Screenshot+from+2016-05-01+14%253A58%253A24.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiA7Kir1InkLcFGcDPja8jF59qqMbUTT49A-2f92GwD42bvjx0XJ_d6-4ioACnjiI8osY5umhJDgSFZedAunVyzDZAnq8eBhWPdlMl16mfbampdZ9efiOnBRDWuwi55QHKi7EVxh4oNBgwP/s1600/Screenshot+from+2016-05-01+14%253A42%253A39.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<br />
This allows us to generate any arbitrary value with an expectancy of only 256 nonce-generation calls per byte (since this is a geometric random variable). But at what cost?<br />
<br />
Since the values in the nonce cache are "rotated" after every call, this means that we mess-up the 15 DWORDs after the last written memory location. We'll have to work our way around that when we design the exploit.<br />
<br />
<h2>
Writing an exploit</h2>
<h2>
</h2>
We finally have enough primitives to craft a full exploit! All we need to do is find a value that we can overwrite using the messy write primitive, which will allow us to hijack the control flow of the application. <br />
<br />
Let's take a look at the function in charge of handling the "6X" category of commands:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4axwZ1kxjqHS_E8RZIHvvfA5bEglSK_Ys8KVO-0vkm9QV8lLzZM3gtwQ0lTIcdjjrsImJfhtW56LT8oS3RTopoKfzwMkNTV0IFRT8SZSO_8uzbQBR9fxSJd5YdKJhxk2gJ7eqLrkI4X8U/s1600/Screenshot+from+2016-05-01+15%253A10%253A50.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4axwZ1kxjqHS_E8RZIHvvfA5bEglSK_Ys8KVO-0vkm9QV8lLzZM3gtwQ0lTIcdjjrsImJfhtW56LT8oS3RTopoKfzwMkNTV0IFRT8SZSO_8uzbQBR9fxSJd5YdKJhxk2gJ7eqLrkI4X8U/s1600/Screenshot+from+2016-05-01+15%253A10%253A50.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6MvEBDcArzycjq52l2MJKkSwAB72kQLzMwrhsq2WF7ECOucHm3_M679sEkaeJ3yV8G9M2MzV2kjKR_QKN4Z82tCPjm2D2VbtTtoh3EwsS3nvuA9Kx1BdZHLwryEF6hKmuociW-1fLWszI/s1600/Screenshot+from+2016-05-01+15%253A10%253A50.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<br />
As you can see, the function calls the requested commands by using the command ID specified as an index into an array stored in the global buffer. Each supported command is represented by a 12-byte entry in the array, containing four pieces of information:<br />
<ul>
<li>The command code (32-bits)</li>
<li>A pointer to the handling function itself (32-bits) </li>
<li>The minimal input length (16-bits)</li>
<li>The minimal output length (16-bits)</li>
</ul>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8-vECdY_r7t5lT5ieOc1LLU9pgRMVtFY8wzgdJ5xQExMEgZEjE_uzQ8fJJel60Dh3IJiB79YJMpBXTVTYOpGGYsq1MfBXeJfwlicyQMgWRp_KC9fbTwpHThkbdiwnFbTIsMXZCsa-Osz8/s1600/Screenshot+from+2016-05-01+15%253A25%253A31.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8-vECdY_r7t5lT5ieOc1LLU9pgRMVtFY8wzgdJ5xQExMEgZEjE_uzQ8fJJel60Dh3IJiB79YJMpBXTVTYOpGGYsq1MfBXeJfwlicyQMgWRp_KC9fbTwpHThkbdiwnFbTIsMXZCsa-Osz8/s1600/Screenshot+from+2016-05-01+15%253A25%253A31.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifGRoqAPdBuWy-IY5wPmASsjL3CToqiV-iumFCmsx1bGA0LtbpqtqHYgw8w4ZNR4y86nQIUfsMGbooi-mBrQabhLHVL5V11r8SNiIUIzdz6Y5LHLi3NVSMS57MZUQ5ZmFkc-ImWaDgrXig/s1600/Screenshot+from+2016-05-01+15%253A21%253A12.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
If this information is valid, the function pointer is executed, passing in the user's input buffer as the first argument and the output buffer as the second argument.<br />
<br />
If we choose an innocuous 6X command, we can overwrite the corresponding entry in the array above so that its function pointer will be directed at any piece of code we'd like to execute. Then, simply calling this command will cause the trustlet to execute the code at our controlled memory location. Great!<br />
<br />
We should be wary, however, not to choose a function which lies directly before an "important" command that we might need later. This is because our messy write primitive will destroy the following 15 DWORDs (or rather, the next 5 array entries). Let's take a look at the function which populates the entries in the command array:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifOoiUy4BAVAN8ntKUfabxjzeWjpl4ZNoTiky8-5Unm7gHD9qi0IplRFTg5SKELl114XCM3NdcnoW57eDg46qpOKog6ot08omAV9SHs71Zl-89Y8JkjGJ68L5M5VaHKh2Og_Y90PHiZysv/s1600/funcptrs.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifOoiUy4BAVAN8ntKUfabxjzeWjpl4ZNoTiky8-5Unm7gHD9qi0IplRFTg5SKELl114XCM3NdcnoW57eDg46qpOKog6ot08omAV9SHs71Zl-89Y8JkjGJ68L5M5VaHKh2Og_Y90PHiZysv/s1600/funcptrs.png" /></a></div>
<br />
There are six consecutive entries corresponding to unused functions. Therefore, if we choose to overwrite the entry directly before them, we'll stay out of trouble.<br />
<h2>
</h2>
<h2>
Universal Shellcode Machine</h2>
<br />
Although we can now hijack the control flow, we still can't quite execute arbitrary code within QSEE yet. The regular course of action at point would probably be to find a
stack-pivot gadget and write a short ROP chain which will enable us to
allocate shellcode - however, since the trustlets' code segments aren't writeable, and the TrustZone kernel doesn't expose any system call to QSEE to allow the allocation of new executable pages, we are left with no way to create executable shellcode.<br />
<br />
So does this mean we need to write all our logic as a ROP chain? That would be extremely inconvenient (even with the aid of automatic "ROP"-compilers), and might even not be possible if the ROP gadgets in the trustlet are not Turing-Complete.<br />
<br />
Luckily, after some careful consideration, we can actually avoid the need to write longer ROP chain. If we think of our shellcode as a Turing Machine, we would like to create a "Universal Turing Machine" (or simulator), which will enable us to execute any given shellcode as if it were running completely within QSEE.<br />
<br />
Given a piece of code, we can easily simulate all the control-flow and logic in the "Normal World", simply by executing the code fully in the "Normal World". But what about operations which behave differently in a QSEE-context? If we think about it, there are only a few such operations:<br />
<ul>
<li>Reading and writing memory</li>
<li>Calling system calls exposed by the TrustZone kernel</li>
</ul>
These operations must execute within QSEE. However, we can actually execute both of these operations in QSEE by writing one small ROP chain!<br />
<br />
All we need is a single ROP chain which will:<br />
<ul>
<li>Hijack control flow to a separate stack</li>
<li>Prepare arguments for a function call</li>
<li>Call the wanted QSEE function</li>
<li>Return the result to the user and restore execution in QSEE</li>
</ul>
As you can see, all this chain do is to enable us to execute any given QSEE function using any supplied arguments. But how can we use it to simulate the special operations?<br />
<br />
Well, since all system-calls in QSEE have matching calling-stubs in each trustlet, we can use our ROP chain to execute any system call with ease. As for memory accesses - there is an abundance of QSEE functions which can be used as read and write gadgets. Hence, both operations are simple to execute using our short ROP chain.<br />
<br />
This leaves us with the following model:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbnhodxd3S_WsNTtKT2ExSAGQhUV_axNB2MP9D4YCcIjlRATdJ4AZqYOuef9eqS-N6igY4EgYx2uZJYLuZenrWpOYwSoV6clmyPkKN7etSmumr19ASSSgt79IhTdnPzbY5u4GVST1BrRcw/s1600/Screenshot+from+2016-05-01+18%253A23%253A37.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbnhodxd3S_WsNTtKT2ExSAGQhUV_axNB2MP9D4YCcIjlRATdJ4AZqYOuef9eqS-N6igY4EgYx2uZJYLuZenrWpOYwSoV6clmyPkKN7etSmumr19ASSSgt79IhTdnPzbY5u4GVST1BrRcw/s1600/Screenshot+from+2016-05-01+18%253A23%253A37.png" /></a></div>
<br />
This also means that executing arbitrary shellcode in QSEE doesn't require any engineering effort! All the shellcode developer needs to do is to delegate memory accesses and system calls to specific APIs exposed by the exploit. The rest of the shellcode's logic can remain unchanged and execute completely in the "Normal World". We'll see an example of some shellcode using this model shortly.<br />
<br />
<h2>
Finding a stack pivot</h2>
<br />
In order to execute a ROP chain, we need to find a convenient stack-pivot gadget. When dealing with large or medium-sized applications, this is not a daunting task - there is simply enough code for us to find at least one gadget that we can use.<br />
<br />
However, since we're only dealing with ~125KB of code, we might not be that lucky. Not only that, but at the point at which we hijack the control flow, we only have control over the registers R0 and R1, which point to the input and output buffers, respectively.<br />
<br />
After fully disassembling the trustlet's code we are faced with the harsh truth - it seems as though there is no usable stack pivot using our controlled registers. So what can we do?<br />
<br />
Recall that ARM opcodes can be decoded in more than one way, depending on the value of the T bit in the <a href="http://infocenter.arm.com/help/topic/com.arm.doc.dui0473c/CHDFAEID.html">CPSR</a>. When the bit is set, the processor is executing in "Thumb" mode, in which the instruction length is 16-bits. Otherwise, the processor is in "ARM" mode, with an instruction length of 32-bits.<br />
<br />
We can easily switch between these modes by using the least-significant bit of the PC register when performing a jump. If the least-significant bit is set, the T bit will be set, and the processor will switch to "Thumb" mode. Otherwise, it will switch to ARM mode.<br />
<br />
Looking at the trustlet's code - it seems to contain mostly "Thumb" instructions. But perhaps if we were to forcibly decode the instructions as if they were "ARM" instructions, we'd be able to find a hidden stack pivot which was not visible beforehand.<br />
<br />
Indeed, that is the case! Searching through the ARM opcodes reveals a very convenient stack-pivot:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIG4NTBhuBPGAc4SduE_NaOBfovQMTbRiwPBafoXPg0nZ_sJsg2_LFqkNKrlc4oSq01NN1Hqim2uSyrMvBxm-eYrGORj7LWl7isrmD9qbjAQWVDHFtOXh1xpNJ_IfOreru7tVaZHKawCw3/s1600/Screenshot+from+2016-05-01+23%253A40%253A45.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIG4NTBhuBPGAc4SduE_NaOBfovQMTbRiwPBafoXPg0nZ_sJsg2_LFqkNKrlc4oSq01NN1Hqim2uSyrMvBxm-eYrGORj7LWl7isrmD9qbjAQWVDHFtOXh1xpNJ_IfOreru7tVaZHKawCw3/s1600/Screenshot+from+2016-05-01+23%253A40%253A45.png" /></a></div>
<br />
By executing this opcode, we will be able to fully control the stack pointer, program counter and other registers by using the values stored in R0 - which, as we saw above, points to the fully user-controlled input buffer. Great!<br />
<br />
As for the rest of the ROP chain - it is pretty standard. In order to execute a function and return all we need to do is build a short chain which:<br />
<ul>
<li>Sets the low registers (R0-R3) and the stack arguments to the function's arguments</li>
<li>Set the link register to point to the rest of our chain </li>
<li>Jump to the function's start address</li>
<li>When control is returned via the crafted LR value, store the return value in user-accessible memory location, such as the supplied output buffer</li>
<li>Restore the stack pointer to the original location and return to the location from which control was originally hijacked</li>
</ul>
You can find the complete ROP chain and gadgets in the provided exploit code, but I imagine it's exactly what you'd expect.<br />
<br />
<h2>
Putting it all together</h2>
<br />
At long last, we have all the pieces needed to create a fully functional exploit. Here's a short run-down of the exploit's stages:<br />
<ul>
<li>Find the Widevine application by repeatedly "tapping" the secapp region and "listening"</li>
<li>Create a "messy" write gadget using the nonce-generation command</li>
<li>Overwrite an unused 6X command entry using the write gadget to direct it to a stack-pivot</li>
<li>Execute any arbitrary code using a small ROP chain under the "Universal Shellcode Machine"</li>
</ul>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhO4kENqz5HDm2zBcY0Ox-0rWGtbc-mNf77BTbXtfXMc_Iz4Vqt8HT8kyX-XxYTrol8BaZSpaknEv9G96x1lOhezW2UZBYdxyviA2bd9mkobsfxkCCCb2XErYyX2Z7uj3zLkTKWOfj072a1/s1600/Screenshot+from+2016-05-02+18%253A06%253A00.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhO4kENqz5HDm2zBcY0Ox-0rWGtbc-mNf77BTbXtfXMc_Iz4Vqt8HT8kyX-XxYTrol8BaZSpaknEv9G96x1lOhezW2UZBYdxyviA2bd9mkobsfxkCCCb2XErYyX2Z7uj3zLkTKWOfj072a1/s1600/Screenshot+from+2016-05-02+18%253A06%253A00.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjc_LR-qWqbtWdXilLoC-4PES-NvDqyseY5vEMOxKRUbdVFWnGXbdjuC2EFgWrniH-Q-aBAUbtbPTgA0Z1tWkDvU1TuselkNb7oesEmQJ1lwnWjl6i6C8Y64NbOvWgh7z2AaDHIWGzwscNB/s1600/Screenshot+from+2016-05-02+15%253A16%253A20.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<br />
<h2>
The Exploit</h2>
<br />
As always, the full exploit code is available here:<br />
<br />
<a href="https://github.com/laginimaineb/cve-2015-6639">https://github.com/laginimaineb/cve-2015-6639</a><br />
<br />
I've also included a sample shellcode using the model described earlier. The shellcode reads a file from TrustZone's secure file-system - SFS. This file-system is encrypted using a special hardware key which should be inaccessible to software running on the device - you can read more about it <a href="http://www.google.com/patents/US7921303">here</a>. Regardless, running within the "Secure World" allows us to access SFS fully, and even extract critical encryption keys, such as those used to decrypt DRM content.<br />
<br />
In fact, this is all it takes:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDFrM_rIhUcKfPwPpiYsVjTyNqBX6iSVHfPuSuegMcwNjti8L9rAnnHOayXQF70O1wehqGSrAUKGjDYoL8yE1NjnNwA5QQ_Yj2c4JgX334xaeW9Y-FqON8Aboy4-i2K-B6EU2ipKRzQp8e/s1600/Screenshot+from+2016-05-01+18%253A46%253A50.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDFrM_rIhUcKfPwPpiYsVjTyNqBX6iSVHfPuSuegMcwNjti8L9rAnnHOayXQF70O1wehqGSrAUKGjDYoL8yE1NjnNwA5QQ_Yj2c4JgX334xaeW9Y-FqON8Aboy4-i2K-B6EU2ipKRzQp8e/s1600/Screenshot+from+2016-05-01+18%253A46%253A50.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkGdEl4mfplGnVpvs1RjAUw2D5Ov89eg240sCevSruSZeHM1Sufk8s2kNFJcVprKDYbsoQQxGQMP9N6N32F22MRKr-2DKfgC1uQeVMp1tiFwMKw36p2AjdOVfz7t7PwH8mk5W1rKaf7c8N/s1600/Screenshot+from+2016-05-01+18%253A45%253A41.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
Also, please note that there are quite a few small details that I did not go into in this blog post (for brevity’s sake, and to keep it interesting). However, every single detail is documented in the exploit's code. So by all means, if you have any unanswered questions regarding the exploit, I encourage you to take a look at the code and documentation. <br />
<br />
<h2>
What's next?</h2>
<br />
Although we have full code-execution within QSEE, there are still some things beyond our reach. Specifically, we are limited only to the API provided by the system-calls exposed by the TrustZone kernel. For example, if we were looking to unlock a bootloader, we would probably need to be able to blow the device's QFuses. This is, understandably, not possible from QSEE.<br />
<br />
With that in mind, in the next blog post, we'll attempt to further elevate our privileges from QSEE to the TrustZone kernel!<br />
<br />
<h2>
Timeline </h2>
<ul>
<li>27.09.2015 - Vulnerability disclosed</li>
<li>27.09.2015 - Initial response from Google</li>
<li>01.10.2015 - PoC sent to Google</li>
<li>14.12.2015 - Vulnerability fixed, patch distributed</li>
</ul>
<br />
I would also like to mention that on 19.10.2015 I was notified by Google that this issue has already been internally discovered and reported by Qualcomm. However, for some reason, the fix was not applied to Nexus devices.<br />
<br />
Moreover, there are quite a few firmware images for other devices (such as the Droid Turbo) that I've downloaded from that same time period that appeared to still contain the vulnerability! This suggests that there may have been a hiccup when internally reporting the vulnerability or when applying the fix.<br />
<br />
Regardless, as Google has included the issue in the bulletin on 14.12.2015, any OEMs that may have missed the opportunity to patch the issue beforehand, got another reminder.laginimainebhttp://www.blogger.com/profile/00867710965331025912noreply@blogger.com2035tag:blogger.com,1999:blog-2029700426505953971.post-71266675812786887332016-04-26T14:16:00.000+03:002016-04-27T16:54:42.696+03:00Exploring Qualcomm's Secure Execution EnvironmentWelcome to a new series of blog posts!<br />
<br />
In this series, we'll dive once more into the world of TrustZone, and explore a new chain of vulnerabilities and corresponding exploits which will allow us to elevate privileges from zero permissions to code execution in the TrustZone kernel.<br />
<br />
This may sound familiar to those of you who have <a href="http://bits-please.blogspot.com/2016/01/android-privilege-escalation-to.html">read</a> <a href="http://bits-please.blogspot.com/2015/08/android-linux-kernel-privilege.html">the</a> <a href="http://bits-please.blogspot.com/2015/08/exploring-qualcomms-trustzone.html">previous</a> <a href="http://bits-please.blogspot.com/2015/08/full-trustzone-exploit-for-msm8974.html">series</a> - but let me reassure you; this series will be much more exciting!<br />
<br />
First of all, this exploit chain features a privilege escalation which is universal across all Android versions and phones (and which requires zero permissions) and a TrustZone exploit which affects a very wide variety of devices. Secondly, we will dive deep into an as-of-yet unexplored operating system - QSEE - Qualcomm's Secure Execution Environment. Lastly, we'll see some interesting TrustZone payloads, such as directly extracting a real fingerprint from TrustZone's encrypted file-system.<br />
<br />
In case you would like to follow along with the symbols and
disassembled binaries, I will be using my own Nexus 6 throughout this
series, with the following fingerprint:<br />
<ul><i><u>google/shamu/shamu:5.1.1/LMY48M/2167285:user/release-keys</u></i><i><u> </u></i></ul>
<br />
You can find the exact factory image <a href="https://dl.google.com/dl/android/aosp/shamu-lmy48m-factory-336efdae.tgz">here</a>.<br />
<h2>
</h2>
<h2>
<a name='more'></a></h2>
<h2>
</h2>
<h2>
Oh say can QSEE </h2>
<br />
In this blog post, we'll explore <b>Q</b>ualcomm's <b>S</b>ecure <b>E</b>xecution <b>E</b>nvironment (QSEE).<br />
<br />
<a href="http://bits-please.blogspot.com/2015/03/getting-arbitrary-code-execution-in.html">As we've previously discussed</a>, one of the main reasons for the inclusion of TrustZone on devices is the ability to provide a "Trusted Execution Environment" (TEE) - an environment which should theoretically allow computation which cannot be interfered with from the regular operating system, and is therefore "trusted".<br />
<br />
This is achieved by creating a small operating system which operates solely in the "Secure World" facilitated by TrustZone. This operating system provides a small number of services directly in the form of system calls which are handled by the TrustZone kernel (TZBSP) itself. However, in order to allow for an extensible model where "trusted" functionality can be added, the TrustZone kernel can also securely load and execute small programs called "Trustlets", which are meant to provide a secure service to the insecure ("Normal World") operating system (in our case, Android).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtXB2A3GXmz05hZXbBsVzVOVwmgp4JOMYR_iu1ORBSJFccjH0RZGySju94XrGRjnPuy1WFlvh0DqwSmwgtX5VohzLTuLdOGfsHBTsHUcRJzqvBmxRQox_Gznji4F3hBaYXveMBEOJ84Us5/s1600/Screenshot+from+2016-04-25+22%253A05%253A26.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtXB2A3GXmz05hZXbBsVzVOVwmgp4JOMYR_iu1ORBSJFccjH0RZGySju94XrGRjnPuy1WFlvh0DqwSmwgtX5VohzLTuLdOGfsHBTsHUcRJzqvBmxRQox_Gznji4F3hBaYXveMBEOJ84Us5/s1600/Screenshot+from+2016-04-25+22%253A05%253A26.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgykdpKC19uHV5AQQq5Mdr5YCEPEf4_PBjPMLqzb_bAk_9gk5JfZSuLzRTjprlgtDNyD2oqwjnD99tPQkDnWanEfMdKX42kbqUBEIHZIIfeq2RP8HSWijIQlB5vJh7hYnR0FD9TH-TsB7Vu/s1600/Screenshot+from+2016-04-25+22%253A04%253A40.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjas5xl7ASgkVfnl5TKWaxULfNfgDM7zijm2meAnwLRE6RIcKFQGuox7vvq4Bwza6V_-bKjt1U3oasPSE-gNFwGjxFQ7NGIu-T0MR7lOfhJozn_P3lghQTJ-JXPgIEcxmrN9cOjNAC_qlnS/s1600/qsee_tzbsp.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<br />
There are several such Trustlets commonly used on devices:<br />
<ul>
<li>keymaster - Implements the key management API provided by the Android "keystore" daemon. It can securely generate and store cryptographic keys and allow the users to operate on data using these keys.</li>
<li>widevine - Implementation of <a href="https://www.widevine.com/wv_drm.html">Widevine DRM</a>, which allows "secure" playback of media on the device.</li>
</ul>
In fact, there are many more DRM related trustlets, depending on the OEM and the device, but these two trustlets are universally used.<br />
<br />
<h2>
Where do we start?</h2>
<br />
Naturally, one place to start would be to look at a trustlet of our choice, and to try and understand what makes it tick. Since the "widevine" module is one of the most ubiquitous, we'll focus on it.<br />
<br />
Searching briefly for the widevine trustlet itself in the device's firmware reveals the following:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitA7BslHFBVq12ScsiEGQgm7miVfa0zd9K8EUExgjDcBlp8B6RjztSjFqkI_1EZMY7M5HzsOFD76PPmYBJvwT3XaU8GL0xGE4W4WufFoyRq1qPn8_GKIXqXWQWIK1cyqMYwdKOdQU_G9SY/s1600/Screenshot+from+2016-04-14+17%253A31%253A36.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitA7BslHFBVq12ScsiEGQgm7miVfa0zd9K8EUExgjDcBlp8B6RjztSjFqkI_1EZMY7M5HzsOFD76PPmYBJvwT3XaU8GL0xGE4W4WufFoyRq1qPn8_GKIXqXWQWIK1cyqMYwdKOdQU_G9SY/s1600/Screenshot+from+2016-04-14+17%253A31%253A36.png" /></a></div>
<br />
Apparently the trustlet is split into a few different files... Opening the files reveals a jumbled up mess - some files contain what looks like code, others contain ELF headers and metadata. In any case, before we can start disassembling the trustlet, we need to make some sense out of this format. We can either do this by opening each of the files and guessing the meaning of each blob, or by following the code-paths responsible for loading the trustlet - let's try a little of both.<br />
<br />
<h2>
Loading a Trustlet</h2>
<br />
In order to load a trustlet from the "Normal World", applications can use the libQSEECom.so shared object, which exports the function "<a href="http://androidxref.com/5.1.1_r6/xref/hardware/qcom/keymaster/QSEEComAPI.h">QSEECom_start_app</a>":<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOauQDNcoMZjECT9p19H5WPtulTCxT8kN0hrJXygy0sfdtNAuHj76T4jFAC1pdV8HG-WEsQ5wMSFw5MymKmQHK-h_30cDLdpzk4gjz2_QWsuzl6P2SakcN6hECxjbmDIuUrEx_7x7mClF0/s1600/Screenshot+from+2016-04-14+21%253A35%253A02.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOauQDNcoMZjECT9p19H5WPtulTCxT8kN0hrJXygy0sfdtNAuHj76T4jFAC1pdV8HG-WEsQ5wMSFw5MymKmQHK-h_30cDLdpzk4gjz2_QWsuzl6P2SakcN6hECxjbmDIuUrEx_7x7mClF0/s1600/Screenshot+from+2016-04-14+21%253A35%253A02.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUTMLj7u4w5NYgNXQHSHNbL-DADaMd5yUYClEHXP_bvSZx5w36UqgKHEBELsFzbPtE5EKnnFztp6a6B47WOu82hQbiYNg22Blwn8OtBzG6NlB2TozskgfbkC5EhGq0QHeyFWhyaZ355tEq/s1600/Screenshot+from+2016-04-14+21%253A33%253A54.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
Unfortunately this library's source code is not available, so we'll have to reverse engineer the function's implementation to find out what it does. Doing so reveals that it performs the following operations:<br />
<ul>
<li>Opens the /dev/qseecom device and calls some ioctls to configure it</li>
<li>Opens the ".mdt" file associated with the trustlet and reads the first 0x34 bytes from it</li>
<li>Calculates the number of ".bXX" files using the 0x34 bytes from the ".mdt"</li>
<li>Allocates a physically continuous buffer (using <a href="https://lwn.net/Articles/480055/">"ion"</a>) and copies the ".mdt" and ".bXX" files into it</li>
<li>Finally, calls a ioctl to load the trustlet itself, using the allocated buffer</li>
</ul>
So, still no luck on exactly how the images are loaded, but we're getting there.<br />
<br />
First of all, the number 0x34 might look familiar - this is the size of a (32 bit) ELF header. Opening the MDT file reveals that the first 0x34 bytes are indeed a valid ELF header:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0bmLM3l8fmp1lr9BxcTD4NeQcfC7C_f4Im2WSUytp1S0eTgfkBaMjXpehTQG99CJsHoxKdo3Lonpq2P6qrl8yE74DT2UvT9V0XDOPcUfQymQk6i68M6J7T5sWCvZo43IO93rPZVAjWtPa/s1600/Screenshot+from+2016-04-14+22%253A38%253A03.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="409" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0bmLM3l8fmp1lr9BxcTD4NeQcfC7C_f4Im2WSUytp1S0eTgfkBaMjXpehTQG99CJsHoxKdo3Lonpq2P6qrl8yE74DT2UvT9V0XDOPcUfQymQk6i68M6J7T5sWCvZo43IO93rPZVAjWtPa/s640/Screenshot+from+2016-04-14+22%253A38%253A03.png" width="640" /></a></div>
<br />
Moreover, the "QSEECOM_start_app" function we just had a look at used the word at offset 0x2C in order to calculate the number of ".bXX" files. As you can see above, this corresponds to the "e_phnum" field in the ELF header.<br />
<br />
Since the "e_phnum" field is usually used to specify the number of program headers, this hints that perhaps each of the ".bXX" files contains single segment of the trustlet. Indeed, opening each of the files reveals content the seems like it may be a segment of the program being loaded... But in order to make sure, we'll need to find the program headers themselves (and see if they match the ".bXX" files).<br />
<br />
Looking further, the next few chunks in the ".mdt" file are in fact the program headers themselves, one for each of the ".bXX" files present.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUmTGaEW_f3OPgCqFNX2wUt5OBmbCdg3dMJXiGrnDAUzKOwqx7hGRK8BcMRlp0zWYDIOJagzF0j87qZX3pIjNO0kHarnPLg8R7RwX4QSZOgocXeyNrwlhlZPQr0s78iJ41IxXFNltcSoQb/s1600/Screenshot+from+2016-04-22+15%253A24%253A28.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUmTGaEW_f3OPgCqFNX2wUt5OBmbCdg3dMJXiGrnDAUzKOwqx7hGRK8BcMRlp0zWYDIOJagzF0j87qZX3pIjNO0kHarnPLg8R7RwX4QSZOgocXeyNrwlhlZPQr0s78iJ41IxXFNltcSoQb/s1600/Screenshot+from+2016-04-22+15%253A24%253A28.png" /></a></div>
<br />
And, confirming our earlier suspicion, their sizes match the sizes of the ".bXX" files exactly. Great!<br />
<br />
Note that the first two program headers above look a little strange - they are both NULL-type headers, meaning they are "reserved" and should not be loaded into the resulting ELF image. Strangely, opening the corresponding ".bXX" files reveals that the first block contains the same ELF header and program headers present in the ".mdt", and the second block contains the rest of the ".mdt" file.<br />
<br />
In any case, here's a short schematic summing up what we know so far:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgd3Z7uOVL2gbg6Ob315hKmBpH84mfIO2qfqStJfCFF24rGnGzWwC4Cxunu8gl14hlDl8nsRSrIu0wrCpHQ0JWHblyow_cEiyqLSDcTyPARAtJVLvJyD1pePmRl2rG8VKEQ0Qqt-8lciNVf/s1600/Screenshot+from+2016-04-26+00%253A36%253A37.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="363" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgd3Z7uOVL2gbg6Ob315hKmBpH84mfIO2qfqStJfCFF24rGnGzWwC4Cxunu8gl14hlDl8nsRSrIu0wrCpHQ0JWHblyow_cEiyqLSDcTyPARAtJVLvJyD1pePmRl2rG8VKEQ0Qqt-8lciNVf/s400/Screenshot+from+2016-04-26+00%253A36%253A37.png" width="400" /></a></div>
<br />
<br />
Also, note that since the ELF header and the program headers are all present in the ".mdt", we can use "readelf" in order to quickly dump the information about program headers in the trustlet:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdSLSSwgGTVJ2x0Pwo-fFGrKy4JZ1j7zvGtggiT-IW8tsWbyLP9hb2yDRed-YbAtlaKCNxtwodZiwBLsFp629pVOOxvQzemE49Hjcx7pLkM_yxsafG4cs2DwsbYlyHVgaJ9EgiE2po_eij/s1600/Screenshot+from+2016-04-22+15%253A26%253A11.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdSLSSwgGTVJ2x0Pwo-fFGrKy4JZ1j7zvGtggiT-IW8tsWbyLP9hb2yDRed-YbAtlaKCNxtwodZiwBLsFp629pVOOxvQzemE49Hjcx7pLkM_yxsafG4cs2DwsbYlyHVgaJ9EgiE2po_eij/s1600/Screenshot+from+2016-04-22+15%253A26%253A11.png" /></a></div>
<br />
<br />
<br />
At this point we have all the information we need in order to create a complete and valid ELF file from the ".mdt" and ".bXX" files; we have the ELF header and the program headers, as well as each of the segments themselves. We just need to write a small script that will create an ELF file using this data.<br />
<br />
I've written a small python script which does just that. You can find it here:<br />
<br />
<a href="https://github.com/laginimaineb/unify_trustlet">https://github.com/laginimaineb/unify_trustlet</a><br />
<br />
<h2>
Reflections on Trusting Trustlets</h2>
<h2>
</h2>
By now have a basic understanding of how trustlets are assembled into an executable file, but we still don't know how they are verified. However, since we know the ".bXX" files contain <u>only</u> the segments to be loaded, this means that this data must reside in the ".mdt" file. <br />
<br />
So it's time for some guesswork - if we were to build a trusted loader, how would we do it?<br />
<br />
One very common paradigm would be to use hash-and-sign (relying on a <a href="https://en.wikipedia.org/wiki/Collision_resistance">CRHF</a> and a <a href="https://en.wikipedia.org/wiki/Digital_signature">digital signature</a>). Essentially - we calculate the hash of the data to be authenticated and sign it using a private key for which a corresponding public key is known to the loader.<br />
<br />
If that were the case, we'd expect to find two things in the ".mdt":<br />
<ul>
<li>A certificate chain</li>
<li>A signature blob</li>
</ul>
Let's start by looking for a certificate chain. There are <a href="https://blogs.msdn.microsoft.com/kaushal/2010/11/04/various-ssltls-certificate-file-typesextensions/">way too many</a><a href="https://www.blogger.com/null"> </a>formats for certificates, but since the ".mdt" file only contains binary data, we can assume it'll probably be a binary format, the most common of which is <a href="https://en.wikipedia.org/wiki/X.690#DER_encoding">DER</a>.<br />
<br />
There's a quick hack we can use to find DER encoded certificates - they almost always start with an "ASN.1 SEQUENCE" blob, which is encoded as: 0x30 0x82. So let's search for these two bytes in the ".mdt" and save each found blob into a file. Now, we can check if these blobs are well-formed certificates using "openssl": <br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9zR7hJ9hoQ1rr0xjDI5vRtpJfImFh3SpUe0c6INzIDiw4_Ll84gGuAnvcwNy0x8mEMHV0HW6KGFQ6IWMEeOSy4bE4A-cASNwsEMneNqLvFyhSNGNB35K3izVy2C2v7JhmLU3UB7-AaggE/s1600/Screenshot+from+2016-04-25+23%253A08%253A18.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9zR7hJ9hoQ1rr0xjDI5vRtpJfImFh3SpUe0c6INzIDiw4_Ll84gGuAnvcwNy0x8mEMHV0HW6KGFQ6IWMEeOSy4bE4A-cASNwsEMneNqLvFyhSNGNB35K3izVy2C2v7JhmLU3UB7-AaggE/s1600/Screenshot+from+2016-04-25+23%253A08%253A18.png" /></a></div>
<br />
Yup, we guessed correctly - those are certificates.<br />
<br />
In fact, the trustlet contains three certificates, one after the other. Just for good measure, we might also want to check that these three certificates are in fact a certificate chain which forms a valid chain of trust. We can do this by dumping the certificates to a single "certificate chain" file and using "openssl" to verify each certificate using this chain:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizSO0pyTPcVoEE0cdfDAFxjbqUZYiCCZoD9ts0XmXCUsMut4YsAWv6EsoyvH-OVDkNh8vy-kzvfYqRJwkn0D0QW2GX6qcAotumderW2BBrH6KLqwxCCMxwk9SvFrmTpPkkSZ8nSQS3Zp_I/s1600/Screenshot+from+2016-04-25+23%253A27%253A38.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizSO0pyTPcVoEE0cdfDAFxjbqUZYiCCZoD9ts0XmXCUsMut4YsAWv6EsoyvH-OVDkNh8vy-kzvfYqRJwkn0D0QW2GX6qcAotumderW2BBrH6KLqwxCCMxwk9SvFrmTpPkkSZ8nSQS3Zp_I/s1600/Screenshot+from+2016-04-25+23%253A27%253A38.png" /></a></div>
<br />
As for the root of trust of this chain - looking at the root certificate in the chain reveals the same root certificate which is used to verify all other parts of the boot chain in Qualcomm's "Secure Boot" process. There has been some research about this mechanism, which has shown that the validation occurs by comparing the SHA256 of the root certificate to a special value called "OEM_PK_HASH", which is "fused" into the devices QFuses during the production process. Since this value should theoretically not be modifiable after the production of the device, this means that forging such a root certificate would essentially require a second pre-image attack against SHA256.<br />
<br />
Now, let's get back to the ".mdt" - we've found the certificate chain, so now it's time to look for a signature. Normally, the private key is used to produce a signature and the public key can be used to recover the signed data. Since we have the public key of the top-most certificate in the chain, we can use it to go over the file and opportunistically try to "recover" each blob.<br />
<br />
But how will we know when we've succeeded?<br />
<br />
Recall that RSA is a trapdoor permutation family - every blob with the same number of bits as the public modulus N is mapped to another blob of the same size. <br />
<br />
However, while the RSA public modulus in our case is 2048 bits long, most hashes are <u>much</u> shorter than that (160 bits for SHA1, 256 bits for SHA256). This means that if we try to "decrypt" a blob using our public key and it happens to end with a lot of "slack" space (for example, zero bytes), there's a very good chance that this is the signature we're looking for (for a completely random permutation, the chance of <i>n</i> consecutive zero bits is <i>2^-n</i> - extremely small for even a moderate <i>n</i>)<br />
<br />
In order to do so, I wrote a small program which loads the public key from the top-most certificate in the chain and tries to "recover" each blob in the ".mdt" (using <a href="https://www.openssl.org/docs/manmaster/crypto/RSA_private_encrypt.html">rsa_public_decrypt</a> with PKCS #1 v1.5 padding). If the "recovered" blob ends with a bunch of zero bytes, the program outputs it. So... Running it on our ".mdt":<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKSjmfaZ534nAKQg_NUv5DrpB_IxgBirlcHPINxT3bnZ0nevkGHIp3kyyRSIvH1RWnsuqroyf1deiPPjEn7Tau8iTJXOf6d9wNFlxTCaovqLGfigR4AMl10E320vkiIaSvO6RSFpdsC75_/s1600/Screenshot+from+2016-04-25+23%253A58%253A43.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKSjmfaZ534nAKQg_NUv5DrpB_IxgBirlcHPINxT3bnZ0nevkGHIp3kyyRSIvH1RWnsuqroyf1deiPPjEn7Tau8iTJXOf6d9wNFlxTCaovqLGfigR4AMl10E320vkiIaSvO6RSFpdsC75_/s1600/Screenshot+from+2016-04-25+23%253A58%253A43.png" /></a></div>
<br />
We've found a signature! Great.<br />
<br />
What's more, this signature is 256 bits long, which implies that it may be a SHA256 hash... And if there's one SHA256 in the ".mdt", perhaps there are more? <br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinyMXKIU2g8whO-X9nGzAFZ17CqbWvnma0Fqe1ECDmrXnlkMeG78AjdsJH5z3wY3JOaPy4J89LDZr9FZVuB4sP2Ol4LBxoo39648zLPjoFTxfwss3dS1hQtm_y3MU6lSW_B0XLJsTc1F1Y/s1600/hashes.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinyMXKIU2g8whO-X9nGzAFZ17CqbWvnma0Fqe1ECDmrXnlkMeG78AjdsJH5z3wY3JOaPy4J89LDZr9FZVuB4sP2Ol4LBxoo39648zLPjoFTxfwss3dS1hQtm_y3MU6lSW_B0XLJsTc1F1Y/s1600/hashes.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-B5xpcvEBqTn4u6c5MBX0s-KTlMXy3Cl-urG9R6lTrv4bN3QX8gy5W8k4CYMh2fNXEPlLyJ7Asao0lBcPTPpWTJJ9sKnLtgsL9Z54EQn-r_3lBEbc1mDjV-bxev42xWNkG6j4kxf54_Iw/s1600/Screenshot+from+2016-04-26+00%253A09%253A10.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<br />
Lucky once again!<br />
<br />
As we can see, the SHA256 hashes for each of the ".bXX" files are also stored in the ".mdt", consecutively. We can also make an educated guess that this will be the data (or at least some of the data) that is signed to produce the signature we found earlier.<br />
<br />
Note that the ".b01" file's hash is missing - why is that? Remember that the ".b01" file contains all the data in the ".mdt" other than the ELF header and program headers. Since this data also contains the signature above, and the signature is (possibly) produced over the hashes of the block files, this would cause a circular dependency (since changing the block file would change the hash, which would change the signature, which would again change the block file, etc.). So it makes sense that this block's hash wouldn't be present.<br />
<br />
By now we've actually decoded all of the data in the ".mdt" file apart from a small structure which resides right after the program headers. However, after looking at it for a while, we can see that it simply contains pointers and lengths of the various parts of the ".mdt" that we've already decoded:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIFAfxtyZpCoJ3hD6LBc-lsk_XrCMsg2V4n6AzH4o6zaT4iULpTMoB7TA16hQprCo7D-xppMm_6JLShb1bdwbeitvFcEhpbxhwmp92W1YIirXlGHxzqum39PhQJZryn6tQZ02GgiZPioJl/s1600/Screenshot+from+2016-04-26+01%253A51%253A37.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIFAfxtyZpCoJ3hD6LBc-lsk_XrCMsg2V4n6AzH4o6zaT4iULpTMoB7TA16hQprCo7D-xppMm_6JLShb1bdwbeitvFcEhpbxhwmp92W1YIirXlGHxzqum39PhQJZryn6tQZ02GgiZPioJl/s1600/Screenshot+from+2016-04-26+01%253A51%253A37.png" /></a></div>
<br />
So finally, we've decoded all of the information in the ".mdt"... Phew. <br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgROYiz5vuFXT1sLqlQVym_N4KMnCvxGESP2pQMhaWfyhad1NIVBqO_v2d6bBE6YUbVeH0GjFcRsj3EyycKDxdT7TxzjkNFMdo91IuBGzJe3ufL09s682lVdUyuHLCjBJU6z2m0VENBOxjG/s1600/Screenshot+from+2016-04-26+01%253A53%253A44.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgROYiz5vuFXT1sLqlQVym_N4KMnCvxGESP2pQMhaWfyhad1NIVBqO_v2d6bBE6YUbVeH0GjFcRsj3EyycKDxdT7TxzjkNFMdo91IuBGzJe3ufL09s682lVdUyuHLCjBJU6z2m0VENBOxjG/s400/Screenshot+from+2016-04-26+01%253A53%253A44.png" width="383" /></a></div>
<h2>
Motorola's High Assurance Boot</h2>
<br />
Although the ".mdt" file format we've seen above is universal for all OEMs, Motorola decided to add a little twist.<br />
<br />
Instead of supplying an RSA signature like the one we saw earlier, they actually leave the signature blob empty (in fact, the signature I showed you earlier was from a Nexus 5). In fact, Motorola's signature looks like this:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiiB8erF9Yl5QE0kL2Kplok0yEY7_1KiFQhbGOkiDx05W2jVcU_v8U4MoKH7BzAQ_8kUnDM7D09TEfdtocvsm_F0TAc8JG_ETUPgMu6nszp3K8DN8daZCl7r7ySzczQNwWwav7LpB8ITtfc/s1600/Screenshot+from+2016-04-26+02%253A03%253A02.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiiB8erF9Yl5QE0kL2Kplok0yEY7_1KiFQhbGOkiDx05W2jVcU_v8U4MoKH7BzAQ_8kUnDM7D09TEfdtocvsm_F0TAc8JG_ETUPgMu6nszp3K8DN8daZCl7r7ySzczQNwWwav7LpB8ITtfc/s1600/Screenshot+from+2016-04-26+02%253A03%253A02.png" /></a></div>
<br />
So how is the image verified? <br />
<br />
This is done by using a mechanism which Motorola calls HAB ("<b>H</b>igh <b>A</b>ssurance <b>B</b>oot"). This mechanism allows them to verify the ".mdt" file by appending a certificate chain and a signature over the whole ".mdt" to the end of the file, encoded using a proprietary format used by "HAB":<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYa0UgtTt-y0ZA1iarUBb1LgmSrNyERYvhrzCfGm0ykAyYO1TPrU5nAP_N-CNS3hCuU7Sd-1Nh08qmwKpd0vJ8WcOyqGIPi-WYadeYkt6RxahPxdYnm9uV1MBUN6pO6oplgYlyiHJEV4br/s1600/Screenshot+from+2016-04-26+10%253A44%253A16.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYa0UgtTt-y0ZA1iarUBb1LgmSrNyERYvhrzCfGm0ykAyYO1TPrU5nAP_N-CNS3hCuU7Sd-1Nh08qmwKpd0vJ8WcOyqGIPi-WYadeYkt6RxahPxdYnm9uV1MBUN6pO6oplgYlyiHJEV4br/s1600/Screenshot+from+2016-04-26+10%253A44%253A16.png" /></a></div>
<br />
For more information about this mechanism, you can check out this <a href="http://iknowu.duckdns.org/files/public/Qualcomm-Secure-Boot/Qualcomm-Secure-Boot.htm">great research by Tal Aloni</a>. In short, the ".mdt" is hashed and signed using the top-most key in the certificate chain, while the root certificate in the chain is verified using a "Super Root Key", which is hard-coded in one of the bootloader's stages. <br />
<h2>
</h2>
<h2>
Life of a Trustlet</h2>
<h2>
</h2>
After the verification process we saw above, the TrustZone kernel loads the trustlet's segments into a secure memory region ("secapp-region") which is inaccessible from the "Normal World" and assigns an ID to it.<br />
<br />
Then, the kernel switches into "Secure World" user-mode and executes the trustlet's entry function:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdBtikEVgLRXt-QyKCwe1d_vzPVwB7CeqZ3gBVoSIHhOSX1J9Hs3roj0FQkNnOv2TFQO51zIRkWN2eQPDgFbt2TkPFchFoHUCzIu5Dfde6huEGf_SkV7QrSU1p6EGZHIrWK2yV7tg6-1Za/s1600/Screenshot+from+2016-04-26+13%253A43%253A38.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdBtikEVgLRXt-QyKCwe1d_vzPVwB7CeqZ3gBVoSIHhOSX1J9Hs3roj0FQkNnOv2TFQO51zIRkWN2eQPDgFbt2TkPFchFoHUCzIu5Dfde6huEGf_SkV7QrSU1p6EGZHIrWK2yV7tg6-1Za/s1600/Screenshot+from+2016-04-26+13%253A43%253A38.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVD8Cg6MFm-xGDzCp1uoHm1RNqre-bylay5mgzKki30kgi3E5K47A4zB3CaLwhMoc9wdpa28ng2W39MI7BTx5ePZV3dRGGrKJgQM14Bjp_37gLg19m62KcpujE2Z-j9dUpvVU0aUhZ2uRI/s1600/Screenshot+from+2016-04-26+13%253A33%253A39.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqrZfwmoShYbJrC7aAvvI7STfsnGxhKzQDDhVKetGN_QhgMUgsFysT7qQL27fAbJGq73JMMlknFi6_1Zs-4UcnAmopISO5GfFJVCbUjEE4K7-CN0NcZamzGgD4v1zNKgBSgRfsNJTVOpjb/s1600/Screenshot+from+2016-04-26+13%253A31%253A18.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
As you can see, the trustlet registers itself with the TrustZone kernel, along with a "handler function". After registering the trustlet, control is returned to the TrustZone kernel, and the loading process finishes.<br />
<br />
Now, once the trustlet is loaded, the "Normal World" can send commands to the trustlet by issuing a special SCM call (called "QSEOS_CLIENT_SEND_DATA_COMMAND") containing the loaded trustlet's ID and the request and response buffers. Here's what it looks like:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-LaNiZQUTG24sxMRl2Li-IJBHq_O94FYuACpIA74ju8VPfjkrlWDncqYZn762or37tVOOLZXGcSiakKXst8ZgaK5w4sk-7CiG6uTxRX6D2E7FY0tbfc3txGQfXDWRuMjin6VTHCkOQts1/s1600/Screenshot+from+2016-04-26+13%253A58%253A50.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-LaNiZQUTG24sxMRl2Li-IJBHq_O94FYuACpIA74ju8VPfjkrlWDncqYZn762or37tVOOLZXGcSiakKXst8ZgaK5w4sk-7CiG6uTxRX6D2E7FY0tbfc3txGQfXDWRuMjin6VTHCkOQts1/s1600/Screenshot+from+2016-04-26+13%253A58%253A50.png" /></a></div>
<br />
The TrustZone kernel (TZBSP) receives the SCM call, maps it to QSEOS, which then finds the application with the given ID and calls the handler function which was registered earlier (from "Secure World" user-mode) in order to serve the request.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvGoytClZmf_rPxA_D8qEQ7Gyqqs7JL3UVzUtaktiVSZScOLfEVbIuykCxsyGc7THW-fE_zf3ajWvfFUG8sH_bbr8E9RXlhW57prJKv6iUvrLWcQIiBlWgq-5uP61c3BGofDgezeyG4zdx/s1600/Screenshot+from+2016-04-26+14%253A10%253A52.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="529" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvGoytClZmf_rPxA_D8qEQ7Gyqqs7JL3UVzUtaktiVSZScOLfEVbIuykCxsyGc7THW-fE_zf3ajWvfFUG8sH_bbr8E9RXlhW57prJKv6iUvrLWcQIiBlWgq-5uP61c3BGofDgezeyG4zdx/s640/Screenshot+from+2016-04-26+14%253A10%253A52.png" width="640" /></a></div>
<br />
<br />
<h2>
What's Next? </h2>
<br />
Now that we have some understanding of what trustlets are and how they are loaded, we can move on to the exploits! In the next blog post we'll find a vulnerability in a very popular trustlet and exploit it in order to execute code within QSEE.<br />
<br />
<br />laginimainebhttp://www.blogger.com/profile/00867710965331025912noreply@blogger.com597tag:blogger.com,1999:blog-2029700426505953971.post-10115240944483707452016-02-10T21:27:00.001+02:002016-02-15T01:54:14.938+02:00Unlocking the Motorola BootloaderIn this blog post, we'll explore the Motorola bootloader on recent Qualcomm Snapdragon devices. Our goal will be to unlock the bootloader of a Moto X (2nd Gen), by using the TrustZone kernel code execution vulnerability from the <a href="http://bits-please.blogspot.co.il/2015/08/full-trustzone-exploit-for-msm8974.html">previous blog posts</a>. Note that although we will show the complete unlocking process for this specific device, it should be general enough to work at-least for most modern Motorola devices.<br />
<br />
<h2>
Why Motorola? </h2>
<br />
After reporting the <a href="http://bits-please.blogspot.co.il/2015/08/full-trustzone-exploit-for-msm8974.html">previous TrustZone kernel privilege escalation</a> to Qualcomm, I was gifted a shiny new Moto X. However... There was one little snag - they accidentally sent me a locked device. This was a completely honest mistake, and they did offer many times to unlock the device - but where's the fun in that? So without further ado, let's dive into the Motorola bootloader and see what it takes to unlock it.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9KIkJjEoGArMq9lBLdEKN0MuREyiwzNO7N_1ITx8JTZJg9u2Zh-eXYWVmtpwGqo6d0kFJ-KxxCrol_-Klb76XGL8EiO-YY3bakZLOG7e68OGnWd5dPEelHnU2bof9eRrmZUSaVa7KgQH_/s1600/bootloader.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9KIkJjEoGArMq9lBLdEKN0MuREyiwzNO7N_1ITx8JTZJg9u2Zh-eXYWVmtpwGqo6d0kFJ-KxxCrol_-Klb76XGL8EiO-YY3bakZLOG7e68OGnWd5dPEelHnU2bof9eRrmZUSaVa7KgQH_/s400/bootloader.png" width="282" /></a></div>
<br />
<a name='more'></a><br />
<h2>
Setting the Stage</h2>
<br />
Before we start our research, let's begin with a short introduction to the boot process - starting right at the point at which a device is powered on.<br />
<br />
First - the <b>PBL </b>(<b>P</b>rimary <b>B</b>oot <b>L</b>oader), also known as the "BootROM" is executed. Since the PBL is stored within an internal mask ROM, it cannot be modified or provisioned, and is therefore an intrinsic part of the device. As such, it only serves the very minimal purpose of allowing the device to boot, and authenticating and loading the next part of the boot-chain.<br />
<br />
Then, two secondary bootloaders are loaded, <b>SBL</b>1 (<b>S</b>econdary <b>B</b>oot<b> L</b>oader), followed by SBL2. Their main responsibility is to boot up the various processors on the SoC and configure them so that they're ready to operate.<br />
<br />
Next up in the boot-chain, the third and last secondary bootloader, SBL3, is loaded. This bootloader, among other tasks, verifies and loads the Android Bootloader - "aboot".<br />
<br />
Now this is where we get to the part relevant for our unlocking endeavours; the Android Bootloader is the piece of software whose responsibility is, as its name suggests, to load the Android operating system and trigger its execution.<br />
<br />
This is also the piece of boot-chain that OEMs tend to customize the
most, mainly because while the first part of the boot-chain is written by Qualcomm and deals with SoC specifics, the Android bootloader
can be used to configure the way the Android OS is loaded.<br />
<br />
Among
the features controlled by <i>aboot</i> is the "bootloader lock" - in other
words, <i>aboot</i> is the first piece of the boot-chain which can opt to break
the chain of trust (in which each bootloader stage verifies the next)
and load an unsigned operating system.<br />
<br />
For devices with
an unlockable bootloader, the unlocking process is usually performed by
rebooting the device into a special ("bootloader") mode, and issuing
the relevant <a href="https://wiki.cyanogenmod.org/w/Doc:_fastboot_intro">fastboot</a>
command. However, as we will later see, this interface is also handled
by <i>aboot</i>. This means that not only does <i>aboot</i> query the lock status
during the regular boot process, but it also houses the code
responsible for the actual unlocking process.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8TfXOWQrnF6M_bMfEPxZllBaNipU1PjWVr2l6GcRo46UQm8jWLow_mc8dmVKl0IC2dZUPAaFcF3MyUMZxW7nmY0Geog8jZEIRXycmVw1Sq7avL67FcmOVdH-kG-0B-sNZ2DEqW7tUamxg/s1600/Screenshot+from+2016-02-10+12%253A44%253A43.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8TfXOWQrnF6M_bMfEPxZllBaNipU1PjWVr2l6GcRo46UQm8jWLow_mc8dmVKl0IC2dZUPAaFcF3MyUMZxW7nmY0Geog8jZEIRXycmVw1Sq7avL67FcmOVdH-kG-0B-sNZ2DEqW7tUamxg/s1600/Screenshot+from+2016-02-10+12%253A44%253A43.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-OIgIewtVo6bxU7IJJJO5_wXUhOBGnyaSw6HDX9FKBpSd1HRCl0T24b9vYtzJ6Vah9Hle6V9irI7Y47FK6aYVRe7sHiNOU3bW8C5OWdV6uydJA4o1f4e_a6Y4mXP5w4AP9F-IJWfkfFAb/s1600/Screenshot+from+2016-02-10+12%253A35%253A13.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
As you may know, different OEMs take different stances on this issue. In short, "Nexus" devices always ship with an "unlockable" bootloader. In contrast, Samsung doesn't allow bootloader unlocking for most of its devices. Other OEMs, Motorola included, ship their devices locked, but certain devices deemed "eligible" can be unlocked using a "magic" (signed) token supplied by the OEM (although this also voids the warranty for most devices).<br />
<br />
So... it's all very complex, but also irrelevant. That's because we're going to do the whole process manually - if <i>aboot</i> can control the lock status of the device, this means we should probably be able to do so as well, given an elevated enough set of privileges.<br />
<br />
<h2>
Getting Started</h2>
<br />
Now that we have a general grasp of the components involved and of our goal, the next stage is to analyse the actual <i>aboot</i> code.<br />
<br />
Since the binaries for all stages of the boot-chain are contained within the factory firmware image, that would naturally be a good place to start. There are several download links available - <a href="http://forum.gsmhosting.com/vbb/f783/firmware-motorola-moto-x-abhayranasingh-firmware-1970675/">here are a few</a>. In case you would like to follow along with me, I'm going to refer to the symbols in the version "ATT_XT1097_4.4.4_KXE21.187-38".<br />
<br />
After downloading the firmware image, we are faced with our first challenge - the images are all packed using a proprietary format, in a file called "motoboot.img". However, opening the file up in a hex-editor reveals it has a pretty simple format we can deduce:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-vYNr_4-DBErbi4Nhi2fiGagqWM04Cg7kCib2SPV-xMX4RIMtqgbkhok0grJUHcEo0oMt_z1ltO3BiBOCsTNAzju12aBJJWXtGgx6e_k1jwQNIBdhbhXvUIblnw-pFNQlAt9hg5vbLwZr/s1600/Screenshot+from+2016-01-25+02%253A38%253A06.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="536" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-vYNr_4-DBErbi4Nhi2fiGagqWM04Cg7kCib2SPV-xMX4RIMtqgbkhok0grJUHcEo0oMt_z1ltO3BiBOCsTNAzju12aBJJWXtGgx6e_k1jwQNIBdhbhXvUIblnw-pFNQlAt9hg5vbLwZr/s640/Screenshot+from+2016-01-25+02%253A38%253A06.png" width="640" /></a></div>
<br />
As you can see above, the sought-after <i>aboot</i> image is stored within this file, along with the TrustZone image, and various stages of the boot-chain. Good. <br />
<br />
After analysing the structure above, I've written a python script which can be used to unpack all the images from a given Motorola bootloader image, <a href="https://github.com/laginimaineb/unpack_motoboot/blob/master/unpack_motoboot.py">you can find it here</a>.<a href="https://github.com/laginimaineb/unpack_motoboot/blob/master/unpack_motoboot.py"></a><br />
<br />
<h2>
Much ado aboot nothing</h2>
<br />
We'll start by inspecting the <i>aboot</i> image. Discouragingly, it is 1MB large, so going over it all would be a waste of time. However, as we've mentioned above, when booting the device into the special "bootloader" mode, the actual interaction with the user is provided by <i>aboot</i> itself. This means that we can start by searching for the strings which are displayed when the unlocking process is
performed - and continue from there.<br />
<br />
A short search for the "unlock..." string which is printed after starting the unlock process brings us straight to the function (@0xFF4B874) which deals with the unlocking logic:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidbRXwwtdFsDLT7yC58myhV7fpFE0z63hNWp2e5bOI-Da10AhyXlrjb4MLtxcUuhLoaw6MuV2Qb-jy1YunYyc39ISzxQnbtoE3BhjUpxR3DktwnTVma8iOnr9RxGEHn5ONI-NsfUp8Ur6E/s1600/Screenshot+from+2016-02-10+01%253A28%253A06.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidbRXwwtdFsDLT7yC58myhV7fpFE0z63hNWp2e5bOI-Da10AhyXlrjb4MLtxcUuhLoaw6MuV2Qb-jy1YunYyc39ISzxQnbtoE3BhjUpxR3DktwnTVma8iOnr9RxGEHn5ONI-NsfUp8Ur6E/s1600/Screenshot+from+2016-02-10+01%253A28%253A06.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEji6Gku8Ec2UOvAd1i6bN5oNOnxd-tkOZwr867-yLEDxNRz9R5Et49l-HIwp_GpaiZZ5Fof9FpC2UbcnkefNKNixGK-Dixwvapfv8dn6CTPT83Nr_akKFMbEj2j026gZ4vk9zktuQjS-lmh/s1600/Screenshot+from+2016-02-10+01%253A19%253A43.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
That was pretty fast!<br />
<br />
As you can see, after printing the string to the console, three functions are called consecutively, and if all three of them succeed, the device is considered unlocked.<br />
<br />
Going over the last two functions reveals their purpose is to erase the user's data partitions (which is always performed after the bootloader is unlocked, in order to protect the device owner's privacy). In any case, this means they are irrelevant to the unlocking process itself and are simply side-effects.<br />
<br />
This leaves us with a single function which, when called, should unlock the bootloader.<br />
<br />
So does this mean we're done already? Can we just call this function and unlock the device?<br />
<br />
Actually, not yet. Although the TrustZone exploit allows us to achieve code-execution within the TrustZone kernel, this is only done <u>after</u> the operating system is loaded, at which point, executing <i>aboot</i> code directly could cause all sorts of side-effects (since, for example, the code might assume that there is no operating system/the MMU could be disabled, etc.). And even if it were that simple, perhaps there is something interesting to be learned by fully understanding the locking mechanism itself.<br />
<br />
Regardless, if we can understand the logic behind the code, we can simply emulate it ourselves, and perform the meaningful parts of it from our TrustZone exploit. Analysing the unlocking function reveals a surprisingly simple high-level logic:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhI95IiKrMDE4o_hrNP-Wv0Kn01VFqyksxpvprQrhYmViKM76w8fTZ8S20ehFTVmnAslRhl-8ZDoC2ANgbicx6vJnLB2DDmuOY-f96Os1uliSxWQT9_10DivX9MtaxbdBTL3l842c1EP7cS/s1600/Screenshot+from+2016-02-10+01%253A37%253A55.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhI95IiKrMDE4o_hrNP-Wv0Kn01VFqyksxpvprQrhYmViKM76w8fTZ8S20ehFTVmnAslRhl-8ZDoC2ANgbicx6vJnLB2DDmuOY-f96Os1uliSxWQT9_10DivX9MtaxbdBTL3l842c1EP7cS/s1600/Screenshot+from+2016-02-10+01%253A37%253A55.png" /></a></div>
<br />
Unfortunately, these two functions wreak havoc within IDA (which fails to even display a meaningful call-graph for them).<br />
<br />
Manually analysing the functions reveals that they are in fact quite similar to one another. They both don't contain much logic of their own, but instead they prepare arguments and call the following function:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaxT9-mffumTk-MMbNcm3dMtH2s3w-ASIr2CZLYK_RV4Gv1zg4zSds6Kc6qv4Koh49Nvh-k35k4yFSla1tFnek1-L-ZbDyP7NuQWbkxbbyJWslc8yS6IZwOSBIDEZN6YY77Z2SJDyhpJSH/s1600/Screenshot+from+2016-02-10+15%253A04%253A00.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaxT9-mffumTk-MMbNcm3dMtH2s3w-ASIr2CZLYK_RV4Gv1zg4zSds6Kc6qv4Koh49Nvh-k35k4yFSla1tFnek1-L-ZbDyP7NuQWbkxbbyJWslc8yS6IZwOSBIDEZN6YY77Z2SJDyhpJSH/s1600/Screenshot+from+2016-02-10+15%253A04%253A00.png" /></a></div>
<br />
This is a little surprising - instead of handling the logic itself, this function issues an an <b>SMC</b> (<b>S</b>upervisor <b>M</b>ode <b>C</b>all) in order to invoke a TrustZone system-call from <i>aboot</i> itself! (as we've discussed in <a href="http://bits-please.blogspot.co.il/2015/03/getting-arbitrary-code-execution-in.html">previous blog posts</a>). In this case, both functions issue an SMC with the request code 0x3F801. Here is the relevant pseudo-code for each of them:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPKuHCPQHs7Z31jI5AIqZpVQ_pUF1hZn3pBtsvNCgdf1cor4-DZEn-WyM_K9GqE0bMDwzWTSWL9899eJ6faqXfqlwFNs0qExzPVnH8zBqNq1CopmhcSP2lfPeB11SnUAIeSu7UMvV5jlGa/s1600/Screenshot+from+2016-02-10+15%253A39%253A04.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPKuHCPQHs7Z31jI5AIqZpVQ_pUF1hZn3pBtsvNCgdf1cor4-DZEn-WyM_K9GqE0bMDwzWTSWL9899eJ6faqXfqlwFNs0qExzPVnH8zBqNq1CopmhcSP2lfPeB11SnUAIeSu7UMvV5jlGa/s1600/Screenshot+from+2016-02-10+15%253A39%253A04.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxg_CbPs2mD06CXjjQxJXa9uN6Tc1pL8qZMI-uL8x3Z6Gn8tJbwJF7Bm6JFnThda2Squqq6-polLkcWE197cqRcDneAc7mzWrF-opd8RZWP4KQDutuLyort0a8t4GIku5xh61u8oda3AYL/s1600/Screenshot+from+2016-02-10+15%253A36%253A15.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
At this point we've gleaned all the information we need from <i>aboot</i>, now lets switch over to the TrustZone kernel to find out what this SMC call does.<br />
<br />
<h2>
Enter Stage Left, TrustZone</h2>
<br />
Now that we've established that an SMC call is made with the command-code 0x3F801, we are left with the task of finding this command within the TrustZone kernel.<br />
<br />
Going over the TrustZone kernel system calls, we arrive at the following entry:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTWfu4jMkqjL2rj87WASJa6zttvoDe880xX94r4s0cerjv9_JzUw_HGqHz4oXe-VlxOMQH685HiAz-7L6gpjCYgDj4Oc_oyM1H7ge8PZIEYfEvp8f6c-DZBQsfJWXa_RoEBBiROCTvIUdQ/s1600/Screenshot+from+2016-02-10+02%253A15%253A25.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTWfu4jMkqjL2rj87WASJa6zttvoDe880xX94r4s0cerjv9_JzUw_HGqHz4oXe-VlxOMQH685HiAz-7L6gpjCYgDj4Oc_oyM1H7ge8PZIEYfEvp8f6c-DZBQsfJWXa_RoEBBiROCTvIUdQ/s1600/Screenshot+from+2016-02-10+02%253A15%253A25.png" /></a></div>
<br />
This is a huge function which performs widely different tasks based on the first argument supplied, which we'll call the "command code" from now on.<br />
<br />
It should be noted an additional flag is passed into this system-call indicating whether or not it was called from a "secure" context. This means that if we try invoking it from the Android OS itself, an argument will be passed marking our invocation is insecure, and will prevent us from performing these operations ourselves. Of course, we can get around this limitation using our TrustZone exploit, but we'll go into that later!<br />
<br />
As we've seen above, this SMC call is triggered twice, using the command codes #1 and #2 (I've annotated the functions below to improve readability):<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1T0VvTjo9DcGk-GDkg73M3cOa5eukC5-Q4a6fptGq2YWddWzW6NdLarpjQfVGOYU6PpRfoscrFUBYU7DFPdwGc6HTIGgWN36QkQuSU51u6jPpuRlXUm6-YyBxaKGrlmmavzAW4wkC1307/s1600/Screenshot+from+2016-02-10+02%253A22%253A02.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1T0VvTjo9DcGk-GDkg73M3cOa5eukC5-Q4a6fptGq2YWddWzW6NdLarpjQfVGOYU6PpRfoscrFUBYU7DFPdwGc6HTIGgWN36QkQuSU51u6jPpuRlXUm6-YyBxaKGrlmmavzAW4wkC1307/s1600/Screenshot+from+2016-02-10+02%253A22%253A02.png" /></a></div>
<br />
In short, we can see both commands are used to read and write (respectively) values from something called a "QFuse".<br />
<br />
<h2>
QFuses </h2>
<br />
Much like a real-life fuse, a QFuse is a hardware component which facilitates a "one-time-writeable" piece of memory. Each fuse represents a single bit; fuses which are in-tact represent the bit zero, and "blown" fuses represent the bit one. However, as the name suggests, this operation is irreversible - once a fuse is blown it cannot be "un-blown".<br />
<br />
Each SoC has it's own arrangement of QFuses, each with it's own unique purpose. Some fuses are already blown when a device is shipped, but others can be blown depending on the user's actions in order to change the way a specific device feature operates.<br />
<br />
Unfortunately, the information regarding the role of each fuse is not public, and we are therefore left with the single option of reversing the various software components to try and deduce their role.<br />
<br />
In our case, we call a specific function in order to decide which fuse we are going to read and write:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtdHdbVkuChiyXuM6YgIXNp2b0n9J_ZtL7uNouRiZ54_0HLCEqd5YS2MlvxH_f5_7M7v508TLJMgUjBNSIolrMxiqu2fPSBaBCo_84-7BVYsArVFqtN30_BsOF7_Xkq5TpNybIpXKQq3dU/s1600/Screenshot+from+2016-02-10+17%253A35%253A55.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtdHdbVkuChiyXuM6YgIXNp2b0n9J_ZtL7uNouRiZ54_0HLCEqd5YS2MlvxH_f5_7M7v508TLJMgUjBNSIolrMxiqu2fPSBaBCo_84-7BVYsArVFqtN30_BsOF7_Xkq5TpNybIpXKQq3dU/s1600/Screenshot+from+2016-02-10+17%253A35%253A55.png" /></a></div>
<br />
Since we call this function with the second syscall argument, in our case "4", this means we will operate on the fuse at address 0xFC4B86E8.<br />
<br />
<h2>
Putting it all together</h2>
<br />
Now that we understand the <i>aboot</i> and the TrustZone logic, we can put them together to get the full flow:<br />
<br />
<ul>
<li>First, aboot calls SMC 0x3F801 with command-code #1</li>
<ul>
<li>This causes the TrustZone kernel to read and return the QFuse at address 0xFC4B86E8</li>
</ul>
</ul>
<ul>
<li>Then, iff the first bit in the QFuse is disabled, aboot calls SMC 0x3F801 once more, this time with command-code #2</li>
<ul>
<li>This causes the TrustZone kernel to write the value 1 to the LSB of the aforementioned QFuse.</li>
</ul>
</ul>
Turns out to be very simple after all - we just need to set a single bit in a single QFuse, and the bootloader will be considered unlocked.<br />
<br />
But how can QFuses be written?<br />
<br />
<h2>
DIY QFuses</h2>
<br />
Luckily the TrustZone kernel exposes a pair of system-call which allow us to read and write a restricted set of QFuses - tzbsp_qfprom_read_row and tzbsp_qfprom_write_row, respectively. If we can lift those restrictions using our TrustZone exploit, we should be able to use this API in order to blow the wanted QFuse.<br />
<br />
Lets take a look at these restrictions within the tzbsp_qfprom_write_row system-call:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4xMZLuLNMY5oaM3vVMohqQo5uUIRr_H4U4tNMMp5UYhGfStTsuAEp8aH3qs6Hjb-29JYLBhBsKJeeqlhWmreHVPAaGzUb9pwgrzOCiJ9DdGU9np023tistWvVbROllQbxZ_YfbcmStgjV/s1600/Screenshot+from+2016-02-10+20%253A38%253A55.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4xMZLuLNMY5oaM3vVMohqQo5uUIRr_H4U4tNMMp5UYhGfStTsuAEp8aH3qs6Hjb-29JYLBhBsKJeeqlhWmreHVPAaGzUb9pwgrzOCiJ9DdGU9np023tistWvVbROllQbxZ_YfbcmStgjV/s1600/Screenshot+from+2016-02-10+20%253A38%253A55.png" /></a></div>
<br />
So first, there's a DWORD at 0xFE823D5C which must be set to zero in order for the function's logic to continue. Normally this flag is in fact set to one, thus preventing the usage of the QFuse calls, but we can easily enough overwrite the flag using the TrustZone exploit.<br />
<br />
Then, there's an additional function called, which is used to make sure that the ranges of fuses being written are "allowed":<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFvFm6D_-lr5aPOpphpLryRsa_kxdbKbDbMAC3x_0y9CojaemYBK4huDD4hJPYRO9LCgeGY63u39oM3ECCXkm9s0RhhRIRJH5_4u7U4JYhB_LWY4iANSU014C3lVRI_PnZueiZyHAxy55Y/s1600/Screenshot+from+2016-02-10+20%253A44%253A52.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFvFm6D_-lr5aPOpphpLryRsa_kxdbKbDbMAC3x_0y9CojaemYBK4huDD4hJPYRO9LCgeGY63u39oM3ECCXkm9s0RhhRIRJH5_4u7U4JYhB_LWY4iANSU014C3lVRI_PnZueiZyHAxy55Y/s1600/Screenshot+from+2016-02-10+20%253A44%253A52.png" /></a></div>
As we can see, this function goes over a static list of pairs, each denoting the start and end address of the allowed QFuses. This means that in order to pass this check, we can overwrite this static list to include all QFuses (setting the start address to zero and the end address to the maximal QFuse relative address - 0xFFFF).<br />
<br />
<h2>
Trying it out</h2>
<h2>
</h2>
Now that we have everything figured out, it's time to try it out ourselves! I've written some code which does the following:<br />
<ul>
<li>Achieves code-execution within TrustZone</li>
<li>Disables the QFuse protections</li>
<li>Writes the LSB QFuse in QFuse 0xFC4B86E8</li>
</ul>
I encourage you to check out the code here: <a href="https://github.com/laginimaineb/Alohamora">https://github.com/laginimaineb/Alohamora</a><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2MFlmxDq5-nD04w03FRmIZh4eczyPc3xFqWXNldlWEAc-9A8saKVw2X65e6IzmqSU3OhFZszR5xHd605ZzpxiQ6dEqo_xksX2fi3Kgk5PrLqTnF9bPUUU_FG47H_HhZKFj5Pjj8tofS25/s1600/motox_unlocked.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2MFlmxDq5-nD04w03FRmIZh4eczyPc3xFqWXNldlWEAc-9A8saKVw2X65e6IzmqSU3OhFZszR5xHd605ZzpxiQ6dEqo_xksX2fi3Kgk5PrLqTnF9bPUUU_FG47H_HhZKFj5Pjj8tofS25/s400/motox_unlocked.png" width="376" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFOqkNqo2pVto4D2MkfqGbltPXXUsUNQWyVy4W5jK2Xd-9tmSGfyzAoSZRSwYcQ96J6-xCfLaeBg-KWEc8vkc7AwxpcPAK_nGX7A8acxVDAEmDm6wNfrwpb9_K8-dR9ru0WAjzSretCnqf/s1600/motox_unlocked.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<br />
Have fun!<br />
<br />
<h2>
Final Thoughts</h2>
<h2>
</h2>
In this blog post we went over the flow controlled by a single QFuse. But, as you can probably guess, there are many different interesting QFuses out there, waiting to be discovered.<br />
<br />
On the one hand, blowing a fuse is really "dangerous" - making one small mistake can permanently brick you device. On the other hand, some fuses might facilitate a special set of features that we would like to enable.<br />
<br />
One such example is the "engineering" fuse; this fuse is mentioned throughout the <i>aboot </i>image, and can be used to enable an amazing range of capabilities such as skipping secure boot, loading unsigned peripheral images, having an unsigned GPT, and much more.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguCmQ1TsNcDIcTUl5GuB8kAx3B_rDCn6c_dHLAvc34LVZh1-ziRrrmH1L5WOGG49YC3axuAwp6LyXQs2JI3-_NwL-rcDerxjdky5GBI4RC81ZdEklSHQ9xFE1wK8fvlgEkwCGNaXCqp8w8/s1600/Screenshot+from+2016-02-10+21%253A10%253A39.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguCmQ1TsNcDIcTUl5GuB8kAx3B_rDCn6c_dHLAvc34LVZh1-ziRrrmH1L5WOGG49YC3axuAwp6LyXQs2JI3-_NwL-rcDerxjdky5GBI4RC81ZdEklSHQ9xFE1wK8fvlgEkwCGNaXCqp8w8/s1600/Screenshot+from+2016-02-10+21%253A10%253A39.png" /></a></div>
<br />
<br />
However, this fuse is blown in all consumer devices, marking the device as a "non-engineer" device, and disabling these features. But who knows, maybe there are other fuses which are just as important, which have not yet been discovered...<br />
<br />laginimainebhttp://www.blogger.com/profile/00867710965331025912noreply@blogger.com652tag:blogger.com,1999:blog-2029700426505953971.post-63701285043267560982016-01-24T15:54:00.002+02:002016-01-24T23:01:34.336+02:00Android privilege escalation to mediaserver from zero permissions (CVE-2014-7920 + CVE-2014-7921)In this blog post we'll go over two
vulnerabilities I discovered which, when combined, enable arbitrary code execution within the "mediaserver" process from any context, requiring no permissions whatsoever.<br />
<h2>
<b> </b></h2>
<h2>
<b>How bad is it?</b></h2>
<br />
The first vulnerability (CVE-2014-7921) was present in all Android version from 4.0.3 onwards. The second vulnerability (CVE-2014-7920) was present in all Android versions from 2.2 (!). Also, these vulnerabilities are not vendor specific and were present in all Android devices. Since the first vulnerability is only needed to bypass ASLR, and ASLR is only present (in a meaningful form) from Android 4.1 onwards, this means that these vulnerabilities allow code execution within "mediaserver" on any Android device starting from version 2.2.<br />
<br />
Although I reported both vulnerabilities in mid October 2014, they were unfortunately only fixed much later (see "Timeline" for full description, below) - in Android version 5.1! This means that there are many devices out there which are still vulnerable to these issues, so please take care.<br />
<br />
You can find the <a href="https://android.googlesource.com/platform/frameworks/av/+/36d1577%5E!/">actual patches here</a>. The patches were pushed to AOSP five months after the vulnerabilities were reported. <br />
<br />
That said, the Android security team was very pleasant to work with, and with other vulnerabilities I reported later on, were much more responsive and managed to solve the issues within a shorter time-frame.<br />
<br />
<a name='more'></a><h2>
<b>Where are we at?</b></h2>
<br />
Continuing our journey of <a href="http://bits-please.blogspot.co.il/2015/03/getting-arbitrary-code-execution-in.html">getting from zero permissions to TrustZone code execution</a>; after recently completing the task of <a href="http://bits-please.blogspot.co.il/2015/08/full-trustzone-exploit-for-msm8974.html">getting to TrustZone from the Linux kernel</a>, and after finding a way to <a href="http://bits-please.blogspot.co.il/2015/08/android-linux-kernel-privilege.html">gain code execution within the Linux kernel</a>, we are left with the final step of gaining the privileges needed in order to execute our kernel exploit.<br />
<br />
As mentioned in the <a href="http://bits-please.blogspot.co.il/2015/08/android-linux-kernel-privilege.html">previous blog post in the series</a>, in order to exploit the kernel vulnerability in the "qseecom" driver, an attacker must only satisfy one of the following
conditions:<br />
<ul>
<li>Gain execution within one of "mediaserver", "drmserver", "surfaceflinger" or "keystore"</li>
<li>Run within a process with the "system", "drm" or "keystore" user-ID</li>
<li>Run within a process with the "drmrpc" group-ID</li>
</ul>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdZzkN542fgvR3OhCmH72kWVS2GiIhgHCXTENqXQ1Pbi7GfXfZLeh4f8FTxAwFgWIO98tQXHmShGc4DEPBZBrWlUchh5Mt4IeN4cMUwMkfxAB4UwMfE4IX6gYzP2Sf96wARq643uPjPpU4/s1600/hobbit_map_final.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdZzkN542fgvR3OhCmH72kWVS2GiIhgHCXTENqXQ1Pbi7GfXfZLeh4f8FTxAwFgWIO98tQXHmShGc4DEPBZBrWlUchh5Mt4IeN4cMUwMkfxAB4UwMfE4IX6gYzP2Sf96wARq643uPjPpU4/s1600/hobbit_map_final.png" /></a></div>
<br />
<br />
In this blog post, we'll gain code execution within the "mediaserver" process, thus completing our journey from zero permissions to TrustZone kernel code execution. <br />
<h2>
<b> </b></h2>
<h2>
Diving in</h2>
<br />
As it's name suggests, the "mediaserver" process is in charge of all media-related tasks. In order to serve different media-related requests, the process exposes a large set of features in the form of four different services: <br />
<ul>
<li>"media.audio_policy" - Enables manipulation of different audio related policies, such as the volumes of different audio streams</li>
<li>"media.audio_flinger" - Main configuration endpoint for media-related tasks, such as recording audio, muting the phone, etc.</li>
<li>"media.camera" - Allows interaction with the device's cameras.</li>
<li>"media.player" - Allows the playback of many different media formats (for example, by using the "stagefright" library).</li>
</ul>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPgZs0WM1HTRBe_l9N4zwspX26Eaf39A8eEiMx7fo8zNpkMIPdzcSHOg8EzTH9ES-D10MOl3vONDZ0k_j2JayAu8SA_lVmiRDNxuw7bvLwKuZMLQWzrGBGXsHQ7lv91wvJOOUEJVcVpswE/s1600/attack_surfaces.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="232" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPgZs0WM1HTRBe_l9N4zwspX26Eaf39A8eEiMx7fo8zNpkMIPdzcSHOg8EzTH9ES-D10MOl3vONDZ0k_j2JayAu8SA_lVmiRDNxuw7bvLwKuZMLQWzrGBGXsHQ7lv91wvJOOUEJVcVpswE/s320/attack_surfaces.png" width="320" /></a></div>
<br />
<ul>
</ul>
As you've probably seen, lately there's been a lot of focus on the "media.player" service (ala Stagefright) , especially focusing on different media-parsing libraries which are utilised by it. However, in this post we'll cover two vulnerabilities in a different service - the "media.audio_policy" service.<br />
<br />
Usually, when registering an Android service, the actual implementation of the service is provided in the Java programming language. This means that finding memory corruption vulnerabilities is more difficult, since those would only present themselves in unique circumstances (using a native "JNI" call from Java code, delegating a feature to a native library, etc.).<br />
<br />
However, in the case of the "mediaserver" process, all of the services housed within the process are implemented in the C++ programming language, making the prospect of finding memory corruptions much more viable.<br />
<br />
Actually, implementing a service is quite a hard task to fulfil in a secure manner - recall when we previously discussed kernel vulnerabilities? Well, in order to prevent accidental access to user-provided data, the kernel uses a coding convention in which user-provided pointers are marked as "tainted". However, for interaction between userspace services, there is no such feature. This means that implementers of a service must always pay attention to the origin of the processed data, and can't trust it at all.<br />
<br />
<h2>
Let's get down to business</h2>
<br />
Here's the game plan - first of all, we'll need to look for a memory corruption vulnerability in the audio policy service. Then, we'll need to find a way to reliably exploit this vulnerability. This is usually made difficult by the presence of ASLR.<br />
<br />
For those of you who haven't encountered <b>ASLR </b>(<b>A</b>ddress <b>S</b>pace <b>L</b>ayout <b>R</b>andomization) yet, you should definitely check <a href="https://www.duosecurity.com/blog/a-look-at-aslr-in-android-ice-cream-sandwich-4-0">this link</a> for Android-specific information (and <a href="https://copperhead.co/2015/05/11/aslr-android-zygote">this link</a> to see the problems still present in Android's ASLR implementation).<br />
<br />
Now, without any further ado, let's take a look at the functionality exposed by the audio policy service. Unsurprisingly, we'll start at the <a href="http://androidxref.com/4.3_r2.1/xref/frameworks/av/services/audioflinger/AudioPolicyService.cpp">"main" function</a> of the "mediaserver" process:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMUbwJJHCX0c8QrSSZaKDImi1xK0VK5q53ktODKzmWw59AJxWYkDVqhyfnIQfcWrx5wojA-fOy8tOkaC91G8kAlYWJxgtIoJRdgyZpKAau3K3Ce7QqV77swScYOXPnz_SvwAIaAIk74gIJ/s1600/Screenshot+from+2015-09-26+18%253A47%253A57.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="110" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMUbwJJHCX0c8QrSSZaKDImi1xK0VK5q53ktODKzmWw59AJxWYkDVqhyfnIQfcWrx5wojA-fOy8tOkaC91G8kAlYWJxgtIoJRdgyZpKAau3K3Ce7QqV77swScYOXPnz_SvwAIaAIk74gIJ/s320/Screenshot+from+2015-09-26+18%253A47%253A57.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwIsFPX1kFHNjB4M-DwQEle52B9lYunqz6kMqDMRyfPWY_mM3rs5RtUVf-fMDvZynLBPDKbFAXC1wZF2Gemeozxq5Sw-W1MTlwZ_MKpUIXGCK2ZoxSA_-K85NVnG7239qSCbyviMp9QWuD/s1600/Screenshot+from+2015-09-26+18%253A47%253A57.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<br />
Looks straight-forward enough. However, looking deeper reveals that while both "AudioPolicyService" and "AudioFlinger" register themselves as the handlers for commands directed at the "media.audio_policy" and "media.audio_flinger" services respectively, they actually acts as a façades for the real concrete implementation, which is provided after going through several layers of abstraction.<br />
<br />
The end result is that the actual implementation for most functionality provided by "AudioPolicyService" and some of the functionality provided by "AudioFlinger" are in fact handled by a single class - "AudioPolicyManagerBase". As a result, this is the class we're going to be focusing on from now on.<br />
<br />
<h2>
Limited Write Primitive</h2>
<br />
Whenever a user would like to start an output stream on a particular output device (such as the front or back speakers), he may do so by calling the <a href="http://androidxref.com/4.4.4_r1/xref/hardware/libhardware_legacy/audio/AudioPolicyManagerBase.cpp#733">"startOutput" function</a>, provided by the audio policy service.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjkMgnNNQDqhNnVbb3P6jsVY8ulSX6QTZhImVY8OHXgq2-VE2NEo8jMxhk5q86oI91M3rjyDh-Din0rzpHJAP052T-h_TwYwCjr7XiHjTCsBdDPneZQ46wnd6oOJ2fajrBDWV1opkoF9rp/s1600/Screenshot+from+2015-09-26+19%253A17%253A07.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjkMgnNNQDqhNnVbb3P6jsVY8ulSX6QTZhImVY8OHXgq2-VE2NEo8jMxhk5q86oI91M3rjyDh-Din0rzpHJAP052T-h_TwYwCjr7XiHjTCsBdDPneZQ46wnd6oOJ2fajrBDWV1opkoF9rp/s1600/Screenshot+from+2015-09-26+19%253A17%253A07.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVZ6CIVRkKyBPinzL7RixD0Jia6IeZeqvMiRgRry997tdG8q1ekh38wcaNJ8mw1ch0wu6GP__H7TWUVMIMstUcIIfeI-tinonkGyr3uukklSsAHr2GyamZiWfXxfPei6kVFCUM2DedoCJY/s1600/Screenshot+from+2015-09-26+19%253A15%253A52.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
This function receives three arguments: <br />
<ul>
<li>The output descriptor - this must be a device (such as the front or back speakers).</li>
<li>The type of stream for which the output should be opened (should be one of the predefined stream types).</li>
<li> The session ID - this should be a number corresponding to a previously opened session.</li>
</ul>
Initially, the function verifies the "output" parameter by fetching the AudioOutputDescriptor object corresponding to the given output device. This means that this argument must, in fact, be valid.<br />
<br />
But what about the other two arguments? Well, peering a little further reveals the following call:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkll7XCsf06nLOwUObFAnBvuSznpBZIgjxGKh7z2NMUsZ1h85UotcGzY5x9BsEsF-msGbUNvY6qSsEUAPHyCHS05m-bi0LbvYux5mxMNg0PiDVWkSClIcAi-fmnSy7HZir4xDpRNjzmJx_/s1600/changeRefCount.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkll7XCsf06nLOwUObFAnBvuSznpBZIgjxGKh7z2NMUsZ1h85UotcGzY5x9BsEsF-msGbUNvY6qSsEUAPHyCHS05m-bi0LbvYux5mxMNg0PiDVWkSClIcAi-fmnSy7HZir4xDpRNjzmJx_/s1600/changeRefCount.png" /></a></div>
<br />
Doesn't seem too shady, but let's just make sure the stream argument is safely handled:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZywbOnXLE3sHS2LmFjTBmUZMyk2p0d-nEiI1Cz-QD8asifm6fendCpgCZMor-h64vOVjo87KZgFFsfYGYvDZZAS1RgQNdGAAJtW1Ynylt_MSiTLNjhlIcxyVAFA14OSYQWHiVDIa6NfZE/s1600/changeRefCountImpl.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZywbOnXLE3sHS2LmFjTBmUZMyk2p0d-nEiI1Cz-QD8asifm6fendCpgCZMor-h64vOVjo87KZgFFsfYGYvDZZAS1RgQNdGAAJtW1Ynylt_MSiTLNjhlIcxyVAFA14OSYQWHiVDIa6NfZE/s1600/changeRefCountImpl.png" /></a></div>
<br />
Oh.<br />
<br />
So - as we can see above, the function uses the "stream" argument as an index into an array (of 32-bit values) within the AudioOutputDescriptor object - and both reads and writes to that address, without ever sanitizing the stream number. We're off to a good start already!<br />
<br />
In reality, there are only a <a href="http://androidxref.com/4.4.4_r1/xref/hardware/libhardware_legacy/include/hardware_legacy/AudioSystemLegacy.h">handful of valid stream_type values</a> (it is in fact an enumerated type), so adding appropriate validation is an easy as checking that the given argument is within the enumerations minimal and maximal values:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbgFJpcxVWXUFo3TGKTJqHLaeIGLaOyxmxQFYf1uIoJquQdcEqCINXnJ_fS-82FWjq1es1D3Z4lz_Iov6ojk5GO0-axrkUXtj9sqbOdEQep2ODVOEMFM4tvha85dDuVdOqeIGw96YE6WBA/s1600/Screenshot+from+2016-01-23+22%253A48%253A36.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbgFJpcxVWXUFo3TGKTJqHLaeIGLaOyxmxQFYf1uIoJquQdcEqCINXnJ_fS-82FWjq1es1D3Z4lz_Iov6ojk5GO0-axrkUXtj9sqbOdEQep2ODVOEMFM4tvha85dDuVdOqeIGw96YE6WBA/s1600/Screenshot+from+2016-01-23+22%253A48%253A36.png" /></a></div>
<br />
Regardless - there are still some constraints we need to figure out. First and foremost, in order to avoid unnecessary side-effects, we would like to choose an output descriptor which is not "duplicated" (so as not to execute the first block). Luckily, this is easy - most output descriptors are in fact not duplicated by default. <br />
<br />
Moving on, when would the second block in this function execute? Well, since the "delta" argument is always 1, this means we'll enter the block iff <i><span style="background-color: #cccccc;">(int)mRefCount[stream] + 1</span></i> is negative. Meaning, if the value pointed to is larger than or equal to 0x7FFFFFFF (since we're dealing with a 32-bit system).<br />
<br />
If that were to happen, the actual value would be logged to the system log (an info leak!), and would then be zeroed out before returning from the function. Although this is a nice info-leak, it has two obvious downsides (and another one which I won't cover in this post):<br />
<ul>
<li>Reading the leaked value requires the READ_LOGS permission (and we originally stated we would like to start with <u>zero permissions</u>)</li>
<li>The value being read is corrupted - this could be troublesome for quite a few exploitation techniques. </li>
</ul>
But all is definitely not lost; we can still create a much stronger primitive using this vulnerability. Assuming the second if block is not executed, we arrive at the function's end:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZxk-Cczq4wQm1ETALccGI2jD-m97atOkdieCaVQVYsOCDU4fuOKPxpJ9UoZD3P7X8dxbeSHG1p0aTOcZVUzbhnb2gNzcrJPkvx4FU3x9Kib7ppyOH20rJvdzbaKyeqBeiJkTh540iELqo/s1600/Screenshot+from+2016-01-23+22%253A34%253A03.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZxk-Cczq4wQm1ETALccGI2jD-m97atOkdieCaVQVYsOCDU4fuOKPxpJ9UoZD3P7X8dxbeSHG1p0aTOcZVUzbhnb2gNzcrJPkvx4FU3x9Kib7ppyOH20rJvdzbaKyeqBeiJkTh540iELqo/s1600/Screenshot+from+2016-01-23+22%253A34%253A03.png" /></a></div>
<br />
So the target value is incremented by one; a limited write primitive. Note that the final log statement is not actually included in a release build (since ALOGV is an empty macro is those builds).<br />
<br />
Putting this all together, we get a write primitive allowing us to increment the value at <i><span style="background-color: #cccccc;">mRefCount[stream]<span style="background-color: white;"> </span></span></i><span style="background-color: #cccccc;"><span style="background-color: white;">by one, so long as it is not larger than or equal to 0x7FFFFFFF.</span></span><br />
<br />
<h2>
I spy with my little eye</h2>
<br />
Now that we have a write primitive, let's look for a read primitive. Also, since our write primitive is relative to an AudioOutputDescriptor object, which is dynamically allocated (and is therefore located in a rather unpredictable location in the heap), it would be much more convenient if we were able to find such a primitive which is also relative to an AudioOutputDescriptor object.<br />
<br />
Pouring over the AudioPolicyManagerBase's methods once more, reveals a very tempting target; the <a href="http://androidxref.com/4.4.4_r1/xref/hardware/libhardware_legacy/audio/AudioPolicyManagerBase.cpp#1327">AudioPolicyManagerBase::isStreamActive</a> method. This method allows a user to query a given stream in order to check if it was active in any of the output descriptors within the user-supplied time-frame:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqbau1DhQs-a8m7gz1MffUDr7KiPqIsS17QkesECARHld7NY4t-RWPr9F15TaoiUmpxjE0o_RxhUrEhk7ziqo5wqk0TZbxjL_Asb_skQOtywT1zAF_HypvanKuGkbW-xlVx8yPEHo3prDR/s1600/Screenshot+from+2016-01-23+22%253A52%253A20.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqbau1DhQs-a8m7gz1MffUDr7KiPqIsS17QkesECARHld7NY4t-RWPr9F15TaoiUmpxjE0o_RxhUrEhk7ziqo5wqk0TZbxjL_Asb_skQOtywT1zAF_HypvanKuGkbW-xlVx8yPEHo3prDR/s1600/Screenshot+from+2016-01-23+22%253A52%253A20.png" /></a></div>
<br />
So once again - this method performs no validation at all on the given "stream" argument. Perhaps the validation is delegated to the internal <a href="http://androidxref.com/4.4.4_r1/xref/hardware/libhardware_legacy/audio/AudioPolicyManagerBase.cpp#3351">AudioOutputDescriptor::isStreamActive</a> method?<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcvDImc58pHQm-fMsA5yRq__dGDAMFikJCe7S8ZNlX2a3ATJeRnlhOYS9G22d1mavzrhMcp5Bn-_PNMEZjpTm_V2JNirtzHJK9OjknipeV4OqP1NqPwezJjuW1hJDGfauZtYcQCbe-POQj/s1600/Screenshot+from+2016-01-23+22%253A54%253A51.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcvDImc58pHQm-fMsA5yRq__dGDAMFikJCe7S8ZNlX2a3ATJeRnlhOYS9G22d1mavzrhMcp5Bn-_PNMEZjpTm_V2JNirtzHJK9OjknipeV4OqP1NqPwezJjuW1hJDGfauZtYcQCbe-POQj/s1600/Screenshot+from+2016-01-23+22%253A54%253A51.png" /></a></div>
<br />
Nope - lucky once again!<br />
<br />
So, once more we access the mRefCount member of the AudioOutputDescriptor using the "stream" argument as a index (while performing no validation whatsoever). As we can see, there are two cases in which this function would return true:<br />
<ul>
<li>If <i><span style="background-color: #cccccc;">mRefCount[stream] != 0</span></i></li>
<li><span style="background-color: #cccccc;"><span style="background-color: white;"></span></span><i><span style="background-color: #cccccc;"><span style="background-color: white;"></span> </span></i>Otherwise, if the time difference between the current system time and the value of <i><span style="background-color: #cccccc;">mStopTime[stream]<span style="background-color: white;"> </span></span></i><span style="background-color: #cccccc;"><span style="background-color: white;">is less than the user-supplied argument - <i>inPastMs</i>. </span></span><i><span style="background-color: #cccccc;"><br /></span></i></li>
</ul>
Since we would like to use this vulnerability as an easy read primitive, we would first seek to eliminate side-effects. This is crucial as it would make the actual exploit much easier to build (and much more modular).<br />
<br />
However, simply passing in the argument "inPastMs" with the value 0x80000000 (i.e., INT_MIN), would cause the last if statement to always evaluate to false (since there are no integers smaller than INT_MIN).<br />
<br />
This leaves us with a simple and "clean" (albeit somewhat weak) read primitive: the function isStreamActive will return true iff the value at <i><span style="background-color: #cccccc;">mRefCount[stream]</span></i><span style="background-color: #cccccc;"><span style="background-color: white;"> is not zero. Since the stream argument is fully controlled by us, we can use it to "scan" the memory relative to the AudioOutputDescriptor object, and to gage whether or not it contains a zero DWORD.</span></span><br />
<br />
<h2>
Thermal Vision</h2>
<br />
At this point you might be wondering - how can you even call this a read-primitive? After all, the only possible information we can learn using this vulnerability is whether or not the value at a given address is zero. Glad you (kind-of) asked!<br />
<br />
In fact, this is more than enough for us to find our way around the heap. Instead of thinking in terms of "heap" and "memory", let's use our imagination.<br />
<br />
You're a secret agent out on a mission. You're standing behind a closed door, leading to the room you need to enter. So what do you naturally do? Turn on your thermal vision goggles, of course. The goggles present you with the following image:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTRAsoGo0ugJ1cQUaKTnKfDuC1wvRT5QZsjEJrQ7VfiS3Lpr-XcwhYVGj4sWev_TNT_pu-3nStsdypwKLz9bzixf0qNG_nyNBqCx6PYimWDrVqKz8uE7ncI1-ooSlhVu_GkbmlGrOwpbvc/s1600/flir_dog.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTRAsoGo0ugJ1cQUaKTnKfDuC1wvRT5QZsjEJrQ7VfiS3Lpr-XcwhYVGj4sWev_TNT_pu-3nStsdypwKLz9bzixf0qNG_nyNBqCx6PYimWDrVqKz8uE7ncI1-ooSlhVu_GkbmlGrOwpbvc/s320/flir_dog.jpg" width="318" /></a></div>
<br />
<br />
So it's safe - we can see it's only a dog.<br />
<br />
Let's look at the image again - did we really need all the heat information? For example, what if we only had information if a given pixel is "hot" or not?<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuBg-dmmyweDVx62cvNrtR8i6KEJ3BnY0LF472cLAmH_0We-ULgn-kcdX8LOuYWk-7nXDthnJieZzrBdgUJQfWoxbzhwkAYRZFrKUsYRJHwsrlQgQbRnzn0iN1zykeuzapd06pwa13aETi/s1600/flir_dog_outline.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuBg-dmmyweDVx62cvNrtR8i6KEJ3BnY0LF472cLAmH_0We-ULgn-kcdX8LOuYWk-7nXDthnJieZzrBdgUJQfWoxbzhwkAYRZFrKUsYRJHwsrlQgQbRnzn0iN1zykeuzapd06pwa13aETi/s320/flir_dog_outline.jpg" width="318" /></a></div>
<br />
<br />
Still definitely recognizable.<br />
<br />
This is because the outline of the dog allowed us to create a "heat signature", which we could then use to identify dogs using our thermal goggles.<br />
<br />
So what about heaps and memory? Let's say that when a value in memory is non-zero, it is "hot", and otherwise, that memory location is "cold". Now - using our read-primitive, we can create a form of thermal vision goggles, just like the pair we imagined a minute ago.<br />
<br />
All that remains to be seen is whether or not we can create good "heat signatures" for objects we are interested in.<br />
<br />
First, looking at a histogram of a full memory dump of the heap in the mediaserver process, reveals that the value zero is <u>by far</u> the most common:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiz71zqypCFVw3_FTsCUM7kurAWDOh_n8mSx8lZE2lhdVU4P2jX6MbdZ7JZkQKz0rQPez__jgqc9PWJuAKJQpE8gQQnxkHPMzGIPtjBPN3dZNefaCyMjjV0UGgZr5gtVA8ggTgIrRwQKBIV/s1600/Screenshot+from+2016-01-24+00%253A36%253A16.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiz71zqypCFVw3_FTsCUM7kurAWDOh_n8mSx8lZE2lhdVU4P2jX6MbdZ7JZkQKz0rQPez__jgqc9PWJuAKJQpE8gQQnxkHPMzGIPtjBPN3dZNefaCyMjjV0UGgZr5gtVA8ggTgIrRwQKBIV/s1600/Screenshot+from+2016-01-24+00%253A36%253A16.png" /></a></div>
<br />
Moreover, typical heap objects appear to have many zeros within them, leading to some interesting repeatable patterns. Here is a heat-map generated from the aforementioned heap dump:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJHUvUT3vbZrqhfbHteI-kbhp_PujzVOH7z1T2c7yTdjuTBj2KPYGNvTIW_xjpSXl_Wr8Ujw0Yq4C6K_50JFKZhBEH85tveTFTtzm6DsWD9J_5HJPprZfMFZv45wInzhgn_5HxcDbBFQf0/s1600/Screenshot+from+2016-01-24+00%253A44%253A30.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="256" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJHUvUT3vbZrqhfbHteI-kbhp_PujzVOH7z1T2c7yTdjuTBj2KPYGNvTIW_xjpSXl_Wr8Ujw0Yq4C6K_50JFKZhBEH85tveTFTtzm6DsWD9J_5HJPprZfMFZv45wInzhgn_5HxcDbBFQf0/s400/Screenshot+from+2016-01-24+00%253A44%253A30.png" width="400" /></a></div>
<br />
Now - looking at the binary heat-map we can see there are still many interesting patterns we can use to try and "understand" which objects we are observing:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7BTzyhyphenhyphenDY6-XNRn8JrYSGhRZJxlfjI9lOXnX-31dPSyP4MAXmaSXSncZR6JpMd3Iwg1hbl5rvXWGHzIxPznmCdquAtBrdUf6_2HVP99FXbrYdmIlSxbD54aV7rgj03lWABmmazJden28y/s1600/Screenshot+from+2016-01-24+00%253A47%253A14.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="260" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7BTzyhyphenhyphenDY6-XNRn8JrYSGhRZJxlfjI9lOXnX-31dPSyP4MAXmaSXSncZR6JpMd3Iwg1hbl5rvXWGHzIxPznmCdquAtBrdUf6_2HVP99FXbrYdmIlSxbD54aV7rgj03lWABmmazJden28y/s400/Screenshot+from+2016-01-24+00%253A47%253A14.png" width="400" /></a></div>
<br />
So now that you're (hopefully) convinced, we can move on to building an actual exploit!<br />
<br />
<h2>
Building an exploit</h2>
<br />
As we've established above, we now have two tools in our belt:<br />
<ul>
<li>We can increment any value, so long as it's lower than 0x7FFFFFFF </li>
<li>We can inspect a memory location in order to check if it contains the value zero or not</li>
</ul>
In order to take this one step further, it would be nice if we were able to find an object that has a very "distinct" heat-signature, and which also contains pointers to functions which we can call (using regular API calls), and to which we can pass controllable arguments.<br />
<br />
Searching around for a bit, reveals a prime candidate for exploitation - <a href="http://androidxref.com/4.4.4_r1/xref/hardware/libhardware/include/hardware/audio.h#438">audio_hw_device</a>. This is a structure holding many function pointers for the implementations of each of the functions provided by an actual audio hardware device (it is part of the audio hardware abstraction layer). Moreover, these function pointers can also be triggered at ease simply by calling different parts
of the audio policy service and audio flinger APIs.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh06oa553ritF0lTVt0ryc_zt0eKliK0tnHKbLmzzRi42669ozRfR3J5wrGyuTCrLk6m-zQ0HLCspHjaIZ7lqTUw3kebM1eBOq1j8MNryYblzDkg2XaYiqDcZqxPfivCRPYhyphenhyphentvAifcEj64/s1600/Screenshot+from+2016-01-24+02%253A14%253A31.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh06oa553ritF0lTVt0ryc_zt0eKliK0tnHKbLmzzRi42669ozRfR3J5wrGyuTCrLk6m-zQ0HLCspHjaIZ7lqTUw3kebM1eBOq1j8MNryYblzDkg2XaYiqDcZqxPfivCRPYhyphenhyphentvAifcEj64/s1600/Screenshot+from+2016-01-24+02%253A14%253A31.png" /></a></div>
<br />
<br />
However, what makes this object especially interesting is its structure - it begins with a header with a fixed length initialized with non-zero values. Then, it contains a large block of "reserved" values, which are initialized to zero, followed by a large block of function pointers, of whom only the second one is initialized to zero.<br />
<br />
This means <i>audio_hw_device</i> objects have quite a unique heat signature:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpGQr9j1aHj3cXVvLx-absvitAXAaL5gJNuaF7Nb0poYzYXx3f9emYM0ea24NbJOojKC21R8ezGjch26Wl2ysOKL22WqJGBARm42-CqEb2DFIUZfe53p6Q4PrZv8q-2Kns3c3B0AkyMzk5/s1600/Screenshot+from+2016-01-24+02%253A17%253A33.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpGQr9j1aHj3cXVvLx-absvitAXAaL5gJNuaF7Nb0poYzYXx3f9emYM0ea24NbJOojKC21R8ezGjch26Wl2ysOKL22WqJGBARm42-CqEb2DFIUZfe53p6Q4PrZv8q-2Kns3c3B0AkyMzk5/s1600/Screenshot+from+2016-01-24+02%253A17%253A33.png" /></a></div>
<br />
So we can easily find these objects, great! Now what?<br />
<br />
Let's sketch a game-plan:<br />
<ul>
<li>Search for a <i>audio_hw_device</i> using its heat signature</li>
<li>Create a "stronger" read primitive (using the existing primitives)</li>
<li>Create a "stronger" write primitive (again, using the existing primitives)</li>
<li>Using the new primitives, hijack a function to execute arbitrary code</li>
</ul>
We've already seen how we can search for a <i>audio_hw_device</i> by using the heat signature mentioned above, but what about creating new primitives?<br />
<br />
<h2>
Harder Better Faster Stronger (primitives)</h2>
<br />
In order to do so, we would like to hijack a function within the <i>audio_hw_device</i> structure with the following properties:<br />
<ul>
<li>We can easily trigger a call to this function by invoking external API calls</li>
<li>The function's return value is returned to the user </li>
<li>The arguments to this function are completely user-controlled </li>
</ul>
Reading through the different API calls once more, we arrive at the perfect candidate; <a href="http://androidxref.com/4.4.4_r1/xref/frameworks/av/services/audioflinger/AudioFlinger.cpp#993">AudioFlinger::getInputBufferSize</a>:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZ3VxRgBnZmxl6CsA1MR3A_WF2hy1DgpD49gYOI9C5sUDlduZqKSCEAl-CGaVRC-K3X88qoLbWX5viaG-P7uEcVrAgUeMqWlCG1EPUCT4t2gCuxC80ZsSDXHI4b_ihZrdZpwANDKcxZNCW/s1600/Screenshot+from+2016-01-24+02%253A28%253A57.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZ3VxRgBnZmxl6CsA1MR3A_WF2hy1DgpD49gYOI9C5sUDlduZqKSCEAl-CGaVRC-K3X88qoLbWX5viaG-P7uEcVrAgUeMqWlCG1EPUCT4t2gCuxC80ZsSDXHI4b_ihZrdZpwANDKcxZNCW/s1600/Screenshot+from+2016-01-24+02%253A28%253A57.png" /></a></div>
<br />
As you can see, an <i>audio_config</i> structure is populated using the user-provided values, and is then passed on to the audio hardware device's implementation - <i>get_input_buffer_size</i>.<br />
<br />
This means that if we find our <i>audio_hw_device</i>, we can modify the <i>get_input_buffer_size</i> function pointer to point to any gadget we would like to execute - and whichever value we return from that gadget, will be simply returned to the user.<br />
<br />
<h2>
Creating the primitives</h2>
<br />
First of all, we would like to find out the real memory address of the <i>audio_hw_device</i> structure. This is useful in case we would like to pass a pointer to a location within this object at a later stage of the exploit.<br />
<br />
This is quite easily accomplished by using our weak write primitive in order to increment the value of the <i>get_input_buffer_size</i> function pointer so that it will point to a "BX LR" gadget - i.e., instead of performing any operation, the function will simply return.<br />
<br />
Since the first argument provided to the function is a pointer the <i>audio_hw_device</i> structure itself, this means it will be stored in register R0 (according to the ARM calling convention), so upon executing our crafted return statement, the value returned will be the value of R0, namely, the pointer to the <i>audio_hw_device</i>.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgl61yZQhROwM0HzB3RGwkD0Ll3rMviVU6w-OgVRZK-JiaU4zbNtduH8zWGQM59HxTiPHIjjMmj0YWD60IUj6faaXCvIahLzyZa96vUi42Pgn5_65qpnjrnZvMoslOvMXx2S9-ap0DB0il6/s1600/Screenshot+from+2016-01-24+03%253A37%253A08.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgl61yZQhROwM0HzB3RGwkD0Ll3rMviVU6w-OgVRZK-JiaU4zbNtduH8zWGQM59HxTiPHIjjMmj0YWD60IUj6faaXCvIahLzyZa96vUi42Pgn5_65qpnjrnZvMoslOvMXx2S9-ap0DB0il6/s1600/Screenshot+from+2016-01-24+03%253A37%253A08.png" /></a></div>
<br />
<br />
Now that we have the address of the <i>audio_hw_device</i>, we would like to also read an address within one of the loaded libraries. This is necessary so that we'll be able to calculate the absolute location of other loaded libraries and gadgets within them.<br />
<br />
However, as we've seen before, the <i>audio_hw_device</i> structure contains many function pointers - all of whom point to the text segment of one of the loaded libraries. This means that reading any of these function pointers is sufficient for us to learn the location of the loaded libraries.<br />
<br />
Moreover, since the <i>get_input_buffer_size</i> function receives the <i>audio_hw_device</i> as its first argument, we can search for any gadget which reads into R0 a value from R0 at an offset which falls within the function pointer block range, and returns. There are many such gadgets, so we can simply chose one:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyZajIKT5NQFmSfDLl-11M-uiiAQtCHMfjPgjQ4YNL-EP28xMOaDmdO8AK2JAHTT4BMkQpWmacaP3r8AjdtlMRqWU-40D3TJGEZOL_cYTLhSn_QmGNahf0uVCifZ74erlVHyD53nGJdc9l/s1600/Screenshot+from+2016-01-24+03%253A44%253A13.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyZajIKT5NQFmSfDLl-11M-uiiAQtCHMfjPgjQ4YNL-EP28xMOaDmdO8AK2JAHTT4BMkQpWmacaP3r8AjdtlMRqWU-40D3TJGEZOL_cYTLhSn_QmGNahf0uVCifZ74erlVHyD53nGJdc9l/s1600/Screenshot+from+2016-01-24+03%253A44%253A13.png" /></a></div>
<br />
At this point, we know the location of the <i>audio_hw_device</i> and of the loaded libraries. All that's left is to create an arbitrary write primitive.<br />
<br />
As we've since before, three user-controlled values are inserted into a structure and passed as the second argument (R1) to <i>get_input_buffer_size</i>. We can now use this to our advantage; we'll pass in values corresponding to our wanted write address and value as the first two arguments to the function. These will get packed into the first two values in the <i>audio_config</i> structure.<br />
<br />
Now, we'll search for a unique gadget which unpacks these two values from R1, writes our crafted value into the wanted location and returns.<br />
<br />
While this seems like a lot to ask for, after searching through a multitude of gadgets, there appears to be a gadget (in <i>libcamera_client.so</i>) which does just this:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxqpEzyMXTINVlXVRo8nUHWTkkoWs3Sa9Ut6kdh9BbT6hYi2bmHfLEJTZj2NnBLg11uHrJyBp5MeWk9bsK_TELHJePFk1fdMiOVdAz49_CRaJYxLwNRNHb5PW225Qty4162Uc8uoU-JNn5/s1600/Screenshot+from+2016-01-24+03%253A56%253A24.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxqpEzyMXTINVlXVRo8nUHWTkkoWs3Sa9Ut6kdh9BbT6hYi2bmHfLEJTZj2NnBLg11uHrJyBp5MeWk9bsK_TELHJePFk1fdMiOVdAz49_CRaJYxLwNRNHb5PW225Qty4162Uc8uoU-JNn5/s1600/Screenshot+from+2016-01-24+03%253A56%253A24.png" /></a></div>
<br />
This means we can now increment the <i>get_input_buffer_size</i> function pointer to point to this gadget, and by passing the wanted values to the AudioFlinger::getInputBufferSize function, we can now write any 32-bit value to any absolute address within the mediaplayer process's virtual address space.<br />
<br />
<h2>
We have lift-off</h2>
<br />
Now that we have all the primitives we need, we just need to put the pieces together. We'll create an exploit which calls the "system" function within the "mediaserver" process.<br />
<br />
Once we have the addresses of the <i>audio_hw_device</i> and the library addresses, along with an arbitrary write primitive, we can prepare the arguments to our "system" function call anywhere within mediaserver's virtual address space.<br />
<br />
A good scratch pad candidate would be the "reserved" block within the <i>audio_hw_device</i>, since we already know its absolute memory location (because we leaked the address of the <i>audio_hw_device</i>), and we also know that overwriting that area won't have any negative side-effects. Using our write primitive, we can now write the path we would like to call "system" on to the "reserved" block, along with the address of the "system" function itself (which we can calculate since we leaked the library load address).<br />
<br />
Now, we can use our write primitive to change the <i>get_input_buffer_size</i> function pointer one final time - this time we would like to point it at a gadget which would unpack the function address and argument we have written into the reserved block, and would execute the function using this argument. This gadget was found in <i>libstagefright.so</i>:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkCyd40flT_Jg3pkZwKrenOPuZMx31kTa85NK9bQgN_q-vH6rCBCqSfprUraIARBulpXamRPLMoZK6ZyaBF1aOmthIAZa0oZmKt4MuK9Xr4-WgUd7gCpK_rxWA5xQleEOAaLY1ZFaOJ8uG/s1600/Screenshot+from+2016-01-24+14%253A50%253A46.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkCyd40flT_Jg3pkZwKrenOPuZMx31kTa85NK9bQgN_q-vH6rCBCqSfprUraIARBulpXamRPLMoZK6ZyaBF1aOmthIAZa0oZmKt4MuK9Xr4-WgUd7gCpK_rxWA5xQleEOAaLY1ZFaOJ8uG/s1600/Screenshot+from+2016-01-24+14%253A50%253A46.png" /></a></div>
<br />
So... This is it; we now have code execution within the "mediaserver" process. Here's a small diagram recapping our total exploit flow:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1kYKLtjD0353PWkgUlF-iSbG06XH64TKRaa72Wae8y6JLGnquRraSK-xX2nzewfvvU9rTgfC6XQ1L3rYexYOfInp3xIImc0O1pusKkLwVkt6FaXZVQErkhIwFbU2UVOPCopFhtUqx0Ury/s1600/Screenshot+from+2016-01-24+14%253A58%253A07.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1kYKLtjD0353PWkgUlF-iSbG06XH64TKRaa72Wae8y6JLGnquRraSK-xX2nzewfvvU9rTgfC6XQ1L3rYexYOfInp3xIImc0O1pusKkLwVkt6FaXZVQErkhIwFbU2UVOPCopFhtUqx0Ury/s1600/Screenshot+from+2016-01-24+14%253A58%253A07.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhc3M7FEapOTWYSsdP9DekhzChfsj2aHKmLaRxD4JQdtVpSEEHYs9zk2fAr0aT4XRGYcsGeQpBrNpjfrfsmzdTSTjyQIKz3cg1WwbFIiGQQiVJyadoHosF6qZAhKmiedEg64mKYFg-VtaU2/s1600/Screenshot+from+2016-01-24+14%253A15%253A30.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<br />
<h2>
Full Exploit Code</h2>
As always; I'd like to provide the full exploit code we have covered in this blog post. You can get it here:<br />
<br />
<a href="https://github.com/laginimaineb/cve-2014-7920-7921">https://github.com/laginimaineb/cve-2014-7920-7921</a><br />
<br />
The gadgets were collected for Android version 4.3, but can obviously be adjusted to whichever Android version you would like to run the exploit against (up to Android 5.1).<br />
<br />
I highly recommend that you download and look at the exploit's source code - there are many nuances I did not cover in the blog post (for brevity's sake) and the each stage of the exploit is heavily documented. <br />
<br />
Have fun!<br />
<br />
<h2>
Timeline</h2>
<ul>
<li>14.10.14 - Vulnerabilities disclosed to Google</li>
<li>21.10.14 - Notified the Android security team that I've written a full exploit</li>
<li>13.12.14 - Sent query to Google regarding the current fix status</li>
<li>03.01.15 - Got response stating that the patches will be rolled out in the upcoming version</li>
<li>03.02.15 - Sent another query to Google</li>
<li>18.02.15 - Got response stating the fix status has not changed</li>
<li>08.03.15 - Sent third query to Google</li>
<li>19.03.15 - Got response saying patches have been pushed into Android 5.1</li>
</ul>
<h3>
<i><span style="background-color: #cccccc;"></span></i></h3>
laginimainebhttp://www.blogger.com/profile/00867710965331025912noreply@blogger.com513tag:blogger.com,1999:blog-2029700426505953971.post-70503622478117108212015-08-26T02:06:00.000+03:002015-08-26T02:06:01.796+03:00Android linux kernel privilege escalation (CVE-2014-4323)In this blog post, we'll cover another Android linux kernel privilege escalation vulnerability I discovered, which could be used to achieve kernel code execution on Android devices.<br />
<br />
This time we'll only go over the vulnerability, with no exploit, since I don't personally have any device which is vulnerable to this issue, and therefore couldn't write an exploit. However, we'll dream up an exploit together, which should be pretty simple to implement.<br />
<br />
Before we start, I'd like to point out that this vulnerability has been <a href="https://www.codeaurora.org/projects/security-advisories/improper-input-validation-mdp-driver-when-processing-color-maps">responsibly disclosed to Qualcomm</a>, and it has since been fixed (see "Timeline" below). It should be noted that this vulnerability was present in all Qualcomm-based devices based on the following chipsets:<br />
<ul>
<li>APQ 8064 (Snapdragon S4 Pro)</li>
<li>MSM 8960 (Snapdragon S4)</li>
<li>MSM 8660 (Snapdragon S3)</li>
<li>MSM 8x30</li>
<li>MSM 7x30</li>
</ul>
So all devices based on these SoCs (such as the Nexus 4, Nexus 7, etc.), with kernels dated before December 2014, should be vulnerable (see "Timeline" below).<br />
<br />
<a name='more'></a><h2>
Let's get to it</h2>
<h2>
</h2>
<br />
Today we'll take a look at the "mdp" display driver. There are slight variations of this driver, depending on the SoC. However, both the MDP22 and MDP303 versions (which correspond to the SoCs listed above) are vulnerable.<br />
<br />
Normally, users may access the display driver in order to modify the display's properties, and perhaps even in order to retrieve the current frame-buffer (that is, take a screenshot).<br />
<br />
Since these operations are somewhat sensitive, they are usually restricted so that only processes with the "graphics" group-ID may perform them. This is facilitated by setting the permissions on the device files appropriately:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPSl81WY1ZOIeupcmhbniXZaCo2b_mU_cOKgryQwnr613v8ux1qTxTb-279R8_ZhmFn2ireuTR-i-Z0rimgVeXqRBdoPuOmIIgMjHs_BwELUgqZnNCYiPz4gyYO9_WX_3GOk-obWdac_6_/s1600/Screenshot+from+2015-08-25+23%253A29%253A50.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPSl81WY1ZOIeupcmhbniXZaCo2b_mU_cOKgryQwnr613v8ux1qTxTb-279R8_ZhmFn2ireuTR-i-Z0rimgVeXqRBdoPuOmIIgMjHs_BwELUgqZnNCYiPz4gyYO9_WX_3GOk-obWdac_6_/s1600/Screenshot+from+2015-08-25+23%253A29%253A50.png" /></a></div>
<br />
Naturally, the process in charge of compositing surfaces on Android (surfaceflinger) is a member of this group. However! The shell user is also a member of the graphics group - meaning, it can interact freely with the "mdp" driver (and therefore the vulnerability is also locally exploitable).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHv0FDSckIhh5EWwjif9jWaDx7pu7zjosUt1x7LzLyKQo57eyZXIMM-1D7fxr1wkBlAd-THwJaqHG27zdnOTbGkVXL7nO_VrRmg4ao5BpJ8obfw_eMyPMfu_GhbPMWw_u88iohy7zy4Zf0/s1600/Screenshot+from+2015-08-25+23%253A31%253A57.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHv0FDSckIhh5EWwjif9jWaDx7pu7zjosUt1x7LzLyKQo57eyZXIMM-1D7fxr1wkBlAd-THwJaqHG27zdnOTbGkVXL7nO_VrRmg4ao5BpJ8obfw_eMyPMfu_GhbPMWw_u88iohy7zy4Zf0/s1600/Screenshot+from+2015-08-25+23%253A31%253A57.png" /></a></div>
<div style="text-align: center;">
<span style="font-size: small;"><span style="font-size: x-small;">shell's user-ID and group-IDs</span></span></div>
<br />
<h2>
<b>Diving into the code</b></h2>
<br />
The "mdp" driver is extremely complex, supporting a wide range of commands; from IOCTLs, to memory mapping the device, etc. <br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjerGB4mBeR7k_qCLBapirQX_jpm-kqv6IpTBKOZOsEIINoinR3YxNlNSb87hjtREfFwT__zAtnRwRZzt8e5HjmRNe8rmkAMCm25lkO0RpwsEKQ2tCd7k6ltiA4rhiW1-Um0ZNpNlzzfubn/s1600/Screenshot+from+2015-08-25+23%253A40%253A04.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjerGB4mBeR7k_qCLBapirQX_jpm-kqv6IpTBKOZOsEIINoinR3YxNlNSb87hjtREfFwT__zAtnRwRZzt8e5HjmRNe8rmkAMCm25lkO0RpwsEKQ2tCd7k6ltiA4rhiW1-Um0ZNpNlzzfubn/s1600/Screenshot+from+2015-08-25+23%253A40%253A04.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2DV9i0N7Yc5ihQczbc32BlRUW5tj66zcHa8xU6WKoPcy986_UZNYmYbr1jnDmM1zPSTW21gtsKo5dcmsDCN-9RgVJLAtO-t2-HIg18IVXxT1YWweXPP3xuXID2QedTcsttoX27wHRCpAo/s1600/Screenshot+from+2015-08-25+23%253A38%253A04.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
This means we need a good strategy for mapping out the weak spots within the driver. Skimming over the code, going by the sheer amount of IOCTL commands supported (at least twenty different commands), it seems as though looking at the IOCTL commands in depth might be a lucrative venture.<br />
<br />
Funnily, though, there was no need to go too deeply, since the second IOCTL command turned out to be vulnerable :)<br />
<br />
<h2>
<b>MSMFB_SET_LUT</b></h2>
<b> </b><br />
The "mdp" driver allows a user to change the colour map lookup table used by the display, by means of a special IOCTL called "MSMFB_SET_LUT". The actual implementation of this IOCTL is deferred to a simple call to an internal function pointer, which is initialized to point to the actual implementation based on the MDP platform which is compiled into the kernel.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgESFNBRCOIFwQ725L-fw7uPA2U4K9sCi05pSbJECYWU6V0uurY5EONbsGXKyTlkPclKO32q8ShnR_9s_S-Zf88LwjXHB9CKfyOGue3_dtjCjrVHA7O42XoV7WhhAV5el-y9PPqgtTlNcST/s1600/Screenshot+from+2015-08-26+00%253A04%253A59.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgESFNBRCOIFwQ725L-fw7uPA2U4K9sCi05pSbJECYWU6V0uurY5EONbsGXKyTlkPclKO32q8ShnR_9s_S-Zf88LwjXHB9CKfyOGue3_dtjCjrVHA7O42XoV7WhhAV5el-y9PPqgtTlNcST/s1600/Screenshot+from+2015-08-26+00%253A04%253A59.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhB7mBFZjSdD3Se5ylJFoAFxA5IKxPNv4ydv1YvYJut-jg-KqKu1jO-AGM7w838reuBAkroN1bxOROjP6LlaYgnLGK7VKrjZzQjagrEkIBUo6VC3YkHgCKGepVcoATXsoCaU6EYm_8dYG18/s1600/Screenshot+from+2015-08-26+00%253A04%253A37.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
The above "lut_update" function pointer is initialized to point to the "mdp_lut_update_lcdc" on the MDP22 system, and to "mdp_lut_update_nonlcdc" on the MDP303. Keep in mind that both of these functions receive the "fb_cmap" structure which is copied from the user directly, without any validations (as evidenced above).<br />
<br />
Both of these functions call the
"mdp_lut_hw_update" function directly in order to update the
lookup-table, without performing any validations of their own on the
user-controlled "fb_cmap" structure.<br />
<br />
Let's take a good look at the "fb_cmap" structure: <br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilspqr1HaoQC_VUppwY2nBUfU2WbjcFc6q7R1rus0HlKohcRX6H3g9YLzaN0vUzJ5ZZR4TigPh9O8x21L_6waDHNRMm3h005qIPmdSvQbgfeoXvYOmdpYfo04r2JP-tud2YrE2SkwtumzQ/s1600/Screenshot+from+2015-08-26+00%253A11%253A18.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilspqr1HaoQC_VUppwY2nBUfU2WbjcFc6q7R1rus0HlKohcRX6H3g9YLzaN0vUzJ5ZZR4TigPh9O8x21L_6waDHNRMm3h005qIPmdSvQbgfeoXvYOmdpYfo04r2JP-tud2YrE2SkwtumzQ/s1600/Screenshot+from+2015-08-26+00%253A11%253A18.png" /></a></div>
<br />
Alarm bells should be ringing right about now:<br />
<ul>
<li>This structure contains a large (32-bit) length field </li>
<li>There's a "start" field which is not only large (32-bit), but whose name indicates that it might actually be treated as a pointer, even though its type is an unsigned integer</li>
<li>All the pointers in the structures aren't marked as "tainted" (using "__user")!</li>
</ul>
<br />
<br />
Finally - let's keep our fingers crossed and take a look at the "mdp_lut_hw_update" function:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAS9C1pgsofrrmF_kZoL605nD62xCjRXKpqGyxKVwLgQvtHy5d74nOBzH9IOE5CVVUvb_8Z0gDk6-PcrYvtHSeIRIq7EC8tutV0hXrfZQBCfUQEsN5Q3tI5XPchNZMqQ5cNIWwDT9SkVNj/s1600/Screenshot+from+2015-08-26+00%253A17%253A44.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAS9C1pgsofrrmF_kZoL605nD62xCjRXKpqGyxKVwLgQvtHy5d74nOBzH9IOE5CVVUvb_8Z0gDk6-PcrYvtHSeIRIq7EC8tutV0hXrfZQBCfUQEsN5Q3tI5XPchNZMqQ5cNIWwDT9SkVNj/s1600/Screenshot+from+2015-08-26+00%253A17%253A44.png" /></a></div>
<br />
First, the function iterates "len" times. Then, for each iteration, the function reads the red, green and blue values from the "fb_cmap" (safely, using "copy_from_user"). But here comes the scary part:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxVEiF8Vl3IU42023kXPdoEC0jbPyNn_jEVgh4n5ul1Wr8uU6hGQBV8SXXBWcYZBJtnXz-FuX_E-oP1dOS4IHOv7ZnG7FW3dL7g5rOrCwWf9VrUtso_-vRLR25xY8ejHSMoOd1J7VbVdXO/s1600/Screenshot+from+2015-08-26+00%253A20%253A39.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxVEiF8Vl3IU42023kXPdoEC0jbPyNn_jEVgh4n5ul1Wr8uU6hGQBV8SXXBWcYZBJtnXz-FuX_E-oP1dOS4IHOv7ZnG7FW3dL7g5rOrCwWf9VrUtso_-vRLR25xY8ejHSMoOd1J7VbVdXO/s1600/Screenshot+from+2015-08-26+00%253A20%253A39.png" /></a></div>
On first glance - this might just be some innocuous piece of code. After all, who knows what MDP_OUTP means... But we've come this far, let's at least find out what it means:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfWPYED5iQbgxLZlMpbiaydA-uLgizqGG00tPIJvcH1UxeVPddaagH1IAynNL9RclexzjjQLsNZrvSjYt2XgQN8Tff7Jm6UhzAnpgiapnkhmR4fbOxXcTnHwe_mOO8BiKc1rztkIFFP3Ua/s1600/Screenshot+from+2015-08-26+00%253A24%253A42.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfWPYED5iQbgxLZlMpbiaydA-uLgizqGG00tPIJvcH1UxeVPddaagH1IAynNL9RclexzjjQLsNZrvSjYt2XgQN8Tff7Jm6UhzAnpgiapnkhmR4fbOxXcTnHwe_mOO8BiKc1rztkIFFP3Ua/s1600/Screenshot+from+2015-08-26+00%253A24%253A42.png" /></a></div>
<br />
Still non the wiser. What does "outpdw" do?<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaMC4tzvGvCy7OxIbdmPy4Fii7hu4Q1OB_v5J3qnL_OPZhXu6p0GddSvFHyX83nn_LUNjUkz0r8x6CNsG0DMksxfZpnowRPzmR99lsAV1ZWZGpKeWJZAlk5DpN5RWkbbvo8T-iYw52eCxe/s1600/Screenshot+from+2015-08-26+00%253A25%253A28.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaMC4tzvGvCy7OxIbdmPy4Fii7hu4Q1OB_v5J3qnL_OPZhXu6p0GddSvFHyX83nn_LUNjUkz0r8x6CNsG0DMksxfZpnowRPzmR99lsAV1ZWZGpKeWJZAlk5DpN5RWkbbvo8T-iYw52eCxe/s1600/Screenshot+from+2015-08-26+00%253A25%253A28.png" /></a></div>
<br />
Oh.<br />
<br />
For those who haven't come across it before, "writel" simply writes the value in "val" into the address at "port", using a memory write barrier beforehand. This is usually used in order to write to memory mapped registers, in order to make sure the write itself remains coherent.<br />
<br />
Regardless, this means that the function above writes the concatenated value of the red, green and blue parameters (which are fully user controlled), into an address which is built from fully known, constant values and a fully controlled 32-bit value which is not validated in any way, since:<br />
<ul>
<li><i>"MDP_BASE</i>" is a macro which is defined to a constant memory mapped address
(one for each SoC)</li>
<li>0x93800 is a constant number and therefore also known in advance</li>
<li>"<i>mdp_lut_i</i>" is actually a flag which is set alternately to either 0 or 1, on each call to MDSSFB_SET_LUT. This means that the value of 0x400*mdp_lut_i is either 0 or 0x400</li>
<li>Since we can set cmap->len to 1, the index "i" will
therefore be zero in the single iteration performed, meaning we can
ignore i*4 (since it will equal zero) </li>
<li><u>cmap->start is fully user-controlled and never validated</u></li>
</ul>
Here's what it looks like:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAyeVmxX4xQk9e0X60Y7IWWFVlPU-Zvgy4xD4n_Szc6OV_djJShefQtMClZ47mo61YwI8uvgwZOzodQ4vwSypanirgK2_AXg9UdYcwLfYiDtiCkK02zXKerWpg6ZOH_Nb5OEMN9N_nh6lk/s1600/lut_addr.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAyeVmxX4xQk9e0X60Y7IWWFVlPU-Zvgy4xD4n_Szc6OV_djJShefQtMClZ47mo61YwI8uvgwZOzodQ4vwSypanirgK2_AXg9UdYcwLfYiDtiCkK02zXKerWpg6ZOH_Nb5OEMN9N_nh6lk/s1600/lut_addr.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTrWhAcir7PDz6z1mLpsC_I6_w-IIpSbnYr6ratrQvKk4eRABe71hakT7sPaBn60ztQLUFYVl8R-UQZCSfI33Ac3aX4C9bPHnGHJ6VHrJ6zgUVk5dEC4vGP6ALz2pYv3_cRcuQTlbOehbC/s1600/lut_addr.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
Putting it together - this means that we can write any 24-bit value into any memory address - great! :)<br />
<br />
<h2>
<b>Dreaming up an exploit</b></h2>
<br />
First, as with all exploits, we'd like to neatly package the write-what-where primitive into a single function. Let's imagine we've done that, and that it's called write_value, and it accepts a 24-bit value and a 32-bit address to which this value should be written.<br />
<br />
In order to make exploitation fully reliable, we'd need to know the current value of "mdp_lut_i". This can be done by mapping a sterile buffer within user-space which is more than 0x400 bytes large. Then, we can simply trigger to overwrite vulnerability with a <i>cmap->start</i> value so that the destination address will either correspond to the beginning of this mapped buffer, or to its end:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaGp1R-N5mvGz3TKzpytRlGf6E0kFxQ8mcSqJquV_U7H_HP5-RnJOhikxop9CxsIHIvohVGChcvFD6Y-vF0osSAzzlj6iD5ibkL_0sgLqStKSKsuuwFpEvW6JbQtXIJtt5z8FnKyLKfgAA/s1600/overflow.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaGp1R-N5mvGz3TKzpytRlGf6E0kFxQ8mcSqJquV_U7H_HP5-RnJOhikxop9CxsIHIvohVGChcvFD6Y-vF0osSAzzlj6iD5ibkL_0sgLqStKSKsuuwFpEvW6JbQtXIJtt5z8FnKyLKfgAA/s1600/overflow.png" /></a></div>
After triggering the overwrite, we can check the sterile buffer to see where the overwrite occurred - allowing us to deduce the value of "mdp_lut_i".<br />
<br />
Now that we know all the values from which the destination address is built, we can freely overwrite any address within the kernel's virtual address space. From here on, we can simply overwrite a kernel function pointer and redirect it to a function stub allocated within user-space.<br />
<br />
This is actually identical to the exploitation method covered in a <a href="http://bits-please.blogspot.co.il/2015/08/android-linux-kernel-privilege.html">previous blog post</a> - in which we overwrote a function pointer within "pppolac_proto_ops" and triggered it by closing a PPP_OLAC socket.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXNOlOLgY6wumH_VcOiiQ8D_zVz0Sty_9zsf4PTi4SKmZvrWPjLj2gnsF35uQ-M7OrcYGwQKq8_n9HfT-fUZVundMlN5SiUNwn2PctOreY1jme1dbzV5PJ8o-fZtFeFLdz2R3JLOkbB1dM/s1600/redirect_exec.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXNOlOLgY6wumH_VcOiiQ8D_zVz0Sty_9zsf4PTi4SKmZvrWPjLj2gnsF35uQ-M7OrcYGwQKq8_n9HfT-fUZVundMlN5SiUNwn2PctOreY1jme1dbzV5PJ8o-fZtFeFLdz2R3JLOkbB1dM/s1600/redirect_exec.png" /></a></div>
<br />
And there you have it. A fully imaginary exploit, just waiting to be written :) If you do happen to write this exploit, please let me know!<br />
<br />
<h2>
Timeline</h2>
<ul>
<li>27.09.14 - Vulnerability disclosed</li>
<li>29.09.14 - Initial response from QC</li>
<li>02.10.14 - Issue confirmed by QC</li>
<li>13.11.14 - QC publishes notification to customers</li>
<li>27.11.14 - QC publishes notification to carriers</li>
<li>11.12.14 - Issue closed, <a href="https://www.codeaurora.org/projects/security-advisories/improper-input-validation-mdp-driver-when-processing-color-maps">CAF advisory issued</a></li>
</ul>
laginimainebhttp://www.blogger.com/profile/00867710965331025912noreply@blogger.com174tag:blogger.com,1999:blog-2029700426505953971.post-14846894697095384142015-08-25T17:59:00.001+03:002015-08-25T23:10:48.006+03:00Effectively bypassing kptr_restrict on AndroidIn this blog post, we'll take a look at a few ways that I've discovered in order to bypass <i>kptr_restrict</i> on Android, allowing for easier exploitation of vulnerabilities that require some information on the virtual addresses in which the kernel is loaded. But first, for those of you who aren't familiar with the "protection" offered by <i>kptr_restrict</i>, let's get you up to speed on the subject.<br />
<br />
<a name='more'></a><br />
<b>What's kptr_restrict?</b><br />
<br />
As we've seen in the <a href="http://bits-please.blogspot.co.il/2015/08/android-linux-kernel-privilege.html">previous blog post</a>, sometimes exploits require knowledge of internal kernel pointers - either in order to hijack them, or in order to corrupt them in a controllable manner.<br />
<br />
This fact has been known for quite some time - enough time, in fact, for it to be addressed directly. The Linux kernel contains a feature which enables it to filter out such addresses in order to avoid leaking them to a potential attacker. This configurable feature is called "kptr_restrict", and has been present in the Android kernel source tree for at least two years.<br />
<br />
As with nearly all configurable kernel parameters, there exists a special file which allows to set the way in which this feature behaves when attempting to filter kernel addresses. In the case of kptr_restrict, the file resides in "/proc/sys/kernel/kptr_restrict", but has some daunting permissions set:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhL45nQOj0BHPKDxowp09tD8KqjacXUluyUDCi6-YtatKtW7vFRs3eUDaihFMhuWtS6s2RR0FBOL_acP8r93goeXVE6ydWBjAb38hTWLMQL3Z0C44cUp7hyoGfkzDgzodGXsAtGhUA71GCU/s1600/Screenshot+from+2015-08-21+19%253A06%253A49.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhL45nQOj0BHPKDxowp09tD8KqjacXUluyUDCi6-YtatKtW7vFRs3eUDaihFMhuWtS6s2RR0FBOL_acP8r93goeXVE6ydWBjAb38hTWLMQL3Z0C44cUp7hyoGfkzDgzodGXsAtGhUA71GCU/s1600/Screenshot+from+2015-08-21+19%253A06%253A49.png" /></a></div>
<br />
Essentially, only root can modify its value, but any user can read it.<br />
<br />
So how does <i>kptr_restrict</i> work? Well, first of all, kernel developers needed a way to mark kernel pointers as such, whenever those are outputted. This is achieved by using a new format specifier, "%pK", which is used to denote that the value written into that specifier contains a kernel pointer, and as such, should be protected.<br />
<br />
There are three different values which control the protection offered by <i>kptr_restrict</i>:<br />
<ul>
<li>0 - The feature is completely disabled</li>
<li>1 - Kernel pointers which are printed using "%pK" are hidden (replaced with zeroes), unless the user has the CAP_SYSLOG capability, and has not changed their UID/GID (to prevent leaking pointers from files opened before dropping permissions).</li>
<li>2 - All kernel pointers printed using "%pK" are hidden </li>
</ul>
The default value of this configuration is chosen when building the kernel (via <a href="https://lwn.net/Articles/419676/">CONFIG_SECURITY_KPTR_RESTRICT</a>), but for all modern Android devices that I've ever encountered, this value is always set to "2".<br />
<br />
However - how many kernel developers actually know of the need to protect kernel pointers by using "%pK"? The can be easily answered by grepping the kernel for this format string. The answer is, as expected, quite sad:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSu1w52tU3YzCDkQDHXt9N0U4eoTGuLF83zrm-VIs0Y0KDFYkc0DzzvQTJY3piWOtmJWG4Pj9eUo-g62FqDYWlBDyoTzvLLahnTJCA-mCLAGQIPSLzbVbKVxgW39YKVFS44jvoEKHtNOW8/s1600/Screenshot+from+2015-08-21+19%253A29%253A34.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSu1w52tU3YzCDkQDHXt9N0U4eoTGuLF83zrm-VIs0Y0KDFYkc0DzzvQTJY3piWOtmJWG4Pj9eUo-g62FqDYWlBDyoTzvLLahnTJCA-mCLAGQIPSLzbVbKVxgW39YKVFS44jvoEKHtNOW8/s1600/Screenshot+from+2015-08-21+19%253A29%253A34.png" /></a></div>
<br />
Merely 35 times (in 23 files) within the entire kernel source code. Needless to say, kernel pointers are <u>very often</u> printed using the "normal" pointer format specifier, "%p" - a simple search shows many hundreds of such uses.<br />
<br />
So now that we've set the stage, let's see why the protection offered by kptr_restrict is insufficient on it's own.<br />
<br />
<b>Method #1 - Getting <i>dmesg</i> from shell</b><br />
<br />
All log messages printed by the kernel are written to a circular buffer held within the kernel's memory. Users may read from this buffer by invoking the "<b>dmesg</b>" (<b>d</b>isplay <b>mes</b>sa<b>g</b>e) command. This command actually accesses the buffer by invoking the <a href="http://man7.org/linux/man-pages/man2/syslog.2.html">syslog</a> system call, as you can see from this strace output:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjiWSV_7J5P32N002jUSPEHb95mT3e7Jh4v3rqulcS78L2l1hAs2o5B-ry5LhNsP8zFliZe-X1XTv5oX8TBX2hdXZZ54fUgewNp1nXaYZZDIbXpZoBAb4FvjJwDFGdAGLtxVugvXHmFtEtz/s1600/Screenshot+from+2015-08-21+19%253A36%253A54.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjiWSV_7J5P32N002jUSPEHb95mT3e7Jh4v3rqulcS78L2l1hAs2o5B-ry5LhNsP8zFliZe-X1XTv5oX8TBX2hdXZZ54fUgewNp1nXaYZZDIbXpZoBAb4FvjJwDFGdAGLtxVugvXHmFtEtz/s1600/Screenshot+from+2015-08-21+19%253A36%253A54.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8D9Q4X_DD5nFb6DsWo7uCbpRVJf6ziUPP_NTK_TsFH0M7QPhJnZHqelz1f09ApzqKTD4Cis8xZMueU-2qVNprrtS9Tp85JhCTC6RqOoSZ_NTRU4taGVWfGyDiXjKcYduzzve9P88FxB1B/s1600/Screenshot+from+2015-08-21+19%253A36%253A54.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
However, the syslog system call can't be accessed by just any user - specifically, the caller must either posses the extremely powerful CAP_SYS_ADMIN capability, or the weaker (and more specific) capability of CAP_SYSLOG.<br />
<br />
Either way, most Android processes do not, in fact, have these capabilities, and therefore can't access the kernel log. Or can they? :)<br />
<br />
Recall that within Android, the "init" process maintains a list of "services" which can be started or stopped as needed. These services are loaded by "init" upon boot, from a hard-coded list of configuration files, which are almost always stored on the root (read-only) partition, and are therefore read-only.<br />
<br />
The configuration files are actually written using a language specific to Android, called the "<a href="https://github.com/android/platform_system_core/blob/master/init/readme.txt">Android Init Language</a>". This language is pretty simple and easy to use, and allows full control over the permissions with which services are launched (UID/GID) as well as their parameters and "type" (for more information about the language itself, check out the link above).<br />
<br />
Another feature of Android are "system properties" - these are key-value pairs which are maintained by the "property service", which is also a thread within the <i>init</i> process. This service allows basic access-control on various "sensitive" system properties, which prevents users from freely modifying any property they please.<br />
<br />
These access-permissions for most properties used to be (until Android 4.4) <a href="http://androidxref.com/4.4.4_r1/xref/system/core/init/property_service.c#64">hard-coded</a> within the property service (since Android 5, the permissions are handled by using SELinux labels instead):<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFOaVkxWyTpoJEgHohsDLMFtpGZHhUuhF0UK15c-tsROijG7DD8sEnGCMzLbzda6s-_9WVCd6rPWpgMGr91HKk_wz5NUkGjn5WAsqXch_BlHIZyBxTcu4PYPATV1inIGIABzLFOCxufFAE/s1600/Screenshot+from+2015-08-21+20%253A29%253A20.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFOaVkxWyTpoJEgHohsDLMFtpGZHhUuhF0UK15c-tsROijG7DD8sEnGCMzLbzda6s-_9WVCd6rPWpgMGr91HKk_wz5NUkGjn5WAsqXch_BlHIZyBxTcu4PYPATV1inIGIABzLFOCxufFAE/s1600/Screenshot+from+2015-08-21+20%253A29%253A20.png" /></a></div>
<br />
However, some properties get special treatment, namely - the "<i>ctl.start</i>" and "<i>ctl.stop</i>" system properties, which are used to either start or stop system services (defined, as mentioned before, using the "Android Init Language").<br />
<br />
These properties are checked strictly using SELinux labels, in order to make sure that the privilege of modifying the status of system services is reserved strictly to certain users.<br />
<br />
But here comes the surprising part - when connecting locally to the device using "<b>adb</b>" (<b>A</b>ndroid <b>D</b>ebug<b> B</b>ridge), we gain execution as the "shell" user. This user is <u>always</u> permitted start and stop one particular service - "dumpstate". Actually, this is used by a feature offered by the "adb" command-line utility, which enables developers to create bug reports containing full information from the device.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimDG8oLTL7cBsfLOJHBwxigKlvhFcL65wlSNLyJOR4atV1TEAL81qqzZwu47jHhY2rqVz8fHGlJem7bcNVXr3gFPHWcKlRmTzhNz4GIup8xfex4BmmvO4289YwKDecUCGH6giYsSK2Qy5d/s1600/Screenshot+from+2015-08-21+20%253A38%253A26.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimDG8oLTL7cBsfLOJHBwxigKlvhFcL65wlSNLyJOR4atV1TEAL81qqzZwu47jHhY2rqVz8fHGlJem7bcNVXr3gFPHWcKlRmTzhNz4GIup8xfex4BmmvO4289YwKDecUCGH6giYsSK2Qy5d/s1600/Screenshot+from+2015-08-21+20%253A38%253A26.png" /></a></div>
<br />
Running "<i>adb</i>" with this command-line argument (or simply executing "bugreport" from the adb shell), actually starts the "dumpstate" service by setting the "ctl.start" system property:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTgCf9njmmPv1tCumNPuluqotk-lSRINj56pFFWijF6F2aie_3WyTp37x-SjnMHtTN1OanpVIAKyFY15A4hlEBBvC6ONT_quVnr7pID3PvppyqYsTi3-P3tHjkECeSXjEG-AHpuupDfcVJ/s1600/Screenshot+from+2015-08-21+20%253A42%253A26.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTgCf9njmmPv1tCumNPuluqotk-lSRINj56pFFWijF6F2aie_3WyTp37x-SjnMHtTN1OanpVIAKyFY15A4hlEBBvC6ONT_quVnr7pID3PvppyqYsTi3-P3tHjkECeSXjEG-AHpuupDfcVJ/s1600/Screenshot+from+2015-08-21+20%253A42%253A26.png" /></a></div>
So let's take a look at the configuration for the "dumpstate" service:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhq4w9nC_1sb-LR0DuCEhbthROYLLICTZWMeBxvrnNnt21aa7AGo0wevFpex7e4j_NQzelYhMFmNfLcHeJqZbHFlD81uvOWv03vgT4On0rVuMYiJzTagrk3CN5ZGjxG8Ye_vjFKNdFSl72a/s1600/Screenshot+from+2015-08-21+20%253A44%253A38.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhq4w9nC_1sb-LR0DuCEhbthROYLLICTZWMeBxvrnNnt21aa7AGo0wevFpex7e4j_NQzelYhMFmNfLcHeJqZbHFlD81uvOWv03vgT4On0rVuMYiJzTagrk3CN5ZGjxG8Ye_vjFKNdFSl72a/s1600/Screenshot+from+2015-08-21+20%253A44%253A38.png" /></a></div>
Since the service has no "user" or "group" configurations, it is actually executed with the root user-ID and group-ID, which could be quite dangerous...<br />
<br />
Luckily, the developers of the service were well aware of the potential security risks of running with such high capabilities, and therefore immediately after starting, the service drops its capabilities by modifying its user-ID, group-ID and capabilities, like so:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_AwpP6tCTbmHC1gFhZNqiS6ZmjWcroxxcPsZQPhE4OoAVfOpgQnurBi9jj3bX8jmTo8UqBDp3Ze1GjAHzOBtDb4mMRuYCxBpxDSEe5lpIhJWYi2BUJOUleYaFwFlRcEp07pwEJDhQg-z2/s1600/Screenshot+from+2015-08-21+20%253A48%253A14.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_AwpP6tCTbmHC1gFhZNqiS6ZmjWcroxxcPsZQPhE4OoAVfOpgQnurBi9jj3bX8jmTo8UqBDp3Ze1GjAHzOBtDb4mMRuYCxBpxDSEe5lpIhJWYi2BUJOUleYaFwFlRcEp07pwEJDhQg-z2/s1600/Screenshot+from+2015-08-21+20%253A48%253A14.png" /></a></div>
<br />
In short, the service sets the user and group IDs to those of the shell user, but makes sure that it keeps the CAP_SYSLOG capability explicitly.<br />
<br />
Reading on reveals that "<i>dumpstate</i>" actually reads the kernel log using the syslog system call (which it is capable of executing since it has the CAP_SYSLOG capability), and writes the contents read back to the caller. Essentially, this means that within the context of the "adb shell", we can freely read the kernel log simply by executing the "bugreport" program. Nice.<br />
<br />
However, this still doesn't solve the problem of getting needed symbols for exploits - since, as mentioned earlier, these symbols should generally be printed using the "%pK" format specifier, which means they would appear "censored" in the kernel log.<br />
<br />
But alas, most pointers within the kernel are certainly not printed using the special format specifier, but instead use the regular "%p" format, and are therefore left uncensored. This means that the kernel log is typically a treasure trove of useful kernel pointers.<br />
<br />
For example, when the kernel boots, the memory map of the kernel's different segments is printed, like so:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZC5WFk_hrlx8KDAEoTRdd97eAXeQZxaOhGxT7df8k8eeR2dpffAgxxLe18I-gOrVKEO2sFNsyHukZldwSAxR-pW2cQ21-U25WjuBDDVGhs0NVlWTfCUH7oP3WiBmtlkmtZj-yvDIDPWLV/s1600/Screenshot+from+2015-08-22+23%253A28%253A41.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZC5WFk_hrlx8KDAEoTRdd97eAXeQZxaOhGxT7df8k8eeR2dpffAgxxLe18I-gOrVKEO2sFNsyHukZldwSAxR-pW2cQ21-U25WjuBDDVGhs0NVlWTfCUH7oP3WiBmtlkmtZj-yvDIDPWLV/s1600/Screenshot+from+2015-08-22+23%253A28%253A41.png" /></a></div>
<br />
Now, assuming there's a single symbol we would like to find, we could simply dump the list of all kernel symbols using the virtual file containing all the symbols - /proc/kallsyms. When <i>kptr_restrict </i>is enabled, the list returned by <i>kallsyms</i> is censored (since it is printed using "%pK"), and therefore won't show any kernel pointers.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuyuiHbJELa_1U5Ubfj9HVOizglCE7Kfx9C0bODijeRtL0edTJ70hHuLNdGSGF5uTSpVdtuCyV4Vfc8miuePHY7rPkATTswIrAIeT0QShDi48x0BdJbP6gInPcIMrhJkZtjd6-3VFzDH6U/s1600/Screenshot+from+2015-08-22+23%253A58%253A02.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuyuiHbJELa_1U5Ubfj9HVOizglCE7Kfx9C0bODijeRtL0edTJ70hHuLNdGSGF5uTSpVdtuCyV4Vfc8miuePHY7rPkATTswIrAIeT0QShDi48x0BdJbP6gInPcIMrhJkZtjd6-3VFzDH6U/s1600/Screenshot+from+2015-08-22+23%253A58%253A02.png" /></a></div>
<div style="text-align: center;">
<span style="font-weight: normal;"><span style="font-size: xx-small;">Censored symbols from kallsyms</span> </span></div>
<br />
However, the symbols returned by <i>kallsyms</i> are ordered by their addresses, even if those addresses aren't shown. Moreover, this task is made easier due to the fact that each segment is prefixed and postfixed by specially named marker symbols:<br />
<br />
<table align="center">
<tbody>
<tr>
<th style="text-align: center;">Segment Name</th>
<th style="text-align: center;"> Start Marker </th>
<th style="text-align: center;"> End Marker </th>
</tr>
<tr class="alt">
<td style="text-align: center;">.text</td>
<td style="text-align: center;">_text</td>
<td style="text-align: center;">_etext</td>
</tr>
<tr>
<td style="text-align: center;">.init</td>
<td style="text-align: center;">__init_begin</td>
<td style="text-align: center;">__init_end</td>
</tr>
<tr class="alt">
<td style="text-align: center;">.data</td>
<td style="text-align: center;">_sdata</td>
<td style="text-align: center;">_edata</td>
</tr>
<tr class="alt">
<td style="text-align: center;">.bss</td>
<td style="text-align: center;">___bss_start</td>
<td style="text-align: center;">__bss_end</td>
</tr>
</tbody></table>
<ul></ul>
We can then use this list to deduce the location of different symbols by simply counting the number of symbols from the start or end marker to our wanted symbol, while adding up the sizes of each of the symbols encountered.<br />
<br />
Another technique would be to cause a wanted kernel pointer to be written to the kernel log. For example, on Qualcomm-based devices (based on the "msm" kernel), whenever the video device is opened, the kernel virtual address of the video device is written to the kernel log:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwvflgwbfhG0Mr7JvX4pyQmf7nhsM2x9YG05tLjcq7dJcGm6NGSZzYThrD_lrCjZYNhHtVPVllEhgQYkbDSuQRTFKLBI4RvXI3fcJGA8-9OjT3nIQ6xa2Qv7kVG4Luf8ryLUSzdYKdDEoM/s1600/Screenshot+from+2015-08-23+00%253A10%253A43.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwvflgwbfhG0Mr7JvX4pyQmf7nhsM2x9YG05tLjcq7dJcGm6NGSZzYThrD_lrCjZYNhHtVPVllEhgQYkbDSuQRTFKLBI4RvXI3fcJGA8-9OjT3nIQ6xa2Qv7kVG4Luf8ryLUSzdYKdDEoM/s1600/Screenshot+from+2015-08-23+00%253A10%253A43.png" /></a></div>
<div style="text-align: center;">
<span style="font-size: xx-small;">msm_vidc_open leaks the pointer to the kernel log</span></div>
<div style="text-align: left;">
<br />
<span style="font-size: normal;"><b>Method #2 - Retrieving the kernel symbols statically</b></span></div>
<div style="text-align: left;">
<br />
<u>Why use this method?</u></div>
<div style="text-align: left;">
In many cases, although the device itself is accessible, it may be heavily locked - for example, in extreme cases, <i>adb </i>access may be disabled (<a href="http://www.tomsguide.com/us/blackphone-hack-def-con-debunk,news-19314.html">however poorly</a>), which would complicate the usage of the first method (unless we manage to gain shell access). In this case, we may wish to build the complete list of kernel symbols from the kernel image itself, statically, without interacting directly with the device.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Also, since <b>KASLR</b> (<b>K</b>ernel <b>A</b>ddress <b>S</b>pace <b>L</b>ayout <b>R</b>andomization) is currently still unused in Android devices, there is no need to consider any kind of runtime modification to the location of the symbols present in the kernel image. This means that the kernel image must contain all the information needed to build the complete list of symbols, including their addresses, exactly as they would appear on a real "live" device.</div>
<div style="text-align: left;">
<br />
<u>How do I get a kernel image?</u><br />
<br />
Assuming you have the full access to a live device, you could read the kernel image directly from the MMC, via /dev/block. However, in most cases reading the MMC blocks directly requires root permissions, which would make this method pretty obsolete, since with root access we could already disable <i>kptr_restrict</i>.<br />
<br />
The more reasonable path to obtaining the kernel image would be to simply download the firmware file for your particular device, and unpack it. There are many tools which enable firmware unpacking for different devices (for example, I wrote a script to unpack to Nexus 5's bootloader - <a href="https://github.com/laginimaineb/unpack_bootloader_image">here</a>), but many such tools are available, and are typically a google-search away.<br />
<br />
Just one word of caution - make sure you download the exact kernel image matching the kernel on your device. You can find the running kernel's version by simply running "<i>uname -a</i>":<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqZIYfGIHkoA9iRZ76ik93hcBViorLNFuX0G2VPpakz40jC84apyVsKWEPoU2gZUVoUU-b5kY6Z0IrlCrNzyry3QE50n1jjRWSPVsvqjeHjIbCn1F3QFWy9ukkjdPL1D5VGVT2m0wm_Xcj/s1600/Screenshot+from+2015-08-25+16%253A31%253A33.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqZIYfGIHkoA9iRZ76ik93hcBViorLNFuX0G2VPpakz40jC84apyVsKWEPoU2gZUVoUU-b5kY6Z0IrlCrNzyry3QE50n1jjRWSPVsvqjeHjIbCn1F3QFWy9ukkjdPL1D5VGVT2m0wm_Xcj/s1600/Screenshot+from+2015-08-25+16%253A31%253A33.png" /></a></div>
<br />
<u>I have the image - now what?</u><br />
<br /></div>
<div style="text-align: left;">
In order to understand how to extract the full symbol list from a kernel image, we must first inspect the way in which a kernel image is built. Looking over the code, reveals that <a href="http://androidxref.com/kernel_3.4/xref/scripts/kallsyms.c">a special program</a> is used to emit the symbols needed in a special format into the kernel's image, as part of the build process. The program which receives the symbol map containing the location of each
kernel symbol in the kernel's virtual address space, and outputs an
assembly file containing the compressed symbol table, which is
assembled into the resulting kernel image.</div>
<br />
This means that all we need to do in order to rebuild this table from a raw kernel binary is to understand the exact format in which this symbol table is written. However, for a normally compiled kernel with no additional symbols, this turns out to be a little tricky.<br />
<br />
Since the labels written by the script are not visible in the
resulting kernel binary, the first thing we'd have to solve is how to
find the beginning of the symbol table within the binary. Luckily, the solution
turns out to be pretty simple - remember when we previously had a look at
the symbol table from <i>kallsyms</i>? The first two symbols were marker symbols pointing
to the beginning of the kernel's text segment. Since the kernel's code is loaded at a known address (typically, 0xC0008000), we can search for this value appearing at least twice consecutively within the binary, and attempt to parse the symbol table's structure starting at that address. <br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghIarul2fPi1cYVpjhhPRPHDBGURBNhzp9B6ILuPw6DsEaI9MhXAijY5Vvj-O-ROeCZ9Dbp4xsrLHxKd-ojszlzQ8Nj4CR9-BXoheVOIFcQHy8RQCUwmBer_CCF7AiPVms1p6Ic32G-aoB/s1600/Screenshot+from+2015-08-25+16%253A45%253A56.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghIarul2fPi1cYVpjhhPRPHDBGURBNhzp9B6ILuPw6DsEaI9MhXAijY5Vvj-O-ROeCZ9Dbp4xsrLHxKd-ojszlzQ8Nj4CR9-BXoheVOIFcQHy8RQCUwmBer_CCF7AiPVms1p6Ic32G-aoB/s1600/Screenshot+from+2015-08-25+16%253A45%253A56.png" /></a></div>
<br />
Going over the symbol table itself, reveals that it is terminated by a NULL address. Then, immediately following the symbol table, the actual number of symbols is written, which means we can easily verify that the table is actually well-formed.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzzC9OcshaIVZBaM6SwycAHYwGd6ld-1WSL8Bo1xfgbDgFldZdKVybu4I2ud4YCLdEWQwMS2PsPwt4cdl9TSgYLvMwEIagmWeQ7h1Xx2_XQxg5YVs4YvQVm4G7uYccTWDWfIlL4qrbJgb3/s1600/Screenshot+from+2015-08-25+16%253A48%253A37.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzzC9OcshaIVZBaM6SwycAHYwGd6ld-1WSL8Bo1xfgbDgFldZdKVybu4I2ud4YCLdEWQwMS2PsPwt4cdl9TSgYLvMwEIagmWeQ7h1Xx2_XQxg5YVs4YvQVm4G7uYccTWDWfIlL4qrbJgb3/s1600/Screenshot+from+2015-08-25+16%253A48%253A37.png" /></a></div>
<br />
Then, two tables of "markers" and "symbols" are written into the file. This is done in order to compress the size of the symbols within the table, and by doing so reduce the size of the kernel binary. The compression maps the 256 most used substrings (which are called tokens), into a single byte value. Then, each symbol's name is compressed into a pascal-style string of bytes (meaning, a byte marking the length of the string, then an actual string of characters). Each byte in the compressed name maps to a single tokens, which in turn corresponds to a single "most commonly used" substring. Putting it together, it looks like this:<br />
<br />
<div style="text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDd6-FgsRDgcCZD6vWYMeec5BIg2fPcodx3BmYuMdwyl_fjY0zesOHoPBFyLlIM5_KOzyOKpJkTWhMg0vtTj2XdTZu4HeU94o4_rBUdAN5AL79NNSfXbR-gYxHSgxns0bOxwvyvZqRO61e/s1600/symbol_table.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDd6-FgsRDgcCZD6vWYMeec5BIg2fPcodx3BmYuMdwyl_fjY0zesOHoPBFyLlIM5_KOzyOKpJkTWhMg0vtTj2XdTZu4HeU94o4_rBUdAN5AL79NNSfXbR-gYxHSgxns0bOxwvyvZqRO61e/s1600/symbol_table.png" /></a></div>
<br />
According to kernel developers, this<a href="http://androidxref.com/kernel_3.4/xref/scripts/kallsyms.c#17"> usually produces a compression ratio of about 50%</a>.<br />
<br />
<br />
I've written a python script which, given a raw kernel binary, extracts the full symbol table from the binary, in the exact same format as they are written within kallsyms. <a href="https://github.com/laginimaineb/static_kallsyms">You can find it here.</a> Please let me know if you find the script useful!<b> </b><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh03qx0YxKZFRqfHPCQLdmTfI4VftM0CrYBx8ga-H5PAo2kCXQVdEhmks03P9runEMVWd_UbU63R8uqgscyI4wC_Crxbbl4hlmZEqOlgpmq9L4kckDzFZuf8ldMekhAhAHygqXFUPFU3sDh/s1600/kallsyms_static.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh03qx0YxKZFRqfHPCQLdmTfI4VftM0CrYBx8ga-H5PAo2kCXQVdEhmks03P9runEMVWd_UbU63R8uqgscyI4wC_Crxbbl4hlmZEqOlgpmq9L4kckDzFZuf8ldMekhAhAHygqXFUPFU3sDh/s1600/kallsyms_static.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiy-CcpFNTxxbzZKFR2_g4fhqxvLlAAFjSO_Jx0vzZVFwDuxSQ800_N-v3Hz_kVO1rbIplyFVwDpnHcDiqEUHxtOLo2PzoJdTS467PCjUWpzUb7tWsO1kKWBuFW9qWRaulCBsJIj5zprCrz/s1600/Screenshot+from+2015-08-25+18%253A20%253A49.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<b>Method #3 - Finding information disclosures within the kernel</b><br />
<br />
This is the "classical" method which is commonly used in order to bypass the restrictions imposed by <i>kptr_restrict</i>. For a remote attacker wishing to target a wide variety of devices, it is quite often the best choice, since:<br />
<ul>
<li>The first method typically requires shell access to the device, in order to execute the "bugreport" service</li>
<li>The second method requires you to obtain the kernel image, which could be tiresome to do for a very wide variety of devices</li>
</ul>
Sadly, it appears that kernel developers are far less aware of the possible risks of leaking kernel pointers than they are of other (e.g., memory corruption) vulnerabilities.<br />
<br />
As a result, finding a kernel memory leak is usually a very short and simple task. To prove this point, after poking around for five minutes on a live device, I've come across such a leak, which is accessible from any context.<br />
<br />
Whenever a socket is opened within Android, it is tagged using a netfilter driver called "qtaguid". This driver accounts for all the data sent or received by every socket (and tag), and allows some restrictions to be placed on sockets, based on the tag assigned to them. Android uses this feature in order to account for data usage by the device. The actual per-process breakdown is done by assigning each process a specific tag, and monitoring the data used by the process based on that tag.<br />
<br />
The driver also exposes a control interface, with which a user can query the current sockets and their tags, along with the user-ID and process-ID from which the socket has been opened. This control interface is facilitated by a <u>world-accessible</u> file, under <i>/proc/net/xt_qtaguid/ctrl</i>.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMDUL7Fv-xOM0ODcsWx5ypis-ioy3-KXUS6WRCJSAzZKW8vInGL-AmrMUepFkRjsOETPfkDpmbDCuyIDDAv-_LoqVCFOAo1ssGKSJKpakKeWD0660pTOR_oqNGVNWvP2VXSEL4Mt51xIme/s1600/Screenshot+from+2015-08-25+17%253A36%253A02.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMDUL7Fv-xOM0ODcsWx5ypis-ioy3-KXUS6WRCJSAzZKW8vInGL-AmrMUepFkRjsOETPfkDpmbDCuyIDDAv-_LoqVCFOAo1ssGKSJKpakKeWD0660pTOR_oqNGVNWvP2VXSEL4Mt51xIme/s1600/Screenshot+from+2015-08-25+17%253A36%253A02.png" /></a></div>
<br />
However, reading this file reveals that it actually contains the kernel virtual address for each of the sockets which <u>completely uncensored</u>:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8h9N-lKNMwRPsbOYBxVpmwSACw_eAH9l9B3swyzDvqZVpYLwWTytkR2HXNBDWegbYizBmLTe4gxbflWFZd3eADkpCtMV2_ssI-64HX-wExBxEsDrfsjg1YvA2zR-9EUQ18vqqjCRWpyeR/s1600/Screenshot+from+2015-08-25+17%253A37%253A33.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8h9N-lKNMwRPsbOYBxVpmwSACw_eAH9l9B3swyzDvqZVpYLwWTytkR2HXNBDWegbYizBmLTe4gxbflWFZd3eADkpCtMV2_ssI-64HX-wExBxEsDrfsjg1YvA2zR-9EUQ18vqqjCRWpyeR/s1600/Screenshot+from+2015-08-25+17%253A37%253A33.png" /></a></div>
<br />
Looking at the source code for the virtual file's "read" implementation, reveals that the address is written without using the special "%pK" format specifier:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijfz6rY_Aa5MXb2GzWS4vanSkI3sPjqONqOqb4ngQg_gvYhoawUBgJyJAxLjNCiwFJxGjdsPJGvfCo8am5ru0b__Q7YrowY4dioobcZ2RAPL1o4SIzrwsLqCS_aLVpbIVkXPquC1Y1iiob/s1600/Screenshot+from+2015-08-25+17%253A40%253A29.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijfz6rY_Aa5MXb2GzWS4vanSkI3sPjqONqOqb4ngQg_gvYhoawUBgJyJAxLjNCiwFJxGjdsPJGvfCo8am5ru0b__Q7YrowY4dioobcZ2RAPL1o4SIzrwsLqCS_aLVpbIVkXPquC1Y1iiob/s1600/Screenshot+from+2015-08-25+17%253A40%253A29.png" /></a></div>
<br />
For those interested - the actual pointer written is to the "sock" structure, which is the kernel structure containing the actual "socket" structure, which in turns contains all the function pointers to the operations within this socket.<br />
<br />
This means that if, for example, we have a vulnerability that allows us to overwrite a specific kernel address (<a href="http://bits-please.blogspot.co.il/2015/08/android-linux-kernel-privilege.html">like the vulnerability presented in the previous blog post</a>), we could simply:<br />
<ul>
<li>Open a socket and tag it with "<i>qtaguid</i>"</li>
<li>Look for the socket's address within /proc/net/xt_qtaguid/ctrl</li>
<li>Overwrite the pointer to the "socket" structure to an address within our address-space</li>
<li>Populate the overwritten address with a dummy "socket" structure containing fully controller function pointers </li>
<li>Perform any operation on the socket (like closing it), in order to cause the kernel to execute our own code</li>
</ul>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIOHpnHQCqWprA0lc2_pQwk8Tt_g9LmuQPt_D2pdo1l-t6gddbrCrzuZRZSdoF0tsiLzcYzjLSuZALL8ZiOYPb9GZPZJQulivY7r5F53CXAEDjYG8RFLvUNpRCOvJjkO5TIOdCms9GkJF4/s1600/xt_qtaguid.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="216" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIOHpnHQCqWprA0lc2_pQwk8Tt_g9LmuQPt_D2pdo1l-t6gddbrCrzuZRZSdoF0tsiLzcYzjLSuZALL8ZiOYPb9GZPZJQulivY7r5F53CXAEDjYG8RFLvUNpRCOvJjkO5TIOdCms9GkJF4/s640/xt_qtaguid.png" width="640" /></a></div>
<br />
<b>Summing it all up</b><br />
<br />
Just like any other mitigation, <i>kptr_restrict</i> adds a layer of defence which can sometimes slow down an attacker, but is generally not a show-stopper for anyone determined enough. However, unlike most other mitigations, <i>kptr_restrict</i> requires the cooperation of kernel developers to be effective. Right now, things aren't so great. Hopefully this changes :)laginimainebhttp://www.blogger.com/profile/00867710965331025912noreply@blogger.com991tag:blogger.com,1999:blog-2029700426505953971.post-59820367863144085472015-08-16T03:07:00.000+03:002015-08-16T18:02:18.248+03:00Android linux kernel privilege escalation vulnerability and exploit (CVE-2014-4322)<br />
In this blog post we'll go over a Linux kernel privilege escalation vulnerability I discovered which enables arbitrary code execution within the kernel.<br />
<br />
The vulnerability affected all devices based on Qualcomm chipsets (that is, based on the "msm" kernel) since February 2012.<br />
<br />
I'd like to point out that I've <a href="https://www.codeaurora.org/projects/security-advisories/memory-corruption-qseecom-driver-cve-2014-4322">responsibly disclosed this issue to Qualcomm</a>, and they've been great as usual, and fixed the issue pretty quickly (see "Timeline" below). Those of you who are interested in the fix, should definitely check out the link above.<br />
<a name='more'></a><br />
<b>Where are we at?</b><br />
<br />
Continuing our journey of <a href="http://bits-please.blogspot.co.il/2015/03/getting-arbitrary-code-execution-in.html">getting from zero permissions to TrustZone code execution</a>; after recently completing the task of <a href="http://bits-please.blogspot.co.il/2015/08/full-trustzone-exploit-for-msm8974.html">getting to TrustZone from the Linux kernel</a>, we are now looking for a way to gain code execution within the Linux kernel.<br />
<br />
However, as you will see shortly, the vulnerability presented in this post requires some permissions to exploit, namely, it can be exploited from within a process called "mediaserver". This means that it still doesn't complete our journey, and so the next few blog posts will be dedicated to completing the exploit chain, by gaining code execution in mediaserver from zero permissions.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKyedaUUPvts4OmfHF-IBuZPeJnKFmMtHqummsOkZFCVnQ6W3cD2HmICWTaKATPh0w7R6E0LVB48Z8F1f8jZ4iKDhlyyKaB3rsINw6YGIeFu1cUhJ_oBkWB6tSvxs5VP9SiJCsdT39PE2j/s1600/hobbit_map.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKyedaUUPvts4OmfHF-IBuZPeJnKFmMtHqummsOkZFCVnQ6W3cD2HmICWTaKATPh0w7R6E0LVB48Z8F1f8jZ4iKDhlyyKaB3rsINw6YGIeFu1cUhJ_oBkWB6tSvxs5VP9SiJCsdT39PE2j/s1600/hobbit_map.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRTA6O7RkuNSsMsh_0abt8rnB1qlABxtMV1rqUO4x1BM03njTPjfucuBJzHRCZpihpmVoQicI_DJ02-UB44-T6nDGV8B5MAtloLD6-Ywm201Hrrv8jpAn9ge9apT34f_DrkDYrb-rh14vd/s1600/hobbit_map.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<b>Lets go bug hunting</b><br />
<br />
Since we would like to attack the Linux kernel, it stands to reason that we would take a look at all the drivers which are accessible to "underprivileged" Android users. First, let's take a look at all the drivers which are world accessible (under "/dev"):<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhp2huybyfw2rsh78VdbpeaWQwJbgUxz9T6Njr9rrc2t-IWVuxlR0BcMXGjBVaFwZx8DrJDzW_uh4XZo7MKHLwyJVRfon7sqnSwncmcq4oTTfHSwgVITxiMSOpsixinKJxG05fdjB-74NZA/s1600/Screenshot+from+2015-08-12+22%253A49%253A50.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhp2huybyfw2rsh78VdbpeaWQwJbgUxz9T6Njr9rrc2t-IWVuxlR0BcMXGjBVaFwZx8DrJDzW_uh4XZo7MKHLwyJVRfon7sqnSwncmcq4oTTfHSwgVITxiMSOpsixinKJxG05fdjB-74NZA/s1600/Screenshot+from+2015-08-12+22%253A49%253A50.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4ImhXtLh4F7fMu3IP29Gn2mqKXqKldP-CHJLLYxzJtRg86SJQLruePXBYT6f4nEBDa3y7Z8Ne86HffrnFwnVBYW0zDfKRKkqCZchzC86FRYkf7eRk5pKRRcy1u-u7WgPYthJUYBnH90nU/s1600/Screenshot+from+2015-08-12+22%253A43%253A43.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
Unfortunately, this list is rather short - actually, these drivers are all "generic" Android drivers, which are present on all devices (with the exception of "kgsl-3d0"), and have therefore been the subject of quite a lot of prior research.<br />
<br />
After spending a while looking at each of these drivers, it became apparent that a more effective strategy would be to cast a wider net by expanding the number of drivers to be researched, even if they require some permissions in order to interact with. Then, once a vulnerability is found, we would simply need one more vulnerability in order to get from zero permissions to TrustZone.<br />
<br />
One interesting candidate for research is the "qseecom" driver. For those of you who read the <a href="http://bits-please.blogspot.com/2015/03/getting-arbitrary-code-execution-in.html">first blog post</a>, we've already mentioned this driver before. This is the driver responsible for allowing Android code to interact with the TrustZone kernel, albeit using only a well defined set of commands.<br />
<br />
So why is this driver interesting? For starters, it ties in well with the previous blog posts, and everybody loves continuity :) That aside, this driver has quite a large and fairly complicated implementation, which, following the previous posts, we are sufficiently qualified to understand and follow.<br />
<br />
Most importantly, taking a look at the permissions needed to interact with the driver, reveals that we must either be running with the "system" user-ID which is a very high requirement, or we must belong to the group called "drmrpc".<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEicsN2-jodmowpXq6X0IHa8_Hpr9EfWXnISFAdorRIeF6khfcgRa1bZ9Mh6yNMhtXVOYMaCfuLiaxTpPyRNsM2nen3pMK4YY5MoR-BYCuvYDeuZWIu5Ivy4v1EbLpygTYK6K4SewrpSHo39/s1600/Screenshot+from+2015-08-12+23%253A20%253A14.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEicsN2-jodmowpXq6X0IHa8_Hpr9EfWXnISFAdorRIeF6khfcgRa1bZ9Mh6yNMhtXVOYMaCfuLiaxTpPyRNsM2nen3pMK4YY5MoR-BYCuvYDeuZWIu5Ivy4v1EbLpygTYK6K4SewrpSHo39/s1600/Screenshot+from+2015-08-12+23%253A20%253A14.png" /></a></div>
<br />
However, searching for the "drmrpc" group within all the processes on the system, reveals that the following processes are members of the group:<br />
<ul>
<li>surfaceflinger (running with "system" user-ID)</li>
<li>drmserver (running with "drm" user-ID)</li>
<li>mediaserver (running with "media" user-ID)</li>
<li>keystore (running with "keystore" user-ID)</li>
</ul>
<br />
But that's not all! Within the Linux kernel, each process has a flag named "dumpable", which controls whether or not the process can be attached to using <a href="http://linux.die.net/man/2/ptrace">ptrace</a>. Whenever a process changes its permissions by executing "setuid" or "setgid", the flag is automatically cleared by the kernel to indicate that the process cannot be attached to.<br />
<br />
While the "surfaceflinger" and "drmserver" processes modify their user-IDs during runtime, and by doing so protect themselves from foreign "ptrace" attachments, the "mediaserver" and "keystore" processes do not.<br />
<br />
This is interesting since attaching to a process via "ptrace" allows full control of the process's memory, and therefore <a href="https://github.com/windflyer/droid_injectso/blob/master/README.md">enables code execution within that process</a>. As a result, any process running with the same user-ID as one of these two processes can take control of them and by doing so, may access the "qseecom" driver.<br />
<br />
Summing it up, this means that in order to successfully access the "qseecom" driver, an attacker must only satisfy one of the following conditions:<br />
<ul>
<li>Gain execution within one of "mediaserver", "drmserver", "mediaserver" or "keystore"</li>
<li>Run within a process with the "system", "drm" or "keystore" user-ID</li>
<li>Run within a process with the "drmrpc" group-ID</li>
</ul>
<b>Tricksy Hobbitses</b><br />
<br />
Before we start inspecting the driver's code, we should first recall the (mis)trust relationship<b> </b>between user-space and kernel-space.<br />
<br />
Since drivers deal with user input, they must take extreme caution to <u>never trust user supplied data</u>, and always verify it extensively - all arguments passed in by the user should be
considered by the kernel as <i>"tainted"</i>. While this may sound obvious, it's a really important issue that
is overlooked often times by kernel developers.<br />
<br />
In order
to stop kernel developers from making these kinds of mistakes, some
mechanisms were introduced into the kernel's code which help the
compiler detect and prevent such attempts.<br />
<br />
This is
facilitated by marking variables which point to memory within the user's
virtual address space as such, by using the "__user" macro.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg57bjDm3Ytn-10yz5PDpejdBhvsKpKUlV2VZlM5GD_lTkMDG_uZR_tctnTGSWaEI3VznGVGVrD5J47Avhzl-HHXIv1fqiqRL_JfztjTkroqEFJg2wIlIkDJyuHNvh8JthLhj09PTU0vWVv/s1600/Screenshot+from+2015-08-14+18%253A13%253A17.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg57bjDm3Ytn-10yz5PDpejdBhvsKpKUlV2VZlM5GD_lTkMDG_uZR_tctnTGSWaEI3VznGVGVrD5J47Avhzl-HHXIv1fqiqRL_JfztjTkroqEFJg2wIlIkDJyuHNvh8JthLhj09PTU0vWVv/s1600/Screenshot+from+2015-08-14+18%253A13%253A17.png" /></a></div>
When expanded, this macro marks the variable with
the "noderef" attribute. The attribute is used to tag the pointer as
one that cannot be directly dereferenced. If an attempt is made to
directly dereference a pointer marked as such, the compiler will
simply produce an error and refuse to compile the code. <br />
<br />
Instead, whenever the kernel wishes to either read from or write to the
pointer's location, it must do so using <a href="http://lxr.free-electrons.com/ident?i=put_user">specially</a> <a href="http://lxr.free-electrons.com/ident?i=get_user">crafted</a> <a href="http://lxr.free-electrons.com/ident?i=copy_from_user">kernel</a>
<a href="http://lxr.free-electrons.com/ident?i=copy_to_user">functions</a> which make sure that the location pointed to actually resides
within the user's address space (and not within any memory address
belonging to the kernel).<br />
<br />
<b>Getting to know QSEECOM</b><br />
<br />
Drivers come in many shapes and sizes; and can be
interacted with by using quite a wide variety of functions, each of which
with its unique pitfalls and common mistakes.<br />
<br />
When character devices
are registered within the kernel, they must provide a structure containing pointers to the device's implementation for each of the aforementioned functions, determining how it interacts with
the system.<br />
<br />
This means that an initial step in mapping
out the attack surface for this driver would be to take a look at the
functions registered by it:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFUfLuIXuKKpGg7F1nLsb8H1q5j6yq_9PE8Z_adeo99DJbDzkrJlXyBUd9hkHZaHe-PdQ1YbnLQsGNFX2bJ9C8u6PbGKbunNUwK_UGHeJnJhiAe_SnCDSQXRhNR6KdtDduq7E32rMDqRlt/s1600/Screenshot+from+2015-08-14+17%253A44%253A11.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFUfLuIXuKKpGg7F1nLsb8H1q5j6yq_9PE8Z_adeo99DJbDzkrJlXyBUd9hkHZaHe-PdQ1YbnLQsGNFX2bJ9C8u6PbGKbunNUwK_UGHeJnJhiAe_SnCDSQXRhNR6KdtDduq7E32rMDqRlt/s1600/Screenshot+from+2015-08-14+17%253A44%253A11.png" /></a></div>
In
the case of the QSEECOM driver, the only "interesting" function
implemented is the "ioctl" function call. Generally, character devices
can be interacted with just as any other file on the system - they can be
opened, read from, written to, etc. However, when an operation doesn't
neatly map into one of the "normal" file operations, it can be
implemented within a special function called "<b>IOCTL</b>" (<b>I</b>nput/<b>O</b>utput<b> C</b>on<b>t</b>ro<b>l</b>).<br />
<br />
IOCTLs are called using two arguments:<br />
<ul>
<li>The "command" to be executed</li>
<li>The "argument" to be supplied to that function</li>
</ul>
The complete list of supported "commands" can be deduced by reading the source code of the IOCTL's implementation.<br />
<br />
Having said that, lets take a look at the different commands supported by the <a href="https://www.codeaurora.org/cgit/quic/la/kernel/msm-3.10/tree/drivers/misc/qseecom.c?h=LNX.LA.3.6.1#n3327">qseecom_ioctl</a> function. At first glance, it seems as though quite a large range of commands are supported by the driver, such as:<br />
<ul>
<li>Sending command requests to TrustZone</li>
<li>Loading QSEE TrustZone applications</li>
<li>Provisioning different encryption keys</li>
<li>Setting memory parameters for the client of the driver</li>
</ul>
<b>Setting Memory Parameters</b><br />
<br />
In order to allow the user to send large requests to or receive large responses from the TrustZone kernel, the QSEECOM driver exposes a IOCTL command which enables the user to set up his "memory parameters".<br />
<br />
In order to share a large chunk of memory with the kernel, the user first allocates a contiguous physical chunk of memory by using the "ion" driver. <br />
<br />
We won't go into detail about the "ion" driver, but here's the gist of it - it is an Android driver which is used to allocate contiguous physical memory and expose it to the user by means of a file descriptor. After receiving a file descriptor, the user may then map it to any chosen virtual address, then use it as he pleases. This mechanism is advantageous as a means of sharing memory since anyone in possession of the file descriptor may map it to any address within their own virtual address space, independently of one another. <br />
<br />
The "ion" driver also supports different kinds of pools from which memory can be allocated, and a wide variety of flags - for those interested, you can read much more about "ion" and how it works, <a href="https://lwn.net/Articles/480055/">here</a>.<br />
<br />
In the case of QSEECOM, three parameters are used to configure the user's memory parameters:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEge2v_LE9nhKA8_7vpY0Tfbjktgx1K1KfSG55FG6RZIZPh0xCinvakv2TVpXXUy3KgzXDnpLm78gNQ5GPjl2o1jl-c9XyKGVFjzoVg3XIx1-KL8TV6b9x9ybSN2ECbUq2XC4ZVnPGPpn4aq/s1600/Screenshot+from+2015-08-15+20%253A38%253A52.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEge2v_LE9nhKA8_7vpY0Tfbjktgx1K1KfSG55FG6RZIZPh0xCinvakv2TVpXXUy3KgzXDnpLm78gNQ5GPjl2o1jl-c9XyKGVFjzoVg3XIx1-KL8TV6b9x9ybSN2ECbUq2XC4ZVnPGPpn4aq/s1600/Screenshot+from+2015-08-15+20%253A38%253A52.png" /></a></div>
<ul>
<li><i>virt_sb_base</i> - The virtual address at which the user decided to map the ION allocated chunk</li>
<li><i>sb_len</i> - The length of the shared buffer used</li>
<li><i>ifd_data_fd</i> - The "ion" file descriptor corresponding to the allocated chunk</li>
</ul>
The driver actually verifies that the whole range from "virt_sb_base" to "virt_sb_base + sb_len" is accessible to the user (and doesn't, for example, overlap with the kernel's memory).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDqXwNzoqwBYk9onabwufcpv706Aawrf7N9xILBIAW_xpBhUnGe2b-oY1IKgDNqh3tCJwc_ssDqR-UqxcNRjrC-UHPIxAqr3SmJOsmiV0u5whkZnFvp4M4t6pfJn7e7NtM8K7uyZFdPxzu/s1600/Screenshot+from+2015-08-15+20%253A41%253A43.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDqXwNzoqwBYk9onabwufcpv706Aawrf7N9xILBIAW_xpBhUnGe2b-oY1IKgDNqh3tCJwc_ssDqR-UqxcNRjrC-UHPIxAqr3SmJOsmiV0u5whkZnFvp4M4t6pfJn7e7NtM8K7uyZFdPxzu/s1600/Screenshot+from+2015-08-15+20%253A41%253A43.png" /></a></div>
Then, after performing the needed validations, the driver maps the ION buffer to a kernel-space virtual address, and stores all the memory parameters in an internal data structure, from which they can later be retrieved whenever the user performs additional IOCTL calls:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijU70nwRgsQKc_KAPKeHWFYfwrQvoDvVzzEiX4OLXCTooNNHCo0Yyo_x1H19mR3KK00Yiy8RfvMCIMHpcIfqKNC8xZ6LmoW5bTuM3SQYhJoPMtGSDdkeGJTolrtKs-HtgFIhn5nMA34mE-/s1600/Screenshot+from+2015-08-15+20%253A43%253A39.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijU70nwRgsQKc_KAPKeHWFYfwrQvoDvVzzEiX4OLXCTooNNHCo0Yyo_x1H19mR3KK00Yiy8RfvMCIMHpcIfqKNC8xZ6LmoW5bTuM3SQYhJoPMtGSDdkeGJTolrtKs-HtgFIhn5nMA34mE-/s1600/Screenshot+from+2015-08-15+20%253A43%253A39.png" /></a></div>
<br />
Note that four different parameters are stored here:<br />
<ul>
<li>The kernel-space virtual address at which the ION buffer is mapped</li>
<li>The actual physical address of the ION buffer</li>
<li>The user-space virtual address at which the ION buffer is mapped</li>
<li>The length of the shared buffer</li>
</ul>
Since this is quite a lot to remember (and it's only going to get worse :) ), let's start mapping out the current state of the virtual address space:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0fhRKYQ5sTHlhbdp1O7AKXJSJE6bC4qx4UafxzT_7QO5mIezHo9_AewR7UcjNF9yCIdIW5QxNG8aG9fMckyeiZkM_Uv7TgHo3I5r6tNMxTNcpXOrGZluPBTFfrODobWO8f23A89B8fpUl/s1600/initial_vas.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="168" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0fhRKYQ5sTHlhbdp1O7AKXJSJE6bC4qx4UafxzT_7QO5mIezHo9_AewR7UcjNF9yCIdIW5QxNG8aG9fMckyeiZkM_Uv7TgHo3I5r6tNMxTNcpXOrGZluPBTFfrODobWO8f23A89B8fpUl/s640/initial_vas.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEim9O-0l6ls-gV09RcYTtnv4kDBWiUU6imMj2ney2Mn9HN-cxks1IgkgVB55_R664ikmCmaZPervVFKpxAvHWszveouxBjZman4MLHmE_h_RMuIW7x88K1HzpWW-IzowbpSYd_beC9Ejnci/s1600/initial_vas.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiM9O7OyvdosAX5XMLdw-qMffLsCVdMc-8CiKB28X9i-x9S7GxEgPWsGrj0EhsU9gOBZpK_JutDnMQ8FLWGb9SS3Eq8z_pz9U0aVHYADvwkNplTCNiOAVec77vuFhhQickjiYOsZNlv-WCb/s1600/initial_vas.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<b>QSEECOM_IOCTL_SEND_MODFD_CMD_REQ</b><br />
<br />
After going over the code for each of the different supported commands<b>, </b>one command in particular seemed to stick-out as a prime candidate for exploitation - <a href="https://www.codeaurora.org/cgit/quic/la/kernel/msm-3.10/tree/drivers/misc/qseecom.c?h=LNX.LA.3.6.1#n3417">QSEECOM_IOCTL_SEND_MODFD_CMD_REQ</a>.<br />
<br />
This command is used in order to request the driver to send a command to TrustZone using user-provided buffers. As we know, any interaction of the kernel with user-provided data, let alone user-provided memory addresses, is potentially volatile. <br />
<br />
After some boilerplate code and internal housekeeping, the actual function in charge of handling this particular IOCTL command is called - "<a href="https://www.codeaurora.org/cgit/quic/la/kernel/msm-3.10/tree/drivers/misc/qseecom.c?h=LNX.LA.3.6.1#n1527">qseecom_send_modfd_command</a>". <br />
<br />
The function first safely copies the IOCTL argument supplied by the user into a local structure, which looks like this:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvw2-C1R9BkVesJ9ErP2sA6IXjyLwz4VXtUkc4qLRkQ3bayzHH26bD_5MYmNAy15ORoP1A2TMWtFpKDO5eoX1TOuVOzJb49Plmx3K2aZq6xc0mlA5U0nZYRrpFRbjh1fTHvc3db6JS2XIG/s1600/Screenshot+from+2015-08-15+19%253A28%253A33.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvw2-C1R9BkVesJ9ErP2sA6IXjyLwz4VXtUkc4qLRkQ3bayzHH26bD_5MYmNAy15ORoP1A2TMWtFpKDO5eoX1TOuVOzJb49Plmx3K2aZq6xc0mlA5U0nZYRrpFRbjh1fTHvc3db6JS2XIG/s1600/Screenshot+from+2015-08-15+19%253A28%253A33.png" /></a></div>
<br />
The "cmd_req_buf" and "cmd_req_len" fields define the request buffer for the command to be sent, and similarly, "resp_buf" and "resp_len" define the response buffer to which the result should be written.<br />
<br />
Now stop! Do you notice anything fishy in the structure above?<br />
<br />
For starters, there are two pointers within this structure which are not marked as "tainted" in any way (not marked as "__user"), which means that the driver might mistakenly access them later on.<br />
<br />
What comes next, however, is a quite an intimidating wall of verifications which are meant to make sure that the given arguments are, in fact, valid. It seems as though Quacomm win this round...<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRVDNMg2bdOrS9UWZyhvDuOf3tPiYLe_KNBcmB7JIjm-FOkb6vqDYoxSOdhyphenhyphenmi68Gop5FdN070vul7zzUkp_F4rUnNLTUAj0NTYRpPyF2ZIfaJOlWVUOVelGKR5hwiskY3Z6rI0-Cg8YvV/s1600/Screenshot+from+2015-08-15+19%253A53%253A08.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRVDNMg2bdOrS9UWZyhvDuOf3tPiYLe_KNBcmB7JIjm-FOkb6vqDYoxSOdhyphenhyphenmi68Gop5FdN070vul7zzUkp_F4rUnNLTUAj0NTYRpPyF2ZIfaJOlWVUOVelGKR5hwiskY3Z6rI0-Cg8YvV/s1600/Screenshot+from+2015-08-15+19%253A53%253A08.png" /></a> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjddrdIQYBFhodu4Rg9qDUJwOidwK0NKH3LOqhs6Qeq6iY43upGtvJsboXh6KqV86ZYaBHwDc1g47_2oHPDhQ4cRiOUF5HiJzaA1atqHL0aBBibi0qAHFNJU-R3W4u3s_S6K0iya__xglvR/s1600/Screenshot+from+2015-08-15+20%253A02%253A49.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjddrdIQYBFhodu4Rg9qDUJwOidwK0NKH3LOqhs6Qeq6iY43upGtvJsboXh6KqV86ZYaBHwDc1g47_2oHPDhQ4cRiOUF5HiJzaA1atqHL0aBBibi0qAHFNJU-R3W4u3s_S6K0iya__xglvR/s1600/Screenshot+from+2015-08-15+20%253A02%253A49.png" /></a><br />
Or do they?<br />
<br />
Well, let's look at each of the validations performed:<br />
<ul>
<li>First, the function makes sure that the request and response buffers are not NULL.</li>
<li>Next, the function makes sure that both the request and response buffers are within the range of the shared buffer discussed earlier.</li>
<li>Then, the function makes sure that the request buffer's length is larger than zero, and that both the request and the response size do not exceed the shared buffer's length.</li>
<li>Lastly, for each file descriptor passed, the function validates that the command buffer offset does not exceed the length of the command buffer.</li>
</ul>
Before even attempting to scale this wall of verifications, lets first see what's on the other side of it.<br />
<br />
After performing all these validations, the function goes on to convert the request and response buffers from user virtual addresses to kernel virtual addresses:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqA4YtPfuNjRfW3V4D73fuL3OKwzoSlcdxSj4e-TTt0CP40-Qfa6PQ2kzB5k56hdaoa8xWp4KamSAuuKPO2tMIvIO5RuT_1HE5-3YiLOoa3nTJINIurALgL2NYiVMrOurBXHCVqCjNkMxx/s1600/Screenshot+from+2015-08-15+20%253A06%253A28.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqA4YtPfuNjRfW3V4D73fuL3OKwzoSlcdxSj4e-TTt0CP40-Qfa6PQ2kzB5k56hdaoa8xWp4KamSAuuKPO2tMIvIO5RuT_1HE5-3YiLOoa3nTJINIurALgL2NYiVMrOurBXHCVqCjNkMxx/s1600/Screenshot+from+2015-08-15+20%253A06%253A28.png" /></a></div>
Where the actual conversion taking place looks like so:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhP44XKGh9s6t7ALETJNSZd7wduV33T-u7PBgzebHCSl9euvL-hI-HVzjUwZekwWZnrJPm5kCnKGxejOB_eXRo9LMf7pZzjNbSWTCj_omjOb4H_s-of8lt_hc78bcs-4IhhLG65M3aWHtGH/s1600/Screenshot+from+2015-08-15+20%253A09%253A16.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhP44XKGh9s6t7ALETJNSZd7wduV33T-u7PBgzebHCSl9euvL-hI-HVzjUwZekwWZnrJPm5kCnKGxejOB_eXRo9LMf7pZzjNbSWTCj_omjOb4H_s-of8lt_hc78bcs-4IhhLG65M3aWHtGH/s1600/Screenshot+from+2015-08-15+20%253A09%253A16.png" /></a></div>
This actually simply amounts to taking the offset from the given virtual address to the beginning of the user-space virtual address for the shared buffer, and adding it to the kernel-space virtual address for the shared buffer. This is because, as mentioned earlier, the kernel maps the ION buffer to a kernel-space virtual address which is unrelated to the user-space virtual address to which the user mapped the buffer. So before the kernel can interact with any pointer within the shared buffer, it must first convert the address to a virtual address within it's own address space. <br />
<br />
What comes next, however, is extremely interesting! The driver passes on the request and response buffers, which should now reside within kernel-space, to an internal function called "__qseecom_update_cmd_buf" - and therein lies the holy grail! The function actually writes data to the converted kernel-space address of the request buffer.<br />
<br />
<br />
We'll expand more on the exact nature of the data written later on, but hopefully by now you're convinced if we are able to bypass the verifications above while still maintaining control of the final kernel-space address of the request buffer, we would achieve a kernel write primitive, which seems quite tempting.<br />
<br />
<b>"Bring down this wall!"</b><br />
<br />
First, let's start by mapping out the locations of the request and response buffers within the virtual address space:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqvL4KHnVyk-UhPwtB3Ml_cM_cideUEWQ1BnKR2I2OmQ0DlErUbwYhLW7BAclAM_gt7SRPNm87JMwxybnyn1zZhCV5XxVuaY6YbYr7NgvurAS4s8tRoIJsiZwAHIjT_GOMWJs8QM4l9gnG/s1600/initial_vas.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="221" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqvL4KHnVyk-UhPwtB3Ml_cM_cideUEWQ1BnKR2I2OmQ0DlErUbwYhLW7BAclAM_gt7SRPNm87JMwxybnyn1zZhCV5XxVuaY6YbYr7NgvurAS4s8tRoIJsiZwAHIjT_GOMWJs8QM4l9gnG/s640/initial_vas.png" width="640" /></a></div>
<br />
Now, as we already know, when setting the memory parameters, the buffer starting at "virt_sb_base" and ending at "virt_sb_base + sb_len" must reside entirely within user-space. This is facilitated by the following check:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1buS3hnfZ_0eRAIvd9GGq2znvJoZmxBvunk_ms1FjoZ_ScCRzN_3hVxClyqZ0np1TQNC4VcCrZ-MePvKUwU1g7G4qtVyXU6kpjLoGxo1e63OligdoaqZMvQkAumBHKoV12IzxCOzFnkna/s1600/Screenshot+from+2015-08-15+20%253A41%253A43.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1buS3hnfZ_0eRAIvd9GGq2znvJoZmxBvunk_ms1FjoZ_ScCRzN_3hVxClyqZ0np1TQNC4VcCrZ-MePvKUwU1g7G4qtVyXU6kpjLoGxo1e63OligdoaqZMvQkAumBHKoV12IzxCOzFnkna/s1600/Screenshot+from+2015-08-15+20%253A41%253A43.png" /></a></div>
<br />
Also, the verifications above make sure that both the "cmd_req_buf" and "resp_buf" pointers are within the user-space virtual address range of the shared buffer.<br />
<br />
However, what would happen if we were to map a huge shared buffer - one so large that it cannot be contained within kernel space? Well, a safe assumption might be that when we'd attempt to set the memory parameters for this buffer, the request would fail, since the kernel will not be able to map the buffer to it's virtual address space.<br />
<br />
Luckily, though, the IOCTL with which the memory parameters are set only uses the user-provided buffer length in order to verify that the user-space range of the shared buffer is accessible by the user (see the access check above). However, when it actually maps the buffer to its own address-space, it does so by simply using the ION file descriptor, without verifying that the buffer's actual length equals the one provided by the user.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYEGeiHpaz1LGMMeuH62TAinjCtvovgw9c19YI0kACCrnIa1CyK-RmRh11qvYyCUR1-BNj_KOFtHn71pJ-8ydn5FVK1aOrtZ-qNUZ44lYz6DUS5XplDEjaqUmMmV6Dqdt8nDFwNRojgAPA/s1600/Screenshot+from+2015-08-15+22%253A55%253A30.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYEGeiHpaz1LGMMeuH62TAinjCtvovgw9c19YI0kACCrnIa1CyK-RmRh11qvYyCUR1-BNj_KOFtHn71pJ-8ydn5FVK1aOrtZ-qNUZ44lYz6DUS5XplDEjaqUmMmV6Dqdt8nDFwNRojgAPA/s1600/Screenshot+from+2015-08-15+22%253A55%253A30.png" /></a></div>
<br />
This means we could allocate a small ION buffer, and pass it to QSEECOM while claiming it actually corresponds to a huge area. As long as the entire area lies within user-space and is write-accessible to the user, the driver will happily accept these parameters and store them for us. But is this feasible? After all, we can't really allocate such a huge chunk of memory within user-space - there's just not enough physical memory to satisfy such a request. What we could do, however, is <u>reserve</u> this memory area by using <i>mmap</i>. This means that until the data is actually written to, it is not allocated, and therefore we can freely map an area of any size for the duration of the validation performed above, then unmap it once the driver is satisfied that the area is indeed writeable.<br />
<br />
From now on, let's assume we map the fake shared buffer at the virtual address 0x10000000 and the mapping size is 0x80000000.<br />
<br />
Recall that if the command and response buffer are deemed valid, they are converted to the corresponding kernel-space virtual addresses, then the converted request buffer is written to at the given offset. Putting it all together, we are left with the following actual write destination:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijaYs8OEFhTBVNF1XMSFGwVI5S6GGBlQ7G1oGh2qqn09eHhmzM0LLxcGIDpMMEPtXLpb9LmlPZD7Bi9oqWHn0ed9hFOSTR94MLU_fVTMXje5JOL0hvQn6bwNzchJ8IGqrhtnc6cXc7woL_/s1600/dest_addr.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijaYs8OEFhTBVNF1XMSFGwVI5S6GGBlQ7G1oGh2qqn09eHhmzM0LLxcGIDpMMEPtXLpb9LmlPZD7Bi9oqWHn0ed9hFOSTR94MLU_fVTMXje5JOL0hvQn6bwNzchJ8IGqrhtnc6cXc7woL_/s1600/dest_addr.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiITpAgVCbW3dWhtquFpduvFVlfHXUKI8ROZdGuEI0HYOkm6YGmi3Kf0Q31fvqISl3c9kvy4LE7eGNYw7fAv_3q_tWNm7a4vgEoLbISzK55XqtUw6l-JnRy3gMt7FYhti4PgS29WqnEI6_Q/s1600/dest_addr.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<div class="separator" style="clear: both; text-align: left;">
Can you spot the mistake in the calculation above? Here it goes -</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Since the kernel believes the shared buffer is huge, this means that the "cmd_req_buf" may point to any address within that range, and in our case, any address within the range [0x10000000, 0x90000000]. It also means that the "cmd_buf_offset" can be as large as 0x80000000, which is the fake size of the shared buffer.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Adding up two such huge numbers would doubtless cause an overflow in the calculation above, which means that the resulting address may not be within the kernel's shared buffer after all!</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
(Before you read on, you may want to try and work the needed values to exploit this on your own.)</div>
<br />
<b>Finding the kernel's shared buffer</b><br />
<br />
As you can see in the calculation above, the location of the kernel's shared buffer is still unknown to us. This is because it is mapped during runtime, and this information is not exposed to the user in any way. However, this doesn't mean we can't find it on our own.<br />
<br />
If we were to set the "cmd_buf_offset" to zero, that would mean that the destination write address for the kernel would be:<br />
<br />
<div style="text-align: center;">
sb_virt - 0x10000000 + cmd_req_buf + 0x0</div>
<br />
Now, since we know the "sb_virt" address is actually within the kernel's heap, it must be within the kernel's memory range (that is, larger than 0xC0000000). This means that for values of "cmd_req_buf" that are larger than (0xFFFFFFFF - 0xD0000000), the calculation above would surely overflow, resulting in a low user-space address.<br />
<br />
This turns out to be really helpful. We can now allocate a sterile "dropzone" within the lower range of addresses in user-space, and fill it with a single known value.<br />
<br />
Then, after we trigger the driver's write primitive, using the parameters described above, we could inspect the dropzone and find out where it has been "disturbed" - that is, where has a value been changed. Since we know only a single overflow happened in the destination address calculation, this means that we can simply reverse the calculation (and add 0xFFFFFFFF + 1) to find the original address of "sb_virt".<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmSrHYmxuxtqyw_dwvn2X6HHDT_2bevDR9TlEB7hsrfQnSH-WC0DyripPzwu6C4cKbmg1sFwzXQHgvSISJgI1MCijuf9BeXczNlSJhIOr34nOFKr83FcJTkja-QhCb2I-Xva5AjmEVXFzJ/s1600/overflow_diag.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmSrHYmxuxtqyw_dwvn2X6HHDT_2bevDR9TlEB7hsrfQnSH-WC0DyripPzwu6C4cKbmg1sFwzXQHgvSISJgI1MCijuf9BeXczNlSJhIOr34nOFKr83FcJTkja-QhCb2I-Xva5AjmEVXFzJ/s1600/overflow_diag.png" /></a></div>
<br />
<b>Creating a controlled write primitive</b><br />
<b> </b><b><br /></b><br />
Now that we know the exact address of "sb_virt", we are free to manipulate the arguments accordingly in order to control the destination address freely. Recall that the destination address is structured like so:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9d_COVe8pZ0gQna-ixzVR2ihmReQ4USSFfumI9fzkkTH3M5vdWySUmtlsQMvd_NGOJBbqOjSpFy7qatoXO9aqfPf-A2v3OxLLxDpF0x2ey1Rxn0TYuAiV1IzrhW1PrLjwz3A8BWL5jgOX/s1600/dest_addr_known.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9d_COVe8pZ0gQna-ixzVR2ihmReQ4USSFfumI9fzkkTH3M5vdWySUmtlsQMvd_NGOJBbqOjSpFy7qatoXO9aqfPf-A2v3OxLLxDpF0x2ey1Rxn0TYuAiV1IzrhW1PrLjwz3A8BWL5jgOX/s1600/dest_addr_known.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCu2VyA-5nqeZic_zJf3XpXHY9Y9HZsi3eqgQIMMnMDK6lXe1m2F2x-9Y9s1q3Erx73312yYhL0cIw8PY09jQafRFT5FxLJq9R3trJD1UVv0PftDez6Jh-C0B1Cwz4AB214APJHcyaQQ0k/s1600/dest_addr_known.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
Now, since all the arguments are known, and the sum "cmd_req_buf" and "cmd_buf_offset" can exceed 0xFFFFFFFF, this means that we can simply modify any address following sb_virt, by setting the following values:<br />
<ul>
<li>user_virt_sb_base = 0x10000000</li>
<li>cmd_req_buf + cmd_buf_offset = (0xFFFFFFFF + 1) + 0x10000000 + wanted_offset</li>
</ul>
This means that the destination write address would be:<br />
<br />
<div style="text-align: center;">
dest_addr = sb_virt - user_virt_sb_base + cmd_req_buf + cmd_buf_offset</div>
<div style="text-align: center;">
<br />
<div style="text-align: left;">
Substituting the variables with the values above:</div>
<br /></div>
<div style="text-align: center;">
dest_addr = sb_virt - 0x10000000 + (0xFFFFFFFF + 1) + 0x10000000 + wanted_offset</div>
<div style="text-align: center;">
<br />
<div style="text-align: left;">
Which equals:</div>
<br /></div>
<div style="text-align: center;">
dest_addr = sb_virt + (0xFFFFFFFF + 1) + wanted_offset</div>
<div style="text-align: center;">
<br /></div>
<div style="text-align: left;">
But since adding 0xFFFFFFFF + 1 will cause an overflow which will result in the same original value, we are therefore left with:</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: center;">
dest_addr = sb_virt + wanted_offset</div>
<div style="text-align: center;">
<br /></div>
<div style="text-align: left;">
Meaning we can easily control the destination to which the primitive will write its data, by choosing the corresponding "wanted_offset" for each destination address.</div>
<br />
<b>Exploiting the write primitive</b><br />
<br />
Now that we have a write primitive, all that's left is for us to exploit it<b>. </b>Fortunately, our write primitive allows us to overwrite any kernel address. However, we still cannot control the data written - actually, going over the code of the vulnerable "__qseecom_update_cmd_buf" reveals that it actually writes a physical address related to the ION buffer to the target address:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQAADGbsvgH3doVPPslNBKujATTLv_MB_lHfbrgkYxEl6XPM_nPtQvJ6L4ycrOCcTDDoOSzgAFD6SvNU46-7hNbCgvbLaw_VDNMs3KR9c1jIHuPLtcsHkzsYE3X2PZ2L-zI8qgcd-dMsjF/s1600/Screenshot+from+2015-08-16+01%253A31%253A55.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQAADGbsvgH3doVPPslNBKujATTLv_MB_lHfbrgkYxEl6XPM_nPtQvJ6L4ycrOCcTDDoOSzgAFD6SvNU46-7hNbCgvbLaw_VDNMs3KR9c1jIHuPLtcsHkzsYE3X2PZ2L-zI8qgcd-dMsjF/s1600/Screenshot+from+2015-08-16+01%253A31%253A55.png" /></a></div>
However, recall that previously, when we discovered the address of "sb_virt", we did so by detecting a modified DWORD at a preallocated "sterile" dropzone. This means that the actual value of this physical address is in fact known to us at this point as well. Moreover, all physical addresses corresponding to the "System RAM" on Qualcomm devices are actually "low" addresses, meaning, they are all definitely lower than the kernel's <u>virtual</u> base address (0xC0000000).<br />
<br />
With that in mind, all that's left for us is to overwrite a function pointer within the kernel with our write primitive. Since the DWORD written will correspond to an address which is within the user's virtual address space, we can simply allocate an executable code stub at that address, and redirect execution from that function stub to any other desired piece of code.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-qAskBe_C99vhpnS6zUS-ANYhZLkFaNMxmR1FiCdt_OcJmfBJNty5uaRioR7I5VmGAw9hm0yGYRU9RYsOQQiqEEr9bpXM96Oe4qGvzUoSj9lu26jjw_RaUkkBiTKJ8VhSW_y99G_ROXrE/s1600/redirect_exec.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-qAskBe_C99vhpnS6zUS-ANYhZLkFaNMxmR1FiCdt_OcJmfBJNty5uaRioR7I5VmGAw9hm0yGYRU9RYsOQQiqEEr9bpXM96Oe4qGvzUoSj9lu26jjw_RaUkkBiTKJ8VhSW_y99G_ROXrE/s1600/redirect_exec.png" /></a></div>
<br />
One such location containing function pointers can be found within the "pppolac_proto_ops" structure. This is the structure used within the kernel to register the function pointers used when interacting with sockets of the PPP_OLAC protocol. This structure is suitable because:<br />
<ul>
<li>The PPP_OLAC protocol isn't widely used, so there's no immediate need to restore the overwritten function pointer</li>
<li>There are no special permissions needed in order to open a PPP_OLAC socket, other than the ability to create sockets</li>
<li>The structure itself is static (and therefore stored in the BSS), and is not marked as "const", and is therefore writeable </li>
</ul>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiiZoNwd_byl2NTBvxPm6jhKGijlMEBt5W0KyvPuMkHDvRNQVrsEnSsvgz5UKsRpzLaocdOXLe5ZDtu4b8h3cfxTuhlvnqclrwjeY18_uwUhSi3uBKJyFlfUBYjLAmaMhDUCwFwz5Vq5lfn/s1600/Screenshot+from+2015-08-16+02%253A07%253A43.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="286" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiiZoNwd_byl2NTBvxPm6jhKGijlMEBt5W0KyvPuMkHDvRNQVrsEnSsvgz5UKsRpzLaocdOXLe5ZDtu4b8h3cfxTuhlvnqclrwjeY18_uwUhSi3uBKJyFlfUBYjLAmaMhDUCwFwz5Vq5lfn/s400/Screenshot+from+2015-08-16+02%253A07%253A43.png" width="400" /></a></div>
<br />
<b>Putting it all together</b> <br />
<br />
At this point, we have the ability to execute arbitrary code within the kernel, thus completing our exploit. Here's a short recap of the steps we needed to perform:<br />
<ul>
<li>Open the QSEECOM driver </li>
<li>Map a ION buffer </li>
<li>Register faulty memory parameters which include a fake huge memory buffer</li>
<li>Prepare a sterile dropzone in low user-space addresses</li>
<li>Trigger the write primitive into a low user-space address</li>
<li>Inspect the dropzone in order to deduce the address of "sb_virt" and the contents written in the write primitive</li>
<li>Allocate a small function stub at the address which is written by the write primitive</li>
<li>Trigger the write primitive in order to overwrite a function pointer within "pppolac_proto_ops"</li>
<li>Open a PPP_OLAC socket and trigger a call to the overwritten function pointer</li>
<li>Execute code within the kernel :)<b> </b></li>
</ul>
<b>Into the Wild</b><br />
<br />
Shortly after the patch was
issued and the vulnerability was fixed, I was alerted by a friend on
mine to the fact that an exploit has been developed for the
vulnerability and the exploit has been incorporated into a popular
rooting kit (<a href="http://forum.xda-developers.com/crossdevice-dev/sony/giefroot-rooting-tool-cve-2014-4322-t3011598">giefroot</a>), in order to achieve kernel code execution.<br />
<br />
Luckily,
the exploit for the vulnerability was quite poorly written (I've fully
reverse engineered it), and so it didn't support all the range of
vulnerable devices.<br />
<br />
Now that the issue has been fixed for a
while, I feel that it's okay to share the full vulnerability writeup
and exploit code, since all devices with kernels compiled after November
2014 should be patched. I've also made sure to use a single symbol within the exploit, to prevent widespread usage by script-kiddies (although this constraint can easily be removed by dynamically finding the pointer mentioned above during the exploit).<br />
<br />
<b>The Code</b><br />
<br />
I've written an exploit for this vulnerability, <a href="https://github.com/laginimaineb/cve-2014-4322/">you can find it here</a>.<br />
<br />
Building the exploit actually produces a shared library, which exports a function called "execute_in_kernel". You may use it to execute any given function within the context of the kernel. Play safe!<br />
<br />
<b>Timeline</b><br />
<ul>
<li>24.09.14 - Vulnerability disclosed</li>
<li>24.09.14 - Initial response from QC</li>
<li>30.09.14 - Issue triaged by QC</li>
<li>19.11.14 - QC issues notice to customers </li>
<li>27.12.14 - Issue closed, <a href="https://www.codeaurora.org/projects/security-advisories/memory-corruption-qseecom-driver-cve-2014-4322">CAF advisory issued</a></li>
</ul>
laginimainebhttp://www.blogger.com/profile/00867710965331025912noreply@blogger.com353tag:blogger.com,1999:blog-2029700426505953971.post-6133420655210427532015-08-10T10:19:00.000+03:002015-11-14T01:14:49.728+02:00Full TrustZone exploit for MSM8974In this blog post, we'll cover the complete process of exploiting the TrustZone vulnerability described in the <a href="http://bits-please.blogspot.com/2015/08/exploring-qualcomms-trustzone.html">previous post</a>. If you haven't read it already, please do! <br />
<br />
<b>Responsible Disclosure</b><br />
<br />
First of all, I'd like to point out that I've <a href="https://www.qualcomm.com/connect/contact/security/product-security">responsibly disclosed this vulnerability to Qualcomm</a>, and the issue has already been fixed (see "Timeline" below).<br />
<br />
I'd also like to take this opportunity to point out that Qualcomm did an amazing job in both responding to the disclosure amazingly fast and by being very keen to fix the issue as soon as possible.<br />
<br />
They've also gifted me a brand new (at the time) Moto X 2014, which will be the subject of many posts later on (going much more in depth into TrustZone's architecture and other security components on the device).<br />
<br />
<a name='more'></a><br />
<b>Patient Zero</b><br />
<br />
While developing this exploit, I only had my trusty (personal) Nexus 5 device to work with. This means that all memory addresses and other specific information written below is taken from that device.<br />
<br />
In case anyone wants to recreate the exact research described below, or for any other reason, the exact version of my device at the time was:<br />
<div style="text-align: center;">
<br /></div>
<div style="text-align: center;">
<pre wrap="">google/hammerhead/hammerhead:4.4.4/KTU84P/1227136:user/release-keys</pre>
</div>
<br />
With that out of the way, let's get right to it!<br />
<br />
<b>The vulnerability primitive</b><br />
<br />
If you read the <a href="http://bits-please.blogspot.com/2015/08/exploring-qualcomms-trustzone.html">previous post</a>, you already know that the vulnerability allows the attacker to cause the TrustZone kernel to write a zero DWORD to any address in the TrustZone kernel's virtual address space.<br />
<br />
Zero write primitives are, drawing on personal experience, not very fun to work with. They are generally quite limited, and don't always lead to exploitable conditions. In order to create a robust exploit using such a primitive, the first course of action would be to attempt to leverage this weak primitive into a stronger one.<br />
<br />
<b>Crafting an arbitrary write primitive</b><br />
<br />
Since the TrustZone kernel is loaded at a known physical address, this means that all of the addresses are already known in advance, and do not need to be discovered upon execution.<br />
<br />
However, the internal data structures and state of the TrustZone kernel are largely unknown and subject to change due to the many different processes interacting with the TrustZone kernel (from external interrupts, to "Secure World" applications, etc.).<br />
<br />
Moreover, the TrustZone code segments are mapped with read-only access
permissions, and are verified during the secure boot process. This means
that once TrustZone's code is loaded into memory, it theoretically cannot (and should
not) be subject to any change.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlPT4ihjGzvFdrtc0FdnTtOIqQA03id1TDtnJ6rYqYBGxKx7TjrvmLiGir_Xz8nd0XknU-ZlKjl-A41wk1efh_kjP9hoGQTa_iX9EIf10TeeInaDxh6MkCRlMW4mHUNQSPTKj-XaI59-U/s1600/aaa.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlPT4ihjGzvFdrtc0FdnTtOIqQA03id1TDtnJ6rYqYBGxKx7TjrvmLiGir_Xz8nd0XknU-ZlKjl-A41wk1efh_kjP9hoGQTa_iX9EIf10TeeInaDxh6MkCRlMW4mHUNQSPTKj-XaI59-U/s1600/aaa.png" /></a></div>
<div style="text-align: center;">
<span style="font-size: x-small;">TrustZone memory mappings and permissions</span></div>
<br />
So that said - how can we leverage a zero write primitive to enable full code execution?<br />
<br />
We could try and edit any modifiable data (such as the heap, the stack or perhaps globals) within the TrustZone kernel, which might allow us to create a stepping stone for a better primitive. <br />
<br />
As we've mentioned in the previous blog post, normally, when an SCM command is called, any argument which is a pointer to memory, is
validated by the TrustZone kernel. The validation is done in order to make sure the physical address is within an "allowed" range, and isn't for example, within the TrustZone kernel's used memory ranges.<br />
<br />
These validations sound like a prime candidate for us to look into, since if we were able to disable their operation, we'd be able to leverage other SCM calls in order to create different kinds of primitives.<br />
<br />
<b>TrustZone memory validation</b><br />
<b><br /></b>
Let's start by giving the memory validation function a name - from now on, we'll call it "tzbsp_validate_memory".<br />
<br />
Here's a decompilation of the function:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYAutQytShCRI-VGJbQgi-2Xkc5lZeW3cBuRTi6LTaY6hyDUEL7FAOFh8UeSF5bclAZ0PFxiRHkgkSI9JVxShcTvo6IitH2NoFSPKTnhenlVSz9jrW0gqNioMIdyIW6WWNiLmYCichRF6T/s1600/Screenshot+from+2015-08-08+02%253A43%253A29.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYAutQytShCRI-VGJbQgi-2Xkc5lZeW3cBuRTi6LTaY6hyDUEL7FAOFh8UeSF5bclAZ0PFxiRHkgkSI9JVxShcTvo6IitH2NoFSPKTnhenlVSz9jrW0gqNioMIdyIW6WWNiLmYCichRF6T/s1600/Screenshot+from+2015-08-08+02%253A43%253A29.png" /></a></div>
The function actually calls two internal functions to perform the validation, which we'll call "is_disallowed_range" and "is_allowed_range", respectively.<br />
<br />
<b>is_disallowed_range</b><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9F6WNpfxkeg6rAo6iRsdtK0sXJGmm_aD02kk4eA_em4CCdHO5Hib1yWEDANyxuloRxzXrAK5t9NiFUWymAVNwJpWamlvmSrJyvSE5u3YMPNT9y5Muyorty1Y0unlM93D9HPS3U5w630mm/s1600/Screenshot+from+2015-08-08+02%253A44%253A46.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9F6WNpfxkeg6rAo6iRsdtK0sXJGmm_aD02kk4eA_em4CCdHO5Hib1yWEDANyxuloRxzXrAK5t9NiFUWymAVNwJpWamlvmSrJyvSE5u3YMPNT9y5Muyorty1Y0unlM93D9HPS3U5w630mm/s1600/Screenshot+from+2015-08-08+02%253A44%253A46.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTVWrjgcnPQWpVdOGy5NiW9fd3jaiKqgVHQI4xCNtq6jOzcSf4j3z6haWgyht56pEmqJ2kK1hSlJo4g3mJOl99rFXOjQWKn9r9i89XPXnXCfeEqXCsZxDu7-uByOKSonDlKNxCOTOcB3dG/s1600/Screenshot+from+2015-08-08+01%253A41%253A10.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
As you can see, the function actually uses the first 12 bits of the given address in the following way:<br />
<ul>
<li>The upper 7 bits are used as an index into a table, containing 128 values, each 32-bit wide.</li>
<li>The lower 5 bits are used as the bit index to be checked within the 32-bit entry which is present at the previously indexed location.</li>
</ul>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhC1Jd9VvuKRNJ5Mu4Y-nZetBu9ivW5wByYJ4lsOQqE-dYSTmSvH4WMsFmDx0fJxlrY06WiasVJbs7rmQ4mYhGg6VKrmDZUx7DyeHfuLmFE8_kzQQxcptUMKrLu8t0enK3qln76Y7U39u7A/s1600/Screenshot+from+2015-08-09+00%253A59%253A36.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="288" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhC1Jd9VvuKRNJ5Mu4Y-nZetBu9ivW5wByYJ4lsOQqE-dYSTmSvH4WMsFmDx0fJxlrY06WiasVJbs7rmQ4mYhGg6VKrmDZUx7DyeHfuLmFE8_kzQQxcptUMKrLu8t0enK3qln76Y7U39u7A/s640/Screenshot+from+2015-08-09+00%253A59%253A36.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3iJxcuXlP_XXshdzfBEi3xrmao9nxAuF8CDdEnNr8DGKdrL7CaZrHqGI3fjNgqOeA3CzSSWMV3878BBEbsTIeCsdcjxSrucwNRcKJCTMv7U9B52qh5m8g2GzeZ_TR5YSsfpBQ8xP-uEsi/s1600/Screenshot+from+2015-08-09+00%253A58%253A37.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
In other words, for each 1MB chunk that intersects the region of memory to be validated, there exists a bit in the aforementioned table which is used to denote whether or not this region of data is "disallowed" or not. If any chunk within the given region is disallowed, the function returns a value indicating as such. Otherwise, the function treats the given memory region as valid.<br />
<br />
<b>is_allowed_range</b><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSSwAI67UL4iVTpoEra0qArQIO8oxL5R0rq36nBbbobB9TUbmnHa3psPUqxnUkOQ2PN_g_mtw8MzV_IMr1Mkkc3ws-mJhzRfI1t0ZHGZgWKtiz_FGWTaP-T5Tvv8WznmFk_ai2j653Q1gM/s1600/Screenshot+from+2015-08-09+01%253A15%253A42.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSSwAI67UL4iVTpoEra0qArQIO8oxL5R0rq36nBbbobB9TUbmnHa3psPUqxnUkOQ2PN_g_mtw8MzV_IMr1Mkkc3ws-mJhzRfI1t0ZHGZgWKtiz_FGWTaP-T5Tvv8WznmFk_ai2j653Q1gM/s1600/Screenshot+from+2015-08-09+01%253A15%253A42.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZ3ddZC9vRsaABBFW8ouy80t1hlctJsGVwyfC_TTjC0pTMUXk20b9AjOiGE9lHasEWbwO1y8kar1-bQ19_pfwxLpEAVB6Ksni6ebwvZaqdu0HdFnpRbOP1UFshUe95okT6k56wZKuQ4IBK/s1600/Screenshot+from+2015-08-09+01%253A00%253A27.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
Although a little longer, this function is also quite simple. Essentially, it simply goes over a static array containing entries with the following structure:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgfk_ytsQB1G4RwA1-VNlksZ8ksIIyS7Gm4-outtXDs9D5_esqw3393xRiJD2b4vPU8_dxoLxdnrBi-MB1DlNlDSaCG3cjhc8po1aRLDqNzGSOdBcEwBd8oA8yH9PMDHkaqa8G6psHAVD5r/s1600/Screenshot+from+2015-08-09+01%253A18%253A41.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgfk_ytsQB1G4RwA1-VNlksZ8ksIIyS7Gm4-outtXDs9D5_esqw3393xRiJD2b4vPU8_dxoLxdnrBi-MB1DlNlDSaCG3cjhc8po1aRLDqNzGSOdBcEwBd8oA8yH9PMDHkaqa8G6psHAVD5r/s1600/Screenshot+from+2015-08-09+01%253A18%253A41.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh26jeqhF4UBiThkBDATCfvyztI5N5wLs9SniMTVxp0kFgL2LQ5XnEU_ndpAPoRK-iJ3Jnn0CXT8DWWAgGUcAL9Irn5YbphYZCOL1s7PWdNCsfZUzA8nswqFwNC_aE-N-FP7aYFyGfc7QzG/s1600/Screenshot+from+2015-08-09+01%253A16%253A35.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<br />
The function iterates over each of the entries in the table which resides at the given memory address, stopping when the "end_marker" field for the current entry is 0xFFFFFFFF. <br />
<br />
Each range specified by such an entry, is validated against to make sure that the memory range is allowed. However, as evidenced in the decompilation above, entries in which the "flags" fields' second bit is set, are skipped!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcECmfjG5TU8SKlZMlz_Pg1BgqZvLJKXC6hYVmhbczsdFy3EU-vZrzSdUX4mONNXq-ycZFH49bGp1x8CE0SpqXyhaz7wAidE57Luj5O9RS722JSoJPmb56g87_d3oCy_WRnFC_9iBjG6n5/s1600/Screenshot+from+2015-08-09+01%253A21%253A44.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcECmfjG5TU8SKlZMlz_Pg1BgqZvLJKXC6hYVmhbczsdFy3EU-vZrzSdUX4mONNXq-ycZFH49bGp1x8CE0SpqXyhaz7wAidE57Luj5O9RS722JSoJPmb56g87_d3oCy_WRnFC_9iBjG6n5/s1600/Screenshot+from+2015-08-09+01%253A21%253A44.png" /></a></div>
<br />
<b>Attacking the validation functions</b><br />
<br />
Now that we understand how the validation functions operate, let's see how we can use the zero write primitive in order to disable their operation.<br />
<br />
First, as described above, the "is_disallowed_range" function uses a table of 32-bit entries, where each bit corresponds to a 1MB block of memory. Bits which are set to one represent disallowed blocks, and zero bits represent allowed blocks.<br />
<br />
This means that we can easily neutralise this function by simply using the zero write primitive to set <i>all the entries in the table</i> to zero. In doing so, all blocks of memory will now be marked as allowed.<br />
<br />
Moving on to the next function; "is_allowed_range". This one is a little tricky - as mentioned above, blocks in which the second bit in the flags field is set, are validated against the given address. However, for each block in which this bit is not set, no validation is performed, and the block is skipped over.<br />
<br />
Since in the block table present in the device, only the first range is relevant to the memory ranges which reside within the TrustZone kernel's memory range, we only need to zero out this field. Doing so will cause it to be skipped over by the validation function, and, as a result, the validation function will accept memory addresses within the TrustZone kernel as valid.<br />
<br />
<b>Back to crafting a write primitive</b><br />
<br />
So now that we've gotten rid of the bounds check functions, we can freely supply any<br />
memory address as an argument for an SCM call, and it will be operated upon without any obstacle.<br />
<br />
But are we any closer to creating a write primitive? Ideally, had there been an SCM call where we could control a chunk of data which is written to a controlled location, that would have sufficed.<br />
<br />
Unfortunately, after going over all of the SCM calls, it appears that there are no candidates which match this description.<br />
<br />
Nevertheless, there's no need to worry! What cannot be achieved with a single SCM call, may be possible to achieve by stringing a few calls together. Logically, we can split the creation of an arbitrary write primitive into the following steps:<br />
<ul>
<li><i><u>Create</u></i> an <i>uncontrolled</i> piece of data at a <i>controlled</i> location</li>
<li><i><u>Control</u></i> the created piece of data so that it actually contains the wanted content</li>
<li><i><u>Copy</u></i> the created data to the target location </li>
</ul>
<b>Create</b><br />
<br />
Although none of the SCM calls seem to be good candidates in order to create a controlled piece of data, there is one call which can be used to create an <i>uncontrolled</i> piece of data at a <i>controlled</i> location - "tzbsp_prng_getdata_syscall".<br />
<br />
This function, as its name implies, can be used to generate a buffer of random bytes at a given location. It is generally used by Android is order to harness the hardware PRNG which is present in Snapdragon SoCs.<br />
<br />
In any case, the SCM call receives two arguments; the output address, and the output length (in bytes).<br />
<br />
On the one hand, this is great - if we (somewhat) trust the hardware RNG, we can be pretty sure that for each byte we generate using this call, the entire range of byte values is possible as an output. On the other hand, this means that we have <u>no control</u> whatsoever on what data is actually going to be generated.<br />
<br />
<b>Control</b><br />
<br />
Even though any output is possible<b> </b>when using the PRNG, perhaps there is some way in which we could be able to verify that the generated data is actually the data that we wish to write.<br />
<br />
In order to do so, let's think of the following game - imagine you have a slot machine with four slots, each with 256 possible values. Each time you pull the lever, all the slots rotate simultaneously, and a random output is presented. How many times would you need to pull the lever in order for the outcome to perfectly match a value that you picked beforehand? Well, there are 4294967296 (2^32) possible values, so each time you pull the lever, there's a chance of about 10^(-10) that the result would match your wanted outcome. Sounds like you're going to be here for a while...<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzhXKQGi6-5fYjM_z6N5G_BPolA8p3kmQMb9lsSFtKd_TjUkvxVtb6cf7kzvcpCW4oNv4249F0VVI1zlfsgN7yKgVFy5EX0AbwDeaz6Fv4xmaYZDWHdBSCtpucjPAw3XMSS5b9HL6lwFVf/s1600/jackpot-slot-machine.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="256" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzhXKQGi6-5fYjM_z6N5G_BPolA8p3kmQMb9lsSFtKd_TjUkvxVtb6cf7kzvcpCW4oNv4249F0VVI1zlfsgN7yKgVFy5EX0AbwDeaz6Fv4xmaYZDWHdBSCtpucjPAw3XMSS5b9HL6lwFVf/s320/jackpot-slot-machine.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<b><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjM_Jd_wmD1dR5PauEi4mOkILft-3m_jrQ4s3rmgkpmZqU7ncygYLkrzDJXDyYrb-7CmIqk0koknAm6joIqlZNPiCGM3rB3I448QNVwAQOPjopE4Vw4HM5Wyi0qQf25VPezUGJW0dNq4PVJ/s1600/jackpot-slot-machine.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></b></div>
<br />
But what if you could cheat? For example, what if you had a different lever for each slot? That way you can only change the value of a single slot with each pull. This means that now for each time the lever is pulled, there's a chance of 1/256 that the outcome will match the desired value <u>for that slot</u>.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgng9PofNvWhnvHyBm3fReII_iQxezzvsu_hi7gXmEH37iiKrTdvQ5skZjDDUymh3CTeA7wmWiCm_9pdQ2OkHPQamp-YsoIk098oKRnhZsCYfwZGvYq1x-IFi_1FqKRd4c1SRb_UPesx_Ul/s1600/multi_jackpot-slot-machine.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="256" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgng9PofNvWhnvHyBm3fReII_iQxezzvsu_hi7gXmEH37iiKrTdvQ5skZjDDUymh3CTeA7wmWiCm_9pdQ2OkHPQamp-YsoIk098oKRnhZsCYfwZGvYq1x-IFi_1FqKRd4c1SRb_UPesx_Ul/s320/multi_jackpot-slot-machine.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGK5NCu84-PSLHcs1YGEJzejs39O97aP9QS120Dd5w2xOyWALlc1n58SyOpYrO3ZQh1AAC4n4i-IoaOxMB1rMl0EpEGEX6kiEaeF5UHeH6yrr7GDpFPbiG2JuoP7v2UJqcjTtcx1ymtYUy/s1600/multi_jackpot-slot-machine.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
Sounds like the game is much easier now, right? But how much easier? In probability theory this kind of distribution for a single "game" is called a <a href="https://en.wikipedia.org/wiki/Bernoulli_distribution">Bernoulli Distribution</a>, and is actually just a fancy way of saying that each experiment has a set probability of success, denoted <i>p</i>, and all other outcomes are marked all failures, and have a probability of <i>1-p</i> of occurring.<br />
<br />
Assuming we would like a 90% chance of success, it turns out that the in original version of the game we would require approximately 10^8 attempts (!), but if we cheat, instead, we would only require approximately 590 attempts per slot, which is several orders of magnitude less.<br />
<br />
So have you figured out how this all relates to our write primitive yet? Here it goes:<br />
<br />
First, we need to find an SCM call which returns a value from a writeable memory location within the TrustZone kernel's memory, to the caller.<br />
<br />
There are many such functions. One such candidate is the "tzbsp_fver_get_version" call. This function can be used by the "Normal World" in order to retrieve internal version numbers of different TrustZone components. It does so by receiving an integer denoting the component whose version should be retrieved, and an address to which the version code should be written. Then, the function simply goes over a static array of pairs containing the component ID, and the version code. When a component with the given ID is found, the version code is written to the output address.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJCF7PECdCQBkw9WWqc-cG-g9uRNR1Fr5eJ-H1FTVrBT6cfc5kpbybNeAnPZnmqEQD14Fd-uzxk2LLIzuvu6XSTOhwWyjAoAZ1DRAjWCpU_DsyVENiiLi6GqnRapXRAmbg0KW10wPjCUI_/s1600/Screenshot+from+2015-08-08+21%253A31%253A12.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img alt="" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJCF7PECdCQBkw9WWqc-cG-g9uRNR1Fr5eJ-H1FTVrBT6cfc5kpbybNeAnPZnmqEQD14Fd-uzxk2LLIzuvu6XSTOhwWyjAoAZ1DRAjWCpU_DsyVENiiLi6GqnRapXRAmbg0KW10wPjCUI_/s1600/Screenshot+from+2015-08-08+21%253A31%253A12.png" title="tzbsp_fver_get_version internal array" /></a></div>
<div style="text-align: center;">
<br /></div>
<div style="text-align: center;">
<span style="font-size: xx-small;">tzbsp_fver_get_version internal array</span></div>
<div style="text-align: center;">
<br /></div>
<div style="text-align: left;">
</div>
Now, using the "tzbsp_prng_getdata_syscall" function, we can start manipulating any version code's value, one byte at a time. In order to know the value of the byte that we've generated at each iteration, we can simply call the aforementioned SCM, while passing in the component ID matching the component whose version code we are modifying, and supplying a return address which points to a readable (that is, not in TrustZone) memory location.<br />
<br />
We can repeat these first two steps until we are satisfied with the generated byte, before moving on to generate the next byte. This means that after a few iterations, we can be certain that the value of a specific version code matches our wanted DWORD.<br />
<br />
<b></b><b>Copy </b><br />
<br />
Finally, we would like to write the generated value to a controlled location. Luckily, this step is pretty straight-forward. All we need to do is simply call the "tzbsp_fver_get_version" SCM call, but now we can simply supply the target address as the return address argument. This will cause the function to write our generated DWORD to a controlled location, thus completing our write gadget.<b> </b><br />
<br />
<b>Phew... What now?</b><br />
<br />
From here on, things get a little easier. First, although we have a write primitive, it is still quite cumbersome to use. Perhaps it would be a little easier if we were able to create a simpler gadget using the previous one.<br />
<br />
We can do this by creating our own SCM call, which is simply a write-what-where gadget. This may sound tricky, but it's actually pretty straight-forward.<br />
<br />
In the previous blog post, we mentioned that all SCM calls are called indirectly via a large array containing, among other things, pointers to each of the SCM calls (along with the number of arguments they are provided, their name, etc.).<br />
<br />
This means that we can use the write gadget we created previously in order to change the address of some SCM call which we deem to be "unimportant", to an address at which a write gadget already exists. Quickly going over the TrustZone kernel's code reveals that there are many such gadgets. Here's one example of such a gadget:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLNcRKwMm0HfXIeIoLXJG6dkQqpDZk5FGbOQ0NhJBH3MZfyC5Xhmex13_TQdos27cVoXVLllTajz7pMhwnbSv5i844if1Ckqwe5nOoOk8vv4nhU0oUPVoVqO58ShgmlmMC9knNHtQwFjQI/s1600/Screenshot+from+2015-08-08+21%253A42%253A02.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLNcRKwMm0HfXIeIoLXJG6dkQqpDZk5FGbOQ0NhJBH3MZfyC5Xhmex13_TQdos27cVoXVLllTajz7pMhwnbSv5i844if1Ckqwe5nOoOk8vv4nhU0oUPVoVqO58ShgmlmMC9knNHtQwFjQI/s1600/Screenshot+from+2015-08-08+21%253A42%253A02.png" /></a></div>
<br />
This piece of code will simply write the value in R0 to the address in R1, and return. Great.<br />
<br />
Finally, it might also be handy to be able to read any memory location which is within the TrustZone kernel's virtual address space. This can be achieved by creating a read gadget, using the exact same method described above, in place of another "unimportant" SCM call. This gadget is actually quite a bit rarer than the write gadget. However, one such gadget was found within the TrustZone kernel:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6ycOoCGdw5FSgkgdSGRnKLtvBHyzM6r6uKLeRs_b7Lzlj-E7u673ZDaYwfG6HUWt8ckRYNVRn-2hCPvaKOMqxLvp8m_jo36n-W97MAhLZDw6bfL_LkH7c61li3ccD8lma1B56WbXbHx_H/s1600/Screenshot+from+2015-08-08+21%253A47%253A51.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6ycOoCGdw5FSgkgdSGRnKLtvBHyzM6r6uKLeRs_b7Lzlj-E7u673ZDaYwfG6HUWt8ckRYNVRn-2hCPvaKOMqxLvp8m_jo36n-W97MAhLZDw6bfL_LkH7c61li3ccD8lma1B56WbXbHx_H/s1600/Screenshot+from+2015-08-08+21%253A47%253A51.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiusYZW9L-xqTl0m6micwe3U-kue268d4pzYIhyphenhyphenooxkFiY5hjEBlZQHgRdNy1QC1Ya-s1JiSTwuF7YI1XcBwdwG6_JYUsimNUCQt5QKDhmsb6j5n_0K2ROjMaZxLTQVSRmVZnZbIBpqfQW7/s1600/Screenshot+from+2015-08-08+21%253A46%253A54.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
This gadget returns the value read from the address in R0, with the offset R1. Awesome.<br />
<br />
<b>Writing new code</b><br />
<br />
At this stage, we have full read-write access to the TrustZone kernel's memory. What we don't yet have, is the ability to execute arbitrary code within the TrustZone kernel. Of course, one might argue the we could find different gadgets within the kernel, and string those together to create any wanted effect. But this is quite tiring if done manually (we would need to find quite a few gadgets), and quite difficult to do automatically.<br />
<br />
There are a few possible way to tackle this problem.<br />
<br />
One possible angle of approach might be to write a piece of code in the "Normal World", and branch to it from the "Secure World". This sounds like an easy enough approach, but is actually much easier said than done.<br />
<br />
As mentioned in the <a href="http://bits-please.blogspot.com/2015/03/getting-arbitrary-code-execution-in.html">first blog post</a>, when the processor in operating in secure mode, meaning the <b>NS </b>(<b>N</b>on-<b>S</b>ecure) bit in the <b>SCR </b>(<b>S</b>ecure <b>C</b>onfiguration <b>R</b>egister) is turned off, it <a href="http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0301h/Chdfjdgi.html">can only execute pages which are marked as "secure"</a> in the translation table used by the MMU (that is, the NS<b> </b>bit is off).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjua5QgoOCRv973j9ndA7WO3Ysrdffy-p5PqTmSGYpMYRku4zgWpTDm45CbQFosUkJn3vDoxJ15eVBKoq_G_0X4ghvyduSyMdZL118N0TzbH1TUEmg4u973GI8zbzU6lb1_MpVHGr_nhsZy/s1600/Screenshot+from+2015-08-09+00%253A42%253A36.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="538" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjua5QgoOCRv973j9ndA7WO3Ysrdffy-p5PqTmSGYpMYRku4zgWpTDm45CbQFosUkJn3vDoxJ15eVBKoq_G_0X4ghvyduSyMdZL118N0TzbH1TUEmg4u973GI8zbzU6lb1_MpVHGr_nhsZy/s640/Screenshot+from+2015-08-09+00%253A42%253A36.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9rA-2vClkGaGQxq9KDVFR6QQbB6p4jPvO85FgHAIddTKt3jmrCL7__uyh5njO4RAOuIgEF1QdfrVej01sOi2QTYXaChpdTnpX0_fkkBFwav6BwDblAxYhD3hv9wuuSKH3eAupJ-auFjrk/s1600/Screenshot+from+2015-08-09+00%253A16%253A58.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<br />
This means that in order to execute our code chunk residing in the "Normal World" we would first have to modify the TrustZone kernel's translation table in order to map the address in which we've written our piece of code as secure.<br />
<br />
While all this is possible, it is a little tiresome.<br />
<br />
A different approach might be to write new code within the TrustZone kernel's code segments, or overwrite existing code. This also has the advantage of allowing us to modify existing behaviour in the kernel, which can also come in handy later on.<br />
<br />
However, upon first glance this doesn't sound easier to accomplish than the previous approach. After all, the TrustZone kernel's code segments are mapped as read-only, and are certainly not writeable.<br />
<br />
However, this is only a minor setback! This can actually be solved without modifying the translation table after all, by using a convenient feature of the ARM MMU called "domains".<br />
<br />
In the ARM translation table, each entry has a field which lists its permissions, as well as a field denoting the "domain" to which the translation belongs. There are 16 domains, and each translation belongs to a single one of them.<br />
<br />
Within the ARM MMU, there is a register called the <b>DACR </b>(<b>D</b>omain <b>A</b>ccess <b>C</b>ontrol <b>R</b>egister). This 32-bit register has 16 pairs of bits, one pair for each domain, which are used to specify whether faults for read access, write access, both, or neither, should be generated for translations of the given domain.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjg1-0p4l3sPTunD3qpkUPVx5R7ReJAzl86MxOwkCgwY0tzy4rL4va7z8KBA9SlKyQbsOXS4iI1Q8oRlRimk6ZvCzbkM4crGYbcfnP7_vY2UoQQMDAd1-quigQmqPhao_enc0gwl2oQk3oT/s1600/Screenshot+from+2015-08-08+22%253A11%253A54.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="104" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjg1-0p4l3sPTunD3qpkUPVx5R7ReJAzl86MxOwkCgwY0tzy4rL4va7z8KBA9SlKyQbsOXS4iI1Q8oRlRimk6ZvCzbkM4crGYbcfnP7_vY2UoQQMDAd1-quigQmqPhao_enc0gwl2oQk3oT/s640/Screenshot+from+2015-08-08+22%253A11%253A54.png" width="640" /></a></div>
<br />
Whenever the processor attempts to access a given memory address, the MMU first checks if the access is possible using the access permissions of the given translation for that address. If the access is allowed, no fault is generated.<br />
<br />
Otherwise, the MMU checks if the bits corresponding to the given domain in the DACR are set. If so, the fault is suppressed and the access is allowed.<br />
<br />
This means that simply setting the DACR's value to 0xFFFFFFFF will actually cause the MMU to enable access to any mapped memory address, for both read and write access, without generating a fault (and more importantly, without having to modify the translation table).<br />
<br />
But how can we set the DACR? Apparently, during the TrustZone kernel's initialization, it also explicitly sets the DACRs value to a predetermined value (0x55555555), like so:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSKxyVYeSJwda4X7AhFvu8BfJrGFSUWApwNq-k5p8mxDB8cxxgRpblHiFyMvI83-vw8y7FeBEHI5bZoa68IY-uWpvSZ9iOmsLR9btiFk63E1jqlVk4eYhxNy0yuHhKOz2iX1CZB-aog0Tc/s1600/Screenshot+from+2015-08-08+22%253A17%253A27.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSKxyVYeSJwda4X7AhFvu8BfJrGFSUWApwNq-k5p8mxDB8cxxgRpblHiFyMvI83-vw8y7FeBEHI5bZoa68IY-uWpvSZ9iOmsLR9btiFk63E1jqlVk4eYhxNy0yuHhKOz2iX1CZB-aog0Tc/s1600/Screenshot+from+2015-08-08+22%253A17%253A27.png" /></a></div>
<br />
However, we can simply branch to the next opcode in the initialization function, while supplying our own value in R0, thus causing the DACR to be set to our controlled value. <br />
<br />
Now that the DACR is set, the path is all clear - we can simply write or overwrite code within the TrustZone kernel.<br />
<br />
In order to make things a little easier (and less disruptive), it's probably better to write code at a location which is unused by the TrustZone kernel. One such candidate is a "code cave".<br />
<br />
Code caves are simply areas (typically at the end allocated memory regions) which are unused (i.e., do not contain code), but are nonetheless mapped and valid. They are usually caused by the fact that memory mappings have a granularity, and therefore quite frequently there is internal fragmentation at the end of a mapped segment.<br />
<br />
Within the TrustZone kernel there are several such code caves, which enable us to write small pieces of code within them and execute them, with minimal hassle.<br />
<br />
<b>Putting it all together</b><br />
<br />
So this exploit was a little complex. Here's a run-down of all the stages we had to complete:<br />
<ul>
<li>Disable the memory validation functions using the zero write primitive</li>
<li>Craft a wanted DWORD at a controlled location using the TrustZone PRNG</li>
<li>Verify the crafted DWORD by reading the corresponding version code</li>
<li>Write the crafted version code to the location of a function pointer to an existing SCM call (by doing so creating a fast write gadget)</li>
<li>Use the fast write gadget to create a read gadget</li>
<li>Use the fast write gadget to write a function pointer to a gadget which enables us to modify the DACR</li>
<li>Modify the DACR to be fully enabled (0xFFFFFFFF)</li>
<li>Write code to a code cave within the TrustZone kernel</li>
<li>Execute! :)</li>
</ul>
<br />
<b>The Code</b><br />
<br />
I've written an exploit for this vulnerability, including all the needed symbols for the Nexus 5 (with the fingerprint stated beforehand).<br />
<br />
First of all, in order to enable the exploit to send the needed crafted SCM calls to the TrustZone kernel, I've created a patched version of the msm-hammerhead kernel which adds such functionality and exposes it to user-space Android.<br />
<br />
I've chosen to do this by adding some new IOCTLs to an existing driver, QSEECOM (mentioned in the <a href="http://bits-please.blogspot.com/2015/03/getting-arbitrary-code-execution-in.html">first blog post</a>), which is a Qualcomm driver used to interface with the TrustZone kernel. These IOCTLs enable the caller to send a "raw" SCM call (either regular, or atomic) to the TrustZone kernel, containing any arbitrary data.<br />
<br />
<a href="https://github.com/laginimaineb/qseecom_modifications">You can find the needed kernel modifications here</a>.<br />
<br />
For those of you using a Nexus 5 device, I personally recommend following Marcin Jabrzyk's great tutorial - <a href="http://marcin.jabrzyk.eu/posts/2014/05/building-and-booting-nexus-5-kernel">here</a> (it's a full tutorial describing how to compile and boot a custom kernel without flashing it to the device).<br />
<br />
After booting the device with a modified kernel, you'll need a user-space application which can use the newly added IOCTLs in order to send SCMs to the kernel.<br />
<br />
<a href="https://github.com/laginimaineb/fuzz_zone">I've written such an application which you can get it here</a>.<br />
<br />
Finally, the exploit itself is written in python. It uses the user-space application to send SCM calls via the custom kernel directly to the TrustZone kernel, and allows execution of any arbitrary code within the kernel.<br />
<br />
<a href="https://github.com/laginimaineb/MSM8974_exploit">You can find the full exploit's code here.</a> <br />
<br />
<b>Using the exploit</b><br />
<br />
Using the exploit is pretty straight forward. Here's what you have to do:<b> </b><br />
<ul>
<li>Boot the device using the modified kernel (<a href="http://marcin.jabrzyk.eu/posts/2014/05/building-and-booting-nexus-5-kernel">see Marcin's tutorial</a>)</li>
<li>Compile the FuzzZone binary and place it under <i>/data/local/tmp/</i></li>
<li>Write any ARM code within the <i>shellcode.S</i> file</li>
<li>Execute the <i>build_shellcode.sh</i> script in order to create a shellcode binary</li>
<li>Execute <i>exploit.py</i> to run your code within the TrustZone kernel </li>
</ul>
<br />
<b>Affected Devices</b><br />
<br />
At the time of disclosure, this vulnerability affected all devices with the MSM8974 SoC. I created a script to statically check the ROMs of many such devices before reporting the vulnerability, and found that the following devices were vulnerable:<br />
<br />
<i><u>Note:</u> This vulnerability has since been fixed by Qualcomm, and therefore should not affect updated devices currently. Also, please note that the following is <u>not</u> an exhaustive list, by any measure. It's simply the result of my static analysis at the time.</i><br />
<br />
<pre wrap=""> -Samsung Galaxy S5
-Samsung Galaxy S5
-Samsung Galaxy Note III
-Samsung Galaxy S4
-Samsung Galaxy Tab Pro 10.1
-Samsung Galaxy Note Pro 12.2
-HTC One
-LG G3
-LG G2
-LG G Flex
-Sony Xperia Z3 Compact
-Sony Xperia Z2
-Sony Xperia Z Ultra
-Samsung Galaxy S5 Active
-Samsung Galaxy S5 TD-LTE
-Samsung Galaxy S5 Sport
-HTC One (E8)
-Oneplus One
-Acer Liquid S2
-Asus PadFone Infinity
-Gionee ELIFE E7
-Sony Xperia Z1 Compact
-Sony Xperia Z1s
-ZTE Nubia Z5s
-Sharp Aquos Xx 302SH
-Sharp Aquos Xx mini 303SH
-LG G Pro 2
-Samsung Galaxy J
-Samsung Galaxy Note 10.1 2014 Edition (LTE variant)
-Samsung Galaxy Note 3 (LTE variant)
-Pantech Vega Secret UP
-Pantech Vega Secret Note
-Pantech Vega LTE-A
-LG Optimus Vu 3
-Lenovo Vibe Z LTE
-Samsung Galaxy Tab Pro 8.4
-Samsung Galaxy Round
-ZTE Grand S II LTE
-Samsung Galaxy Tab S 8.4 LTE
-Samsung Galaxy Tab S 10.5 LTE
-Samsung Galaxy Tab Pro 10.1 LTE
-Oppo Find 7 Qing Zhuang Ban
-Vivo Xshoot Elite
-IUNI U3
-Hisense X1
-Hisense X9T Pantech Vega Iron 2 (A910)
-Vivo Xplay 3S
-ZTE Nubia Z5S LTE
-Sony Xperia Z2 Tablet (LTE variant)
-Oppo Find 7a International Edition
-Sharp Aquos Xx304SH
-Sony Xperia ZL2 SOL25
-Sony Xperia Z2a
-Coolpad 8971
-Sharp Aquos Zeta SH-04F
-Asus PadFone S
-Lenovo K920 TD-LTE (China Mobile version)
-Gionee ELIFE E7L
-Oppo Find 7
-ZTE Nubia X6 TD-LTE 128 GB
-Vivo Xshot Ultimate
-LG Isai FL
-ZTE Nubia Z7
-ZTE Nubia Z7 Max
-Xiaomi Mi 4
-InFocus M810</pre>
<br />
<b>Timeline</b><br />
<ul>
<li>19.09.14 - Vulnerability disclosed</li>
<li>19.09.14 - Initial response from QC</li>
<li>22.09.14 - Issue confirmed by QC</li>
<li>01.10.14 - QC issues notice to customers</li>
<li>16.10.14 - QC issues notice to carriers, request for 14 days of embargo</li>
<li>30.10.14 - Embargo expires </li>
</ul>
I'd like to also point out that after reporting this issue to Qualcomm, I was informed that it has already been
internally identified by them prior to my disclosure. However, these
kinds of issues require quite a long period of time in order to push a
fix, and therefore at the time of my research, the fix had not yet been
deployed (at least, not to the best of my knowledge).<br />
<br />
<b>Last Words</b><br />
<br />
I'd really like to hear some feedback from you, so please leave a comment below! Feel free to ask about anything. laginimainebhttp://www.blogger.com/profile/00867710965331025912noreply@blogger.com556tag:blogger.com,1999:blog-2029700426505953971.post-86986201951966920202015-08-04T22:49:00.000+03:002015-08-11T17:10:39.863+03:00Exploring Qualcomm's TrustZone implementationIn this blog post, we'll be exploring Qualcomm's TrustZone implementation, as present on Snapdragon SoCs. If you haven't already, you might want to read the <a href="http://bits-please.blogspot.com/2015/03/getting-arbitrary-code-execution-in.html">previous blog post</a>, in which I go into some detail about TrustZone in general.<br />
<br />
<b>Where do we start?</b><br />
<br />
First of all, since Qualcomm's TrustZone implementation is closed-source, and as far as I could tell, there are no public documents detailing its architecture or design, we will probably need to reverse-engineer the binary containing the TrustZone code, and analyse it.<br />
<br />
<a name='more'></a><br />
<b>Acquiring the TrustZone image</b><br />
<br />
We can attempt to extract the image from two different locations; either from the device itself, or from a factory image of the device.<br />
<br />
My personal Nexus 5 device was already rooted, so extracting the image from the device should be pretty straight forward. Since the image is stored on the eMMC chip, and the blocks and partitions of the eMMC chip are available under "/dev/block/platform/msm_sdcc.1", I could simply copy the relevant partition to my desktop (using "dd").<br />
<br />
Moreover, the partitions have meaningfully named links to them under "/dev/block/platform/msm_sdcc.1/by-name":<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjAYf0atqQF_3VPlQEzdbfgke3FrG_8bTWkqYgq_ydQjcfRxnPo6eHbW_sTvMcdsF0PHyB4gGA4Db41GcZYFntYbvVuK5dR3XXSK1DWeDrXLeJr8vLgZE56pyVC8NP3PhqNTDXVgTTTuoyV/s1600/Screenshot+from+2015-03-28+15:31:15.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="478" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjAYf0atqQF_3VPlQEzdbfgke3FrG_8bTWkqYgq_ydQjcfRxnPo6eHbW_sTvMcdsF0PHyB4gGA4Db41GcZYFntYbvVuK5dR3XXSK1DWeDrXLeJr8vLgZE56pyVC8NP3PhqNTDXVgTTTuoyV/s1600/Screenshot+from+2015-03-28+15:31:15.png" width="640" /></a></div>
<br />
As you can see, there are two partitions here, one named "tz" (short for <b>T</b>rust<b>Z</b>one), and one named "tzb", which serves as a backup image to the "tz" image, and is identical to it.<br />
<br />
However, having extracted the image this way, I was still rather unsatisfied, for two reasons:<br />
<ul>
<li>Although the TrustZone image is stored on the eMMC chip, it could easily be made inaccessible to the "Normal World" (by requiring the AxPROT bit on the system bus to be set), or several parts of it could be missing.</li>
<li>Pulling the entire partition's data doesn't reveal information about the real (logical) boundary of the image, so it will require some extra work to determine where the image actually ends. (Actually, since the "tz" image is an ELF binary, its size is contained within the ELF header, but that's just a fluke on our part).</li>
</ul>
So, having extracted one image from the device, let's take a look at a factory image.<br />
<br />
The Nexus 5's factory images are all available to <a href="https://developers.google.com/android/nexus/images#hammerhead">download from Google</a>. The factory image contains a ZIP with all the default images, and additionally contains the bootloader image.<br />
<br />
After downloading the factory image and grepping for strings related to TrustZone, it quickly became apparent that the bootloader image contains the wanted code.<br />
<br />
However, there was still a minor problem to solve here - the bootloader image was in an unknown format (although maybe some Google-fu could reveal the answers needed). Regardless, opening the file with a hex-editor and guessing at its structure revealed that the format is actually quite simple: <br />
<br />
<div style="text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSmrvJrCyZUVTmlxuZQbmA2amLCJJOvgyPJIv7xspGUwLpNaoW0AYexD5w_z30OL_ygJJ0CBmk-KKAmUn4qwrtueptfQD4aCWtQ0JEqcaBuyjKeC4Z_gT5c3dmc0HHmh4jWV1qjQ-Gp6Vc/s1600/Screenshot+from+2015-03-28+16:32:15.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSmrvJrCyZUVTmlxuZQbmA2amLCJJOvgyPJIv7xspGUwLpNaoW0AYexD5w_z30OL_ygJJ0CBmk-KKAmUn4qwrtueptfQD4aCWtQ0JEqcaBuyjKeC4Z_gT5c3dmc0HHmh4jWV1qjQ-Gp6Vc/s1600/Screenshot+from+2015-03-28+16:32:15.png" width="514" /></a></div>
<br />
The bootloader file has the following structure:<br />
<ul>
<li>Magic value ("BOOTLDR!") - 8 bytes</li>
<li>The number of images - 4 bytes</li>
<li>The offset from the beginning of the file to the beginning of the image's data - 4 bytes</li>
<li>The total size of the data contained in the images - 4 bytes</li>
<li>An array with a number of entries matching the "number of images" field, above. Each entry in the array has two fields:</li>
<ul>
<li>The image name - 64 bytes (zero padded)</li>
<li>The image length - 4 bytes</li>
</ul>
</ul>
As you can see in the image above, the bootloader image contains an image called "tz", which is the image we're after. In order to unpack this file, I've written a small python script (<a href="https://github.com/laginimaineb/unpack_bootloader_image">available here</a>) which receives a bootloader image and unpacks all of the files contained within it.<br />
<br />
After extracting the image, and comparing it to the one extracted previously from the device, I verified that they were indeed identical. So I guess this means we can now move on to examine the TrustZone image.<br />
<br />
<b>Fixing up the TrustZone image</b><br />
<br />
First of all, examining the file reveals that it is in fact an ELF file, which is pretty good news! This means that the memory segments and their mapped addresses should be available to us.<br />
<br />
After opening the file with IDA Pro and letting the auto-analysis to run for a while, I wanted to start reversing the file. However, surprisingly, there seemed to be a lot of branches to unmapped addresses (or rather, addresses that weren't contained within the "tz" binary).<br />
<br />
After taking a closer look, it seemed as though all the absolute branches that pointed to invalid addresses were within the first code segment of the file, and they were pointing into high addresses that weren't mapped. Also, there were no absolute branches to the address of that first code segment.<br />
<br />
This seemed a little fishy... So how about we take a look at the ELF file's structure? Executing readelf reveals the following:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3OF8-2kGCtyzQlYtuoUMVem4X43jzwNkM0EU_dBtQp_ir2914OLJcbLClIONzy7yL8c_g-2NeC-GNIGmO2Lbowuwr6F49mJpiaAf0IDi1hW32x4e64Q5-PkJ7TXU7_JdNUYhGWbloX5g4/s1600/Screenshot+from+2015-03-28+18:44:47.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3OF8-2kGCtyzQlYtuoUMVem4X43jzwNkM0EU_dBtQp_ir2914OLJcbLClIONzy7yL8c_g-2NeC-GNIGmO2Lbowuwr6F49mJpiaAf0IDi1hW32x4e64Q5-PkJ7TXU7_JdNUYhGWbloX5g4/s1600/Screenshot+from+2015-03-28+18:44:47.png" /></a></div>
<br />
There's a NULL segment mapped to a higher address, which actually corresponds with the address range to which the invalid absolute branches were pointing! The guys over at Qualcomm are sneaky pandas :)<br />
<br />
Anyway, I made a rather safe guess, which is that the first code segment is in fact mapped to the wrong address, and should actually be mapped to the higher address - 0xFE840000. So naturally, I wanted to rebase the segment using IDA's rebase feature, but lo and behold! This causes IDA to crash spectacularly:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkGS9_6L0S3gXQ4z_htozQsBRmwr5mp60qsMey2aOWmRGNXUmd6O4ctGJrlQAznKJ24r-LffqxB0h7akkAeoRy3k0iqIZ-ORX2O3DSV5s-m7vWsnfPri0VSzb0dLF3BLN5J8z9cyAt9svM/s1600/Screenshot+from+2015-03-28+18:51:08.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="174" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkGS9_6L0S3gXQ4z_htozQsBRmwr5mp60qsMey2aOWmRGNXUmd6O4ctGJrlQAznKJ24r-LffqxB0h7akkAeoRy3k0iqIZ-ORX2O3DSV5s-m7vWsnfPri0VSzb0dLF3BLN5J8z9cyAt9svM/s1600/Screenshot+from+2015-03-28+18:51:08.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipTffuDKhuyjTK9GaWIAPxvC4Uj9tlRdSYzSIa9fFu-8EvtOQx84hXCnyxF2g_dvaxkqyQGiYObytZj527FkiVnYvHsSNXA3D5d4G8C2hbKZ9xI7h03e9xcn9HpAF2wxZHGUbp5gpGkyy4/s1600/Screenshot+from+2015-03-28+18:48:46.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
I'm actually not sure if this was intended as an anti-reversing feature by Qualcomm, or if the NULL segment is just a result of their internal build process, but this can be easily bypassed by fixing the ELF file manually. All that's required is to move the NULL segment to an unused address (since it is ignored by IDA anyway), and to move the first code segment from its wrong address (0xFC86000) to the correct address (0xFE840000), like so:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxD1UGXPUJU2tNCsqbeuRp1ps2fucru0RLg293G1goHmogxxbdNW1qq_gC1Vnv_NENKMTMtoPl8PJaaiVf9d8kJPlNFp6lO1C7u2yDRKvWxxb9HNYpoDCUfReuaqzfnR5xKvk9Sw_RNg6e/s1600/Screenshot+from+2015-03-28+19:04:35.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="571" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxD1UGXPUJU2tNCsqbeuRp1ps2fucru0RLg293G1goHmogxxbdNW1qq_gC1Vnv_NENKMTMtoPl8PJaaiVf9d8kJPlNFp6lO1C7u2yDRKvWxxb9HNYpoDCUfReuaqzfnR5xKvk9Sw_RNg6e/s1600/Screenshot+from+2015-03-28+19:04:35.png" width="640" /></a></div>
<br />
<br />
Now, after loading the image in IDA, all the absolute branches are valid! This means we can move on to analyse the image.<br />
<br />
<b>Analysing the TrustZone image</b> <br />
<br />
First, it should be noted that the TrustZone image is a rather large (285.5 KB) binary file, with quite a small amount of strings, and with no public documentation. Moreover, the TrustZone system is comprised of a full kernel with capabilities such as executing applications, and much more. So... it's not clear where we should start, as reversing the whole binary would probably take far too long.<br />
<br />
Since we would like to attack the TrustZone kernel from the application processor, the largest attack surface would probably be the secure monitor calls which enable the "Normal World" to interact with the "Secure World".<br />
<br />
It should be noted, of course, that there are other vectors with which we can interact with the TrustZone, such as shared memory or maybe even interrupt handling, but since these pose a much smaller attack-surface, it is probably better to start by analysing the SMC calls.<br />
<br />
So how do we find where the TrustZone kernel handles the SMC calls? First of all, let's recall that when executing an SMC call, similarly to the handling of SVC calls (that is, regular system calls in the "Normal World"), the "Secure World" must register the address of the vector to which the processor will jump when such an instruction is encountered.<br />
<br />
The "Secure World"'s equivalent is the <a href="http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0433b/CIHGAFJH.html">MVBAR</a> (<b>M</b>onitor <b>V</b>ector <b>B</b>ase <b>A</b>ddress <b>R</b>egister), which provides the address of the vector containing the handling functions for the different events which are handled by the processor in "Secure World".<br />
<br />
Accessing the MVBAR is done using the MRC/MCR opcodes, with the following operands:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgRC7_V_2Tvl2wZex_29MCiJMUCvZZAtMZtTRwTilZxWOCQHL5CTfPn4H9BmLLb-GI5ZNWF9kv9E4CsVyJR1XrOy2iB17oLgN4ycdJ9Ey2kFDV0wvaA-bYs4_iwkOldAJ14jQIFK7z6GZe/s1600/Screenshot+from+2015-03-28+20:30:46.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgRC7_V_2Tvl2wZex_29MCiJMUCvZZAtMZtTRwTilZxWOCQHL5CTfPn4H9BmLLb-GI5ZNWF9kv9E4CsVyJR1XrOy2iB17oLgN4ycdJ9Ey2kFDV0wvaA-bYs4_iwkOldAJ14jQIFK7z6GZe/s1600/Screenshot+from+2015-03-28+20:30:46.png" /></a></div>
<br />
So this means we can simply search for an MCR opcode with the following operands in the TrustZone image, and we should be able to find the "Monitor Vector". Indeed, searching for the opcode in IDA returns the following match:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGGJL7pRrgj38neHlinyC-MSe4OTE6NcVupxw-zGBm8N4_H65E02BY1SyX3bZZBY1gk6nz_J_fePYPNNTA1vNpB6ReVRfFZ6YEbYPrJLpw-68hd99yuEwUEoag7fLkkZ0q2QljiDbh1kBS/s1600/Screenshot+from+2015-03-28+20:33:49.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGGJL7pRrgj38neHlinyC-MSe4OTE6NcVupxw-zGBm8N4_H65E02BY1SyX3bZZBY1gk6nz_J_fePYPNNTA1vNpB6ReVRfFZ6YEbYPrJLpw-68hd99yuEwUEoag7fLkkZ0q2QljiDbh1kBS/s1600/Screenshot+from+2015-03-28+20:33:49.png" /></a></div>
<br />
As you can see, the address of the "start" symbol (which is, by the way, the only exported symbol), is loaded into the MVBAR.<br />
<br />
According to the <a href="http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0425/BABHHDII.html">ARM documentation</a>, the "Monitor Vector" has the following structure:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjg8dKGBLiDMBdefYtykurf8U_mxGo0qY1lA_b1EV_3oLfeB5_0nAQH0Z5FEku0UC-JiSBz6zm1NcKS7mUaT4IYJ5F3plB_DYO6WZ7_u_sV2IQMLdR_OOiKnlzLI86VHFmDWB6F4tA718YV/s1600/Screenshot+from+2015-03-28+20:41:03.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjg8dKGBLiDMBdefYtykurf8U_mxGo0qY1lA_b1EV_3oLfeB5_0nAQH0Z5FEku0UC-JiSBz6zm1NcKS7mUaT4IYJ5F3plB_DYO6WZ7_u_sV2IQMLdR_OOiKnlzLI86VHFmDWB6F4tA718YV/s1600/Screenshot+from+2015-03-28+20:41:03.png" /></a></div>
<br />
Which means that if we look at the "start" symbol mentioned earlier, we can assign the following names to the addresses in that table:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDGtkSQiCPr2P1Ym4hBH9fbH-zs9Bq_tBOdsU3uYwED8etlZxQH5j5tzWASj68QouEDE7Cc8TrrMQ1-kXbDpc6ByUgG_dPAZnkjSE9iDuR7MJ_Wnj77ewRj3i_5wwre8h_OqbDe23tAdQX/s1600/Screenshot+from+2015-03-28+20:44:34.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="284" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDGtkSQiCPr2P1Ym4hBH9fbH-zs9Bq_tBOdsU3uYwED8etlZxQH5j5tzWASj68QouEDE7Cc8TrrMQ1-kXbDpc6ByUgG_dPAZnkjSE9iDuR7MJ_Wnj77ewRj3i_5wwre8h_OqbDe23tAdQX/s1600/Screenshot+from+2015-03-28+20:44:34.png" width="640" /></a></div>
<br />
Now, we can analyse the SMC_VECTOR_HANDLER function. Actually, this function is responsible for quite a few tasks; first, it saves all the state registers and the return address in a predefined address (in the "Secure World"), then, it switches over the stack to a preallocated area (also in the "Secure World"). Finally, after performing the necessary preparations, it goes on to analyse the operation requested by the user and operate according to it.<br />
<br />
Since the code to issue SMCs is present in the Qualcomm's MSM branch of the Linux kernel, we can take a look at the format of commands which the "Normal World" can issue to the "Secure World".<br />
<br />
<b>SMC and SCM</b><br />
<br />
Confusingly, Qualcomm chose to name the channel through which the "Normal World" interacts with the "Secure World" via SMC opcodes - <b>SCM</b> (<b>S</b>ecure <b>C</b>hannel <b>M</b>anager).<br />
<br />
Anyway, as I've mentioned in the <a href="http://bits-please.blogspot.co.il/2015/03/getting-arbitrary-code-execution-in.html">previous blog post</a>, the "qseecom" driver is used to communicate with the "Secure World" using SCMs.<br />
<br />
The documentation provided by Qualcomm in the <a href="http://androidxref.com/kernel_3.4/xref/arch/arm/mach-msm/scm.c">relevant source files</a> is quite extensive, and is enough to get quite a good grip on the format of SCM commands.<br />
<br />
Putting it shortly, SCM commands fall into one of two categories:<br />
<br />
<i><u>Regular SCM Call</u></i> - These calls are used when there is information that needs to be passed from the "Normal World" to the "Secure World", which is needed in order to service the SCM call. The kernel populates the following structure:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoSuiIO1j8A1Lwaw4ht_s-BFmaOJlA70tX-UNgktKwpc2Ht1NcBS9-QmVl6Saqirq2ij7l4uLyI45TtklxETiiLXGY3kIUkdk9cYYQBu94L2nluXaW2qRYjdCNiMwrSK5IXKrLRYJuy1Ln/s1600/Screenshot+from+2015-03-28+21:34:57.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="609" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoSuiIO1j8A1Lwaw4ht_s-BFmaOJlA70tX-UNgktKwpc2Ht1NcBS9-QmVl6Saqirq2ij7l4uLyI45TtklxETiiLXGY3kIUkdk9cYYQBu94L2nluXaW2qRYjdCNiMwrSK5IXKrLRYJuy1Ln/s1600/Screenshot+from+2015-03-28+21:34:57.png" width="640" /></a></div>
<br />
And the TrustZone kernel, after servicing the SCM call, writes the response back to the "scm_response" structure:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgd1ihBq3ZwT3kvvEBFm1eUHSDazQmUVaBUE5_e3o4kJ_3YHNfuCJrqpAYfFVFJ7qt3clSNrGJ_B1Tuhy9hy65XxDCHAlKwM1Lu8I3uabyB6G73eYkYWgsouk7DAg_lRAV6V1Pnyp5uErEH/s1600/Screenshot+from+2015-03-28+21:37:50.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="211" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgd1ihBq3ZwT3kvvEBFm1eUHSDazQmUVaBUE5_e3o4kJ_3YHNfuCJrqpAYfFVFJ7qt3clSNrGJ_B1Tuhy9hy65XxDCHAlKwM1Lu8I3uabyB6G73eYkYWgsouk7DAg_lRAV6V1Pnyp5uErEH/s1600/Screenshot+from+2015-03-28+21:37:50.png" width="640" /></a></div>
<br />
In order to allocate and fill these structures, the kernel may call the wrapping function "scm_call", which receives pointers to kernel-space buffers containing the data to be sent, the location to which the data should be returned, and most importantly, the service identifier and command identifier.<br />
<br />
Each SCM call has a "category", which means which TrustZone kernel subsystem is responsible for handling that call. This is denoted by the service identifier. The command identifier is the code which specifies, within a given service, which command was requested.<br />
<br />
After the "scm_call" function allocates and populates the "scm_command" and "scm_response" buffers, it calls an internal "__scm_call" function which flushes all the caches (inner and outer caches), and calls the "smc" function.<br />
<br />
This last function actually executes the SMC opcode, transferring control to the TrustZone kernel, like so:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJAqIMHzFspP2aE8Rs7c5NbcRje8x1nGugdRJBffbXl_Cb2RbpakRqpGhtHEz8WrcS01VbpWLL5RunBNkfFmZjDHPNjiWYdF2rcbHPyfJBYb_MtgnKpfB14LehQgmITi7OS0-8ToXgNze6/s1600/Screenshot+from+2015-03-28+21:56:59.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="505" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJAqIMHzFspP2aE8Rs7c5NbcRje8x1nGugdRJBffbXl_Cb2RbpakRqpGhtHEz8WrcS01VbpWLL5RunBNkfFmZjDHPNjiWYdF2rcbHPyfJBYb_MtgnKpfB14LehQgmITi7OS0-8ToXgNze6/s1600/Screenshot+from+2015-03-28+21:56:59.png" width="640" /></a></div>
<br />
Note that R0 is set to 1, R1 is set to point to a local kernel stack address, which is used as a "context ID" for that call, and R2 is set to point to the physical address of the allocated "scm_command" structure.<br />
<br />
This "magic" value set in R0 indicates that this is a regular SCM call, using the "scm_command" structure. However, for certain commands where less data is required, it would be rather wasteful to allocate all these data structures for no reason. In order to address this issue, another form of SCM calls was introduced.<br />
<br />
<u>Atomic SCM Call</u> - For calls in which the number of arguments is quite low (up to four arguments), there exists an alternate way to request an SCM call.<br />
<br />
There are four wrapper functions, "scm_call_atomic_[1-4]", which correspond to the number of arguments requested. These functions can be called in order to directly issue an SMC for an SCM call with the given service and command IDs, and the given arguments.<br />
<br />
Here's the code for the "scm_call_atomic1" function:<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLmsfsAIGV_P6kALQCFyAbXhL79Fj02Hd2bLFExQuau-fT7byFqjeIgj_j3kkwSLxAG_1HYTkBfr7BQmeRCauEuLkyXYoFk-lIjQOZj5mEFaqHSfjJNAUnDwqr2K_aoj5w3zNZbRJCtt2c/s1600/Screenshot+from+2015-03-28+22:09:17.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="568" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLmsfsAIGV_P6kALQCFyAbXhL79Fj02Hd2bLFExQuau-fT7byFqjeIgj_j3kkwSLxAG_1HYTkBfr7BQmeRCauEuLkyXYoFk-lIjQOZj5mEFaqHSfjJNAUnDwqr2K_aoj5w3zNZbRJCtt2c/s1600/Screenshot+from+2015-03-28+22:09:17.png" width="640" /></a></div>
<br />
Where SCM_ATOMIC is defined as:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6Y5XLxmCtYnWhw5Ot4iNUQM785JyqjvaWbhx8S7qyDqYyGBGBmWn1xJtgCrQPsn_FQSt84bTuUQQKTEUOp4yBNba312SyRv-gyz8fyEKsYw0-WIBtbY5hgzZsJ9SNy6hMHHFKJR-XIybm/s1600/Screenshot+from+2015-03-28+22:13:27.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="70" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6Y5XLxmCtYnWhw5Ot4iNUQM785JyqjvaWbhx8S7qyDqYyGBGBmWn1xJtgCrQPsn_FQSt84bTuUQQKTEUOp4yBNba312SyRv-gyz8fyEKsYw0-WIBtbY5hgzZsJ9SNy6hMHHFKJR-XIybm/s1600/Screenshot+from+2015-03-28+22:13:27.png" width="640" /></a></div>
<br />
Note that both the service ID and the command ID are encoded into R0, along with the number of arguments in the call (in this case, 1). This is instead of the previous "magic" value of 1 used for regular SCM calls.<br />
<br />
This different value in R0 indicates to the TrustZone kernel that the following SCM call is an atomic call, which means that the arguments will be passed in using R2-R5 (and not using a structure pointed to by R2).<br />
<br />
<b>Analysing SCM calls</b><br />
<br />
Now that we understand how SCM calls work, and we've found the handling function in the TrustZone kernel which is used to handle these SCM calls, we can begin disassembling the SCM calls to try and find a vulnerability in one of them.<br />
<br />
I'll skip over most of the analysis of the SCM handling function, since most of it is boilerplate handling of user input, etc. However, After switching the stack over to the TrustZone area and saving the original registers with which the call was performed, the handling function goes on to process the service ID and the command ID in order to see which internal handling function should be called.<br />
<br />
In order to easily map between the service and command IDs and the relevant handling function, a static list is compiled into the TrustZone image's data segment, and is referenced by the SCM handling function. Here is a short snipped from the list:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEie7CWnJCm681fF7R8xXr29t8My6egfBEB8fGJyICG0ireHLcEk8r8DcN7bescjxkFg3EssHBUJl7n_f_3HNHrW69o3oV0scB_KrRQPoIBwkBpzb0NWvfp_X8BvMr1dgsJ4KCbFM7wYLPOc/s1600/Screenshot+from+2015-03-28+22:27:14.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="261" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEie7CWnJCm681fF7R8xXr29t8My6egfBEB8fGJyICG0ireHLcEk8r8DcN7bescjxkFg3EssHBUJl7n_f_3HNHrW69o3oV0scB_KrRQPoIBwkBpzb0NWvfp_X8BvMr1dgsJ4KCbFM7wYLPOc/s1600/Screenshot+from+2015-03-28+22:27:14.png" width="640" /></a></div>
<br />
<br />
<br />
As you can see, the list has the following structure:<br />
<ul>
<li>Pointer to the string containing the name of the SCM function</li>
<li>"Type" of call</li>
<li>Pointer to the handling function</li>
<li>Number of arguments</li>
<li>Size of each argument (one DWORD for each argument)</li>
<li>The Service ID and Command ID, concatenated into a single DWORD - For example, the "tz_blow_sw_fuse" function above, has the type 0x2002 which means it belongs to the service ID 0x20 and its command ID is 0x02. </li>
</ul>
Now all that's left is to start disassembling each of these functions, and hope to find an exploitable bug.<br />
<br />
<b>The Bug!</b><br />
<br />
So after pouring over all of the aforementioned SMC calls (all 69 of them), I finally arrived at the following function:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4R4mVSz_LwNrjeUSxITq4ldxmfY7ga_os9nvMXoLvrSLoQMizgGeeajKsHfI60J0SBEDDN62honpdeNK2SyHzHIa5IdFnXb2-Ff7rwDc0Zy5xfhIGEJvFO-GN-cxwVF_OV0rYQc1FL_bS/s1600/tzbsp_es_is_activated.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4R4mVSz_LwNrjeUSxITq4ldxmfY7ga_os9nvMXoLvrSLoQMizgGeeajKsHfI60J0SBEDDN62honpdeNK2SyHzHIa5IdFnXb2-Ff7rwDc0Zy5xfhIGEJvFO-GN-cxwVF_OV0rYQc1FL_bS/s1600/tzbsp_es_is_activated.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJTUr9nCx7M8SiCifEbeE3Tu6kYO9j2K3qBqDCQ9N9oLAOX6WpHh-rIHpqonauNnGKXOYBgcfJJjlgTW25_UfA7TrRMJ2gQjfxak3x-my8gp7LL0S0yJPnwluvMrauELqZL1diuTFXsCNK/s1600/tzbsp_es_is_activated.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyud3-Eq2DuL8267VT9vEbAqjMyKHqYUUdBCt2xU43m0P5q8Z8qeIzwZ2HJ15Pq8ENmmGK1SUIEewD8b6fqnJFogBs-_ji5sCQ5Ampx6ohOu5iQinApLmY1zr26BILfHnw4dI4o86O3y3_/s1600/tzbsp_es_is_activated.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
Normally, when an SCM command is called using the regular SCM call mechanism, R0 will contain the "result address" which points to the "scm_response" buffer which was allocated by the kernel, but which is also validated by the TrustZone kernel to make sure it is actually a physical address within an "allowed" range - that is, a physical address which corresponds to the Linux kernel's memory, and not, for example, a memory location within the TrustZone binary.<br />
<br />
This check is performed using an internal function which I will cover in more detail in the next blog post (so keep posted!).<br />
<br />
But what happens if we use an atomic SCM call to execute a function? In that case, the "result address" used is the first argument passed by the atomic call.<br />
<br />
Now - can you see the bug in the function above?<br />
<br />
As opposed to other SCM handling functions, this function fails to validate the value in R0, the "result address", so if we pass in:<br />
<ul>
<li>R1 as a non-zero value (in order to pass the first branch)</li>
<li>The fourth argument (which is passed in at var_1C above) is non-zero</li>
<li>R0 as any physical address, including an address within the range of the TrustZone address space</li>
</ul>
The function will reach the left-most branch in the function above, and write a zero DWORD at the address contained in R0.<br />
<br />
<br />
<b>Responsible Disclosure</b><br />
<br />
I'd like to point out that I've responsibly disclosed this vulnerability to Qualcomm eleven months ago, and the issue has been fixed by them (amazingly fast!). I'll share a detailed timeline and explanation in the next blog post, but I'd like to point out that the people at Qualcomm have been very responsive and a pleasure to work with.<br />
<br />
<b>What's next?</b><br />
<br />
In the next blog post I will share a detailed (and quite complex!) exploit for the vulnerability described above, which enables full code execution within the TrustZone kernel. I will also publish the full exploit code, so stay tuned!<br />
<br />
Also, since this is only my second blog post, I'm really looking for some (any) input, specifically:<br />
<ul>
<li>What should I write more (or less) about?</li>
<li>Blog design issues</li>
<li>Research ideas :)</li>
</ul>
<ul>
</ul>
laginimainebhttp://www.blogger.com/profile/00867710965331025912noreply@blogger.com318tag:blogger.com,1999:blog-2029700426505953971.post-58051124140867483042015-03-28T03:02:00.001+03:002015-08-11T17:11:02.939+03:00Getting arbitrary code execution in TrustZone's kernel from any context<h4>
</h4>
<h4>
<span style="font-size: x-small;"><i><span style="font-weight: normal;">(All the vulnerabilities have been responsibly disclosed and fixed. I
will post the CVE IDs and timelines in the following posts.)</span></i></span></h4>
<h4>
<i><span style="font-weight: normal;"> </span></i></h4>
<h4>
What's the Goal?</h4>
<br />
Transcendence. From Android, that is.<br />
<br />
This
is going to be a series of blog posts detailing a chain of
vulnerabilities that I've discovered which will enable us to escalate
our privileges from any user up to the highest privilege of all -
executing our code within TrustZone itself. <br />
<br />
Since I only have my personal Android device, a Nexus 5 powered by a Snapdragon 800 SoC, I will focus on the TrustZone platform present on my device - Qualcomm's TrustZone implementation.<br />
<br />
It should be noted that Qualcomm's TrustZone platform is present on all devices powered by Qualcomm SoCs, however, they also allow OEMs to make modifications and additions to this platform, which I will go into in more detail in later blog posts.<br />
<br />
Also, I believe objectively Qualcomm's TrustZone implementation is a good target since the Snapdragon SoCs are quite ubiquitous and can be found in a very wide range of devices (which isn't surprising, considering Qualcomm has a very <a href="http://www.nasdaq.com/article/qualcomm-leads-smartphone-chipset-market-analyst-blog-cm329297">large market share</a> in the smartphone chipset market).<br />
<br />
<a name='more'></a><br /><br />
<h4>
Android & Security </h4>
<br />
Over the years many security mechanisms have been added to Android, and existing ones have been improved.<br />
<br />
While the underlying security architecture hasn't changed, the defences have become quite formidable on modern devices, to the point where gaining high privileges can become quite a difficult task, many times requiring more than a single vulnerability.<br />
<br />
If you haven't already, I recommend that you read <a href="https://source.android.com/devices/tech/security/">Google's "Android Security Overview"</a>, which explains the security architecture and lists most of the security mechanisms which are currently in use.<br />
<br />
<span style="font-size: small;">(For the rest of these blog posts, I'm going to assume that you are at least somewhat familiar with Android's security architecture).</span><br />
<br />
<h4>
What is TrustZone?</h4>
<h4>
</h4>
<div style="text-align: center;">
<span style="font-size: x-small;">(First, an obligatory TrustZone schematic from ARM Ltd.)</span></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXSJkn39EUV3FlGM6-gmbarM03gSOsGPucn5Pvq25Cg9Ov9pWLj_0admc8tPt7qvPH3NAE7q4Z_QpwYNiAToyeBVgyOvukwngadJEP_3rSVnTD_0zquW8P9LUwLNk_prHt_COGWfN2H-fI/s1600/TrustZone.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXSJkn39EUV3FlGM6-gmbarM03gSOsGPucn5Pvq25Cg9Ov9pWLj_0admc8tPt7qvPH3NAE7q4Z_QpwYNiAToyeBVgyOvukwngadJEP_3rSVnTD_0zquW8P9LUwLNk_prHt_COGWfN2H-fI/s1600/TrustZone.jpg" /></a></div>
According to ARM Ltd., TrustZone is:<br />
<br />
"...a system-wide
approach to security for a wide array of client and server computing
platforms, including handsets, tablets, wearable devices and enterprise
systems. Applications enabled by the technology are extremely varied but
include payment protection technology, digital rights management, BYOD,
and a host of secured enterprise solutions."<br />
<br />
In short, this means TrustZone is a system which is meant to enable "secure execution" on a target device.<br />
<br />
In order to execute secure TrustZone code, a specific processor is designated. This processor can execute both non-secure code (in the "Normal World") and secure code (in the "Secure World"). All other processors are limited to the "Normal World" only.<br />
<br />
TrustZone is used for various purposes on Android devices, for example:<br />
<ul>
<li>Verifying kernel integrity (TIMA)</li>
<li>Using the Hardware Credential Storage (used by "keystore", "dm-verity")</li>
<li>Secure Element Emulation for Mobile Payments</li>
<li>Implementing and managing Secure Boot</li>
<li>DRM (e.g. PlayReady)</li>
<li>Accessing platform hardware features (e.g. hardware entropy)</li>
</ul>
In order to secure the whole system, and not just the application processor, specific bits on the system bus are set when entering "Secure World" and unset when returning to the "Normal World".<br />
<br />
Peripherals are able to access the state of these bits and therefore can deduce whether or not we are currently running in the secure world or not.<br />
<br />
<h4>
How does TrustZone's security model work? </h4>
<h4>
</h4>
ARM also has a short technical overview of how <a href="http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0333h/Chdfjdgi.html">TrustZone's Secure Model</a> works, which is worth a read.<br />
<br />
To achieve secure execution, the boundary between TrustZone and non-TrustZone code must be defined. This is achieved by defining two "worlds" - "Secure World" (TrustZone) and "Normal World" (in our case, Android).<br />
<br />
As you know, when in the "Normal World" there is a security boundary between code running in "User-mode" and code running in "Supervisor-mode" (Kernel-mode).<br />
<br />
The distinction between the different modes is managed by the <a href="http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0311d/ch02s04s02.html">Current Program Status Register</a> (CPSR):<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkpihPrwQS2Kwc9M7p6T0X0-JBQ1YdF3z9BdHUY0NDhuuMv1GWMbFwByPWPaijzFOdrYwo9by_LwGrpAdcLzubrpTfc1SvPp_W2VuS2_jzDxcer16eJDPlBS8T_xwtNHFu2Ev0MqgJmHRn/s1600/cpsr.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="75" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkpihPrwQS2Kwc9M7p6T0X0-JBQ1YdF3z9BdHUY0NDhuuMv1GWMbFwByPWPaijzFOdrYwo9by_LwGrpAdcLzubrpTfc1SvPp_W2VuS2_jzDxcer16eJDPlBS8T_xwtNHFu2Ev0MqgJmHRn/s1600/cpsr.png" width="400" /></a></div>
<br />
<br />
The five mode bits (marked by "M" in the image above), control the current execution mode. In the case of the Linux kernel, User Mode (b10000) is used for regular user code, and Supervisor Mode (b10011) is used for kernel code.<br />
<br />
And yet, there's something missing here - there's no bit to indicate what is the currently active "world". That is because there is a separate register used for that - the <a href="http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0333h/Babeeidg.html">Secure Configuration Register</a> (SCR):<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOtL_LbPFaz9tQulvME3Etcoj-HAnIHRuGDGc7eqDcmJ61QmOksv3N_37l_Qn1i6vUPn9OqEH1IXkuCb-jm90lAd402FFvNZ8cbf-Qd6ztK8TM_D_HkSJ22jnXfPbpbGeqMsJD9GLyztlI/s1600/scr.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="50" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOtL_LbPFaz9tQulvME3Etcoj-HAnIHRuGDGc7eqDcmJ61QmOksv3N_37l_Qn1i6vUPn9OqEH1IXkuCb-jm90lAd402FFvNZ8cbf-Qd6ztK8TM_D_HkSJ22jnXfPbpbGeqMsJD9GLyztlI/s1600/scr.png" width="400" /></a></div>
This register is a co-processor register, in CP15 c1, which means it can be accessed using the MRC/MCR opcodes.<br />
<br />
As with the CPSR register, the "Normal World" cannot modify the SCR register directly. It can, however, execute an SMC opcode, which is the equivalent of a SWI for regular supervisor mode calls. SMC is short for Supervisor Mode Call, and is the opcode which can be used to issue requests directly to the TrustZone kernel.<br />
<br />
Also, it should be noted that the SMC opcode can only be called from a supervisor context, which means that regular user code cannot use the SMC opcode.<br />
<br />
In order to actually call TrustZone related functionality, the supervisor code, in our case, the Linux kernel, must register some sort of service which can be used to call the relevant SMC calls when needed.<br />
<br />
In the case of Qualcomm, this is achieved by a device driver called "qseecom" - short for <b>Q</b>ualcomm <b>S</b>ecure <b>E</b>xecution <b>E</b>nvironment <b>C</b>ommunication. We'll talk more about this driver in the later blog posts, so hang tight. <br />
<br />
<b>Putting it all together</b><br />
<br />
So the road ahead is pretty long - in order to get to TrustZone code execution from a user-mode Android application with no permissions, we'll need the following privilege escalation vulnerabilities:<br />
<ul>
<li>Escalation from an Android application with no permissions to a privileged Android user.</li>
<li>Escalation from a privileged Android user to code execution in the Linux kernel.</li>
<li>Escalation from the Linux kernel to code execution in the TrustZone kernel.</li>
</ul>
So if this seems like it might interest you, keep reading!<br />
<br />
In the next blog post, I'll cover more details about Qualcomm's TrustZone implementation, and the vulnerability I discovered and exploited within its kernel. laginimainebhttp://www.blogger.com/profile/00867710965331025912noreply@blogger.com493tag:blogger.com,1999:blog-2029700426505953971.post-21723163912303573232014-10-25T00:55:00.000+03:002014-10-25T00:55:54.873+03:00__attribute__((constructor)) I just started this blog to talk about a few things I like (and I hope you will too!).<br />
<br />
I'll be focusing on Android, Mobile Security, and anything else security-related that I fiddle with in my spare time.<br />
<br />
As embargoes expire, I will share vulnerabilities that I have discovered, detailed exploits for those I find interesting, and a few security research tools that I've developed.<br />
<br />
Feel free to contact me by leaving a message on the blog or by emailing me at laginimaineb (at) gmail. If you have an idea for an <i>interesting</i> research topic that you think I should cover - I'm always happy to hear about it (especially if it's Android-related).<br />
<br />P.S - I might also share a few pictures of my dog. So there's that.laginimainebhttp://www.blogger.com/profile/00867710965331025912noreply@blogger.com449