SSL Pinning bypass

SSL pinning is a technique that helps to prevent MITM attacks by hardcoding the SSL/TLS certificate’s public key into the app. This means that when the app or device communicates with the server, it will compare the server’s SSL/TLS certificate’s public key with the one that is hardcoded into the app or device.

There are two techniques used in SSL Pinning:

  • Certificate SSL pinning: SSL certificate pinnning technique involves hard-coding the SSL certificate of a server into the client’s application. The client then compares the server’s certificate with the hard-coded certificate and only allows communication if they match.
  • Public key pinnign: Public key pinning involves hard-coding the public key of the server’s SSL certificate instead of the entire certificate. The client then checks that the server presents a certificate containing the same public key that was hard-coded into the client’s application.

Certificate pinning is easier to implement but may require frequent updates if the server’s certificate changes frequently. Public key pinning provides more flexibility in certificate management but requires more technical expertise to implement.

Why do we need to bypass SSL Pinning

Almost all modern apps implement ssl pinning and hence we need to bypass their pinned certificate and use the burp certificate to intercept the https requests. This is the only way that we will be able to view and manipulate the requests through our burp proxy

FЯIDA

FЯIDA is a Dynamic instrumentation toolkit for developers, reverse-engineers, and security researchers. We can take advantage of the tool to bypass ssl pinning, root detection, hook into both native and java functions and change their return types while the app is running and many more other cool stuff. Let’s now install frida into our machine

pip install Frida
pip install frida-tools

Next we need to download the server that will be running inside our device. Check the architecture of your device using the following command

adb -e shell getprop ro.product.cpu.abi
x86_64

download one of these according to the architecture of your device.

frida-server-12.4.7-android-x86.xz
frida-server-12.4.7-android-x86_64.xz

push frida server into device

adb push ~/Downloads/frida-server /data/local/tmp

give frida server permissions

adb shell chmod 777 /data/local/tmp/frida-server

Open burp suite and navigate to proxy settings. Select Import/ Export CA certificate -> Certificate in Der format -> Next . Choose a file to export then export the file. Push the certificate to the device

adb push cacert.der /data/local/tmp/cert-der.crt

Run frida server

adb shell /data/local/tmp/frida-server &

List all running processes in device

frida-ps -aU
 PID  Name                               Identifier                          
----  ---------------------------------  ------------------------------------
3874  AndroGoat - Insecure App (Kotlin)  owasp.sat.agoat                     
2465  Google Play Store                  com.android.vending                 
2862  Phone                              com.android.dialer                  
4948  Yahoo Mail                         com.yahoo.mobile.client.android.mail

We need to locate our target application. We will bypass yahoo mail so we get the Identifier of yahoo mail

com.yahoo.mobile.client.android.mail

Next we need to find a script to bypass ssl pinning from frida. You can check the script from here. There are many different scripts for different purposes.

/* 
   Android SSL Re-pinning frida script v0.2 030417-pier 

   $ adb push burpca-cert-der.crt /data/local/tmp/cert-der.crt
   $ frida -U -f it.app.mobile -l frida-android-repinning.js --no-pause

   https://techblog.mediaservice.net/2017/07/universal-android-ssl-pinning-bypass-with-frida/
   
   UPDATE 20191605: Fixed undeclared var. Thanks to @oleavr and @ehsanpc9999 !
*/

setTimeout(function(){
    Java.perform(function (){
    	console.log("");
	    console.log("[.] Cert Pinning Bypass/Re-Pinning");

	    var CertificateFactory = Java.use("java.security.cert.CertificateFactory");
	    var FileInputStream = Java.use("java.io.FileInputStream");
	    var BufferedInputStream = Java.use("java.io.BufferedInputStream");
	    var X509Certificate = Java.use("java.security.cert.X509Certificate");
	    var KeyStore = Java.use("java.security.KeyStore");
	    var TrustManagerFactory = Java.use("javax.net.ssl.TrustManagerFactory");
	    var SSLContext = Java.use("javax.net.ssl.SSLContext");

	    // Load CAs from an InputStream
	    console.log("[+] Loading our CA...")
	    var cf = CertificateFactory.getInstance("X.509");
	    
	    try {
	    	var fileInputStream = FileInputStream.$new("/data/local/tmp/cert-der.crt");
	    }
	    catch(err) {
	    	console.log("[o] " + err);
	    }
	    
	    var bufferedInputStream = BufferedInputStream.$new(fileInputStream);
	  	var ca = cf.generateCertificate(bufferedInputStream);
	    bufferedInputStream.close();

		var certInfo = Java.cast(ca, X509Certificate);
	    console.log("[o] Our CA Info: " + certInfo.getSubjectDN());

	    // Create a KeyStore containing our trusted CAs
	    console.log("[+] Creating a KeyStore for our CA...");
	    var keyStoreType = KeyStore.getDefaultType();
	    var keyStore = KeyStore.getInstance(keyStoreType);
	    keyStore.load(null, null);
	    keyStore.setCertificateEntry("ca", ca);
	    
	    // Create a TrustManager that trusts the CAs in our KeyStore
	    console.log("[+] Creating a TrustManager that trusts the CA in our KeyStore...");
	    var tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
	    var tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
	    tmf.init(keyStore);
	    console.log("[+] Our TrustManager is ready...");

	    console.log("[+] Hijacking SSLContext methods now...")
	    console.log("[-] Waiting for the app to invoke SSLContext.init()...")

	   	SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom").implementation = function(a,b,c) {
	   		console.log("[o] App invoked javax.net.ssl.SSLContext.init...");
	   		SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom").call(this, a, tmf.getTrustManagers(), c);
	   		console.log("[+] SSLContext initialized with our custom TrustManager!");
	   	}
    });
},0);

Next we need to hook the script to our target application. we use -l to specificy the path to our javascript payload, -f to specificy our target to hook and -U to specify that we are connecting using USB

frida -l ssl_pinning.js -f com.yahoo.mobile.client.android.mail -U
     ____
    / _  |   Frida 16.1.4 - A world-class dynamic instrumentation toolkit
   | (_| |
    > _  |   Commands:
   /_/ |_|       help      -> Displays the help system
   . . . .       object?   -> Display information about 'object'
   . . . .       exit/quit -> Exit
   . . . .
   . . . .   More info at https://frida.re/docs/home/
   . . . .
   . . . .   Connected to Nexus 5 (id=127.0.0.1:6555)
Spawned `com.yahoo.mobile.client.android.mail`. Resuming main thread!   
[Nexus 5::com.yahoo.mobile.client.android.mail ]->
[.] Cert Pinning Bypass/Re-Pinning
[+] Loading our CA...
[o] Our CA Info: CN=PortSwigger CA, OU=PortSwigger CA, O=PortSwigger, L=PortSwigger, ST=PortSwigger, C=PortSwigger
[+] Creating a KeyStore for our CA...
[+] Creating a TrustManager that trusts the CA in our KeyStore...
[+] Our TrustManager is ready...
[+] Hijacking SSLContext methods now...
[-] Waiting for the app to invoke SSLContext.init()...
[o] App invoked javax.net.ssl.SSLContext.init...
[+] SSLContext initialized with our custom TrustManager!
[o] App invoked javax.net.ssl.SSLContext.init...
[+] SSLContext initialized with our custom TrustManager!

We are now able to intercept network traffic from yahoo.

img-description

We can now use frida to bypass root detection the same way we did to bypass ssl pinning. To multiple bypass you just need to put multiple scripts in one file and run it.