📜 ⬆️ ⬇️

SSL Pinning bypass in iOS application



Hi, my name is Andrey Batutin, I am a Senior iOS Developer in DataArt. In the previous article, we talked about how you can sniff the traffic of our mobile application using an HTTPS proxy. In this post we discuss how to bypass SSL pinning. Just in case, I recommend reading the first article, if you have not read it yet: it will be necessary to understand the text below.

Actually, in practice, SSL Pinning is used so that the described method of inspecting and modifying the traffic of a mobile application is not accessible to bad guys or a curious boss.
')

What is SSL Pinning


In the previous article, we installed a Charles Root Certificate on our mobile device, which allowed our Charles Proxy to receive, decrypt, show us traffic, encrypt it back and send it to Dropbox.

If, as a mobile application developer, I want my traffic to be inspected only by my server and no one else, even if this other one has installed its SSL certificate on the device, I can use SSL Pinning.

Its essence comes down to the fact that during the SSL handshake the client verifies the certificate received from the server.

This article discusses the easiest way to implement SSL Pinning using the allowed list of certificates sewn into the application (whitelisting).

Read more about SSL Pinning types here .

Implementing SSL Pinning at FoodSniffer


The complete project code is here . First we need to get two certificates in DER format for two hosts:


The second server stores JSON itself with a list of our purchases.

To get certificates in the right format, I used Mozila Firefox.

Open in dropbox.com browser.

Click on the lock symbol in the address bar.





Click More Information, select Security -> View Certificate.



Then select Details and find the final certificate in the Certificate Hierarchy.



Click Export and save in DER format.



Repeat the same procedure for uc9b17f7c7fce374f5e5efd0a422.dl.dropboxusercontent.com.

Note
For the Dropbox content server (* .dl.dropboxusercontent.com), a wildcard certificate is used. This means that the certificate that you extracted for the uc9b17f7c7fce374f5e5efd0a422 server will be suitable for any other * .dl.dropboxusercontent.com Dropbox servers.

As a result, I got two files with certificates:

dropboxcom.crt ,
dldropboxusercontentcom.crt ,

which I added to the project FoodSniffer iOS application.



Then I added an extention for the FoodListAPIConsumer-class, in which I check the certificate received from the server. To do this, I look for it in the list of allowed certificates, processing the Authentication Challenge delegate of the NSURLSessionDelegate protocol:

extension FoodListAPIConsumer { func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { guard let trust = challenge.protectionSpace.serverTrust else { completionHandler(.cancelAuthenticationChallenge, nil) return } let credential = URLCredential(trust: trust) if (validateTrustCertificateList(trust)) { completionHandler(.useCredential, credential) } else { completionHandler(.cancelAuthenticationChallenge, nil) } } func validateTrustCertificateList(_ trust:SecTrust) -> Bool{ for index in 0..<SecTrustGetCertificateCount(trust) { if let certificate = SecTrustGetCertificateAtIndex(trust, index){ let serverCertificateData = SecCertificateCopyData(certificate) as Data if ( certificates.contains(serverCertificateData) ){ return true } } } return false } } 

In the certificates array, I have stored Data representing my allowed certificates.

Now, when Charles Proxy is running, the application will break the connection with it due to the fact that the Charles-certificate is not on the list of allowed. The user will see the following error:



Hackers defeated!

But now there is one small problem - how can I, the developer, monitor the HTTPS traffic of my application?

Frida


One option is to disable SSL Pinning using Frida's dynamic code injection framework.

The idea is that during the development of an application, the validateTrustCertificateList method always returns true.

This, of course, can be achieved without dynamic code injection, for example, using the #if targetEnvironment (simulator) condition to disable SSL Pinning on the simulator, but this is too simple.

With the help of Frida, we will be able to write a script in JavaScript (deftly, right?), In which we will substitute the implementation of validateTrustCertificateList to one that always returns true.
And this script will be injected into the application already at the execution stage.

How Frida works on iOS, you can read here .

Installing Frida (taken from here ).



sudo pip install frida-tools

Frida script


The immediate script for replacing the validateTrustCertificateList function looks like this:

 // Are we debugging it? DEBUG = true; function main() { // 1 var ValidateTrustCertificateList_prt = Module.findExportByName(null, "_T016FoodSnifferFrida0A15ListAPIConsumerC024validateTrustCertificateD0SbSo03SecG0CF"); if (ValidateTrustCertificateList_prt == null) { console.log("[!] FoodSniffer!validateTrustCertificateList(...) not found!"); return; } // 2 var ValidateTrustCertificateList = new NativeFunction(ValidateTrustCertificateList_prt, "int", ["pointer"]); // 3 Interceptor.replace(ValidateTrustCertificateList_prt, new NativeCallback(function(trust) { if (DEBUG) console.log("[*] ValidateTrustCertificateList(...) hit!"); return 1; }, "int", ["pointer"])); console.log("[*] ValidateTrustCertificateList(...) hooked. SSL pinnig is disabled."); } // Run the script main(); 

  1. We find by the full name of the function a pointer to validateTrustCertificateList in the application binary.
  2. Wrap the pointer in a NativeFunction wrapper, indicating the type of the parameter and the output value of the function.
  3. We replace the implementation of the validateTrustCertificateList function such that it always returns 1 (that is, true).

The whole script is in {source_root} /fridascrpts/killCertPinnig.js .

One of the problems is how the full name of the function _T016FoodSnifferFrida0A15ListAPIConsumerC024validateTrustCertificateD0SbSo03SecG0CF was obtained

For this, I used the following technique.


And then used it in killCertPinnig.js .

Why such a “strange” name came to the function in the end, and what all these T016 and 0A15 mean, can be found here .

SSL Pinning Murder


Now let's finally launch FoodSniffer with Pinnig SSL disabled!

Run Charles Proxy.

Run the FoodSnifferFrida target in the Xcode project in the simulator. We should just see a white screen. The app is waiting for Frida to connect to it.


Run Frida to execute the killCertPinnig.js script:
frida -R -f re.frida.Gadget -l ./fridascrpts/killCertPinnig.js

Wait for the connection to the iOS application:



We continue the application using the command% resume:



Now we should see the list of food in the app:



And JSON in Charles Proxy:



Profit!

Conclusion


Frida is like Wireshark for binaries. It works on iOS, Android, Linux, Windows-based platforms. This framework allows you to track calls to methods and functions - both system and user. And also to substitute the values ​​of parameters, return values ​​and implementation of functions.

Bypassing SSL Pinning in the design process using Frida may seem a bit overkill . It attracts me because I don’t need to have specific logic in the application itself for debugging and developing the application. Such logic clutters the code and, if incorrectly implemented, can leak into the release version of the assembly (macros, hello to you!).

In addition, Frida is applicable for Android. Which gives me the opportunity to make life easier for all my team and ensure a smooth development process for the entire product line.
Frida positions itself as a black box process code injection tool. With it, it is possible, without changing the immediate code of the iOS application, to add to the runtime the logging of method calls, which can be indispensable when debugging complex and rare bugs.

Source: https://habr.com/ru/post/424485/


All Articles