14

SSL pinning bypass with frida-gadget (gadget-injector.py)

This article expands on the things you can achieve with the frida framework. It focuses on the usage of frida-gadget to bypass SSL Pinning controls on Android applications. Do note that this method requires tampering of the application, which means that it also requires re-signing the app. Also note that this approach should work on both rooted and non-rooted devices.



if code in yourInterests:
click(here)
elif video in yourInterests:
click(here)
else:
keepReading()


def keepReading():

 In a previous post, we saw how to use the frida framework to bypass SSL pinning on a an Android mobile application in order to intercept the traffic between the application and the server. This approach, however, requires you to push and run the frida-server into de device.

Here, we will see how to use a different approach: by reverse engineering  the application, injecting a library and then re-building it, we can embed the frida-gadget within the application, so that the next time the app is loaded, it will wait to establish a connection with the frida client on our workstation.


What is the frida-gadget?

Unlike the frida-server (which you have to run on the device), the frida-gadget is part of the frida framework and comes as a library (.so) that you can include and call in your code at your will.

This is very helpful if you have limited access/control over the device (for instance on a non-rooted device), you can reverse engineer and tamper the application to execute the frida functionality when the main activity is launched, which is what we are about to do.


Let’s get to it…

Using this approach requires a series of steps that you have you follow in a specific order. Overall, the process goes like this:

  1. Find device architecture.
  2. Get proxy’s certificate (& install it on device).
  3. Reverse application with apktool.
  4. Inject frida-gadget.
  5. Inject smali hook.
  6. Increase app version.
  7. Re-build application with apktool.
  8. Zipalign the resulting apk.
  9. Sign the apk with apksigner.
  10. Install the tampered app.
  11. Tweak the frida script.
  12. Run the frida client.
  13. Intercept and enjoy.


1. Finding device architecture.

First thing we need to do is to find what is the architecture of the device we are dealing with. In this case, we will be using a Genymotion virtual device running an Android 6.0 instance, we will need the Android Debug Bridge (ADB) to communicate between the device and workstation.

Let’s fire up the virtual emulator and use ADB to find out what architecture the device is, the next command starts ADB and lists the connected devices:

./adb devices
List of devices attached
192.168.56.101:5555 device

Then we use getprop to pull the information about the CPU’s architecture:

./adb shell getprop ro.product.cpu.abi
x86


2. Getting the proxy’s certificate.

Next thing we need to do is to get the certificate of the proxy we will be using for interception. In this case we will be using Burpsuite as proxy:

burp-running
Burpsuite proxy running on 192.168.0.9:8080.


Now, go to a browser and get the certificate easily by navigating to the proxy’s ip:port (192.168.0.9:8080 in this case). Here you need to go to the top right corner and click the CA Certificate link.

Getting the proxy’s CA certificate.


Once downloaded, install the certificate in the device (push it into the sdcard and go to Settings -> Security -> Install from sdcard). Also, keep the certificate at hand for future use:

Proxy’s CA certificate.


3. Reverse engineering the application.

Mobile applications for Android devices come in the apk format. Our target, in this case, is a very simple application that implements SSL pinning for the securitygrind.com domain and the pinning code looks like the following:

SSL Pinning code for securitygrind.apk


Once the application is compiled, an apk file will be generated. It is that file (securitygrind.apk)that we will be using from now on. We now proceed to reverse engineer the app with apktool:

apktool d securitygrind.apk -f

You shall see something like the below.

apktool-d-gadget-injector
apktool reverse engineering.


Also, a new folder with the name on the file and containing the reversed code will be created (in this case securitygrind).

ls-gadget-injector
Reverse engineering results.


4. Injecting frida-gadget.

In the first step, we found that the device’s architecture is x86, we need this information to find the right frida-gadget here. If we search for “frida-gadget” we will see a few on them, look for the one appropriate for your device’s arch.

frida-gadget for different archs.


In this case, we will be using frida-gadget-12.2.26-android-x86.so.xz, after downloading it, we need to decompress with unxz.

unxz frida-gadget-12.2.26-android-x86.so.xz

After this, you will be ready to inject the library. To do this, copy the frida-gadget library into the folder containing the reversed application,  put it into the lib folder (if it doesn’t exists, create it and create an arch folder within as well):

mkdir securitygrind/lib/
mkdir securitygrind/lib/x86

Now, copy the frida-gadget library into the newly created folders:

cp frida-gadget-12.2.26-android-x86.so /securitygrind/lib/x86/libfrida-gadget.so


5. Injecting smali hook.

Now, we need to find the application’s starting point, for this, we can inspect the AndroidManifest.xml file created by the reversing process. On this file, first look for the application tag, within it, look for the activity that has an intent-filter with the MAIN action and find it’s name:

AndroidManifest.xml

As we can see, in our case , the activitie’s name is LoginActivity, which means that, within the smali directory there must be a file with this name and the smali extension (LoginActivity.smali), we can find it with the next command:

find . -name 'LoginActivity.smali'

find-smali-file-gadget-injector
Finding the smali file.


We now open the smali file and look for the constructor, it is here where we need to add the smali hook that will invoke the frida-gadget library:

Constructor.


To inject the hook, first increase the locals by one and add the next two lines and save the file:

.locals 3
const-string v2, "frida-gadget"
invoke-static {v2}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V

Updated constructor.


6. Increase app version.

We also need to increase the application version, otherwise, you might get an error when installing, saying that that the app version is incorrect. To do this, we edit the apktool.yml file (you can also set the versionCode property on the AndroidManifest.xml file, within the manifest tag).

In this case, we will edit the apktool.yml, the version code (versionCode) is at the end of the file.

apktool.yml version code.

We increase the application version by 1:

Version code increased.


7. Re-building application.

Now that we have included the frida-gadget, tampered the application to load the library as one of the first thing the app does and increased the app version, we are ready to re-build the application.

To do this, we use apktool again:

apktool b securitygrind

apktool-b-gadget-injector
apktool re-build.


You will notice the dist folder has been created within the target folder and, in this folder, you will find the re-builded app.


8. zipaligning the app.

Aligning the re-builded apk optimizes the application and it’s space usage, to do so, we use zipalign, as shown below:

ziplign -p 4 securitygrind.apk securitygrind-aligned.apk

zipalign-frida-gadget
zipsligning the app.


9. Signing the app.

Once aligned, we proceed to sign the application, otherwise, we won’t be able to re-install the app. To do this, we first need a keystore, if you don’t have one already, you can create one with the following command:

keytool -genkey -alias keystore -keystore keyStore.pfx -storetype PKCS12 -keyalg RSA -validity 365 -keysize 2048

keytool-gadget-injector
Creating a keystore for signing.


With our keystore at hand, we now use apksigner to digitally sign the application with our newly created keystore.

apksigner sign --ks keyStore.pfx securitygrind/dist/securitygrind-aligned.apk

apksigner-gadget-injector
Digitally signing with apksigner.


10. Install the tampered app.

We now install the the tampered application, if the original application is installed, uninstall it and install the tampered one:

./adb install /opt/gadget-injector/securitygrind/dist/securitygrind-aligned-signed.apk

If you run the application at this point, you will see noting but a white screen, which means that the tampering worked fine and that frida is awaiting a connection:

App awaiting frida connection.


Close the application for now and, within the device,  go to Settings -> Wi-Fi -> WiredSSID (long press) -> Modify Network -> Proxy -> Manual: here you will configure the device to use Burpsuite as proxy:

Setting the proxy on the device.


11. Tweak the frida script.

Scripts in frida contains JavaScript code that translates into Java code for overriding a specific behavior in the target process or application. In this case, we will be overriding the init method of the SSL Context.

There is already a script for bypassing SSL pinning, which works by re-pinning the SSL Context with our proxy’s certificate, you can find it here. However, this script reads the certificate to be re-pinned from the internal storage of the device, this can be a problem if we are working with a non-rooted device.

Instead, we will tweak the frida script a little bit to hard code our proxy’s certificate on it (so that we do not have to read it from somewhere else).  But, the first thing we need to do is to have the proxy certificate in a format suitable for hardcoding into the frida script. A PEM format will work just fine, let’s use openssl to convert the proxy’s cert to PEM format:

openssl x509 -inform der -in cacert.der -out proxy-cert.pem

openssl-gadget-injector
Converting DER to PEM.


Then, going back into the frida script, we need to  add the String and ByteArrayInputStream object references:

var String = Java.use("java.lang.String");
var ByteArrayInputStream = Java.use("java.io.ByteArrayInputStream");

Now, we to load our PEM certificate into a String variable:

var cert = String.$new("-----BEGIN CERTIFICATE-----\n"
+ "MIIDyTCCArGgAwIBAgIEVA06ODANBgkqhkiG9w0BAQsFADCBijEUMBIGA1UEBhML\n"
...snipped...

After this, we create a byteArrayInputStream from the bytes of our previously define String (cert). Generated a certificate based on that input stream and finally close it.

var byteArrayInputStream = ByteArrayInputStream.$new(cert.getBytes());
var ca = cf.generateCertificate(byteArrayInputStream);
byteArrayInputStream.close();

Here is an example of the tweaked frida script.


12. Running frida client.

We are ready to bypass SSL pinning for this application, double check that Burpsuite is running and that the device is setup to use it as proxy. Using the modified frida script, run the next command (but run the application first, which will only show a white screen; awaiting for connection):

frida -U gadget -l frida-sslpinning.js

frida-gadget-injector
Running frida client.


You will notice that frida has ran correctly and that it is “waiting for the app to invoke SSLContext.init()…“, that is to say, that frida is waiting until the applications tries to establish an SSL connection with the server. In our case, that happens when the user hits the “Sign or Register” button.

After we enter the credentials on the application and hit the button, we can see frida injected our TrustManager (with our proxy’s certificate):

frida-trustmanager-gadget-injector
Frida overrides.


13. Intercept and enjoy.

And, if we check our proxy, we sill see that traffic was intercepted and decrypted, go to Burpsuite and check:

Traffic intercepted and decrypted.



Gadget Injector

I have created a small python script that automates part of the process described above. Find more on the GitHub repository here or by clicking the image below.

Gadegt Injector


Also, find the next video showcasing how the tool works:


Conclusion

Securing the communication between the mobile client and the server is of paramount importance. As a pentester you are often required to bypass security controls meant to protect the application. The process describe above does not mean that the application is vulnerable, it only means that the attacker have certain level of control over the device and/or the user was allows installations from unknown sources.