Basics Of: Bypassing SSL Pinning on Android (with Frida)

May 30, 2023 (1y ago)

In the previous article I explained how to setup a rooted Android device to work whilst connected to the Burp proxy so that monitoring network traffic is possible. In this one we go one small step further than that in order to be able to also intercept network communications of some apps that use SSL pinning. So first of all, if you don't yet have a rooted Android device configured to connect through a proxy that you can monitor please go read the previous post in order to set that up. If you got that then you're ready, there are no other pre-requesites.

What is SSL Pinning?

First things first, what is SSL Pinning and why is it causing these types of errors to appear on your proxy?

certificate unknown burp error

If your proxy is setup the same way mine currently is then you might be thinking: I already installed the Burp ca certificate as a system level certificate, what do you mean it is unknown?

Well, basically even though the device does have a directory of certificates it generally considers trustworthy and will use to validate if a connection is secure, in order to prevent man-in-the-middle (mitm) attacks app developers often pin specific certificates (not necessary found in the mentioned directory) as the sole ones to be used for the validation. This causes the app to ignore traffic from sources not identifying themselves with the specific ones.

In the above screenshot, since the traffic is going through our proxy configuration and it is Burp who performs the SSL handshake and identifies itself with its own certificate, it does not matter that Burp is trusted by the system because Twitter is custom built to only trust its own.

There are a few different ways one can try to bypass ssl pinning, but I usually resort to a couple of known Frida scripts. So let's go ahead and install Frida.

Installing Frida

Or actually, before that, what is it? Well, here's the explanation provided by its website:

Dynamic instrumentation toolkit for developers, reverse-engineers, and security researchers.

More specifically for us Android reverse engineers, it allows us to write very simple Javascript code to hook to and modify the Java method and classes of the app we're dissecting.

Now let's talk about how to install it.

1. Install frida-tools

No surprises here, it's literally on the homepage. Run the following to install the client tools from pip:

pip install frida-tools

2. Download and run the Frida server on your Android device

It should not matter if you're using a physical device or emulator, the process is the same.

  1. Run frida --version to check which version got installed in the previous step (in my case it's 15.1.17)
  2. Download the relevant Android server binary from your version's Github release page

In my case I'll be downloading the frida-server-15.1.17-android-arm64 binary

But make sure to download the server binary that matches your frida version and Android device or emulator processor architecture.

  1. Unzip the .xz archive to get the actual binary
  2. Push the binary to your device and run it

I generally keep these types of things in the /data/local/tmp directory so generally this looks something like this for me:

# Push binary to /data/local/tmp
adb push frida-server-15.1.17-android-arm64 /data/local/tmp/

And then in the adb shell (adb shell):

cd /data/local/tmp

# Make the binary executable
chmod +x frida-server-15.1.17-android-arm64

# Run it in the background with '&' (so you can keep using this shell pane for other things)
./frida-server-15.1.17-android-arm64 &

That's it. Now let's get to the fun part.

Run an SSL Unpinning Script

In a future post we will cover the basics of Frida and hopefully eventually some intermediate stuff as well, but not understanding it does not have to stop you at this point. There are a lot of open source Frida scripts for all kinds of purposes out there that you're completely free to grab and use. And luckily for our SSL unpinning scenario this is definitely the case.

Most app developers don't venture into making their own SSL pinning implementations, they use third party libraries. And obviously a lot of these are commonly known. So as you might imagine, there are muliple ssl unppining scripts that attepts to bypass the implementations of these more common libraries that you can search for in google/github/whatever.

I will share with you two methods to use the one script (frida-multiple-unpinning) that works for most of the apps I reverse.

Run it directly from the Frida Codeshare

The Frida creators also created an online "codeshare" for users to publish their Frida scripts so that anyone can use them (if they have the url) without having to directly download the .js file and all that, and I suppose also to encourage collaboration and information sharing. But the site is missing a search functionality so it's not as useful as Github in that aspect.

Nonetheless, its "Most Popular Projects" section is quite useful and the most effective ssl unpinning script I have found is in the top three of this list. So let me explain the flags we're using to run it and then you can try it yourself:

  • -U: USB device (attach to usb device) must be used even if using an emulator
  • -f: force start the application
  • --no-pause: do not pause the app. By default Frida pauses the app process and you have to type %resume after it successfully attaches in order for the app to continue running, --no-pause just automatically resumes the app process after it successfully attaches to it

And the rest is self explanatory, so to finally attempt to bypass the ssl pinning implementation of multiple common libraries run the following:

frida --codeshare akabe1/frida-multiple-unpinning -U -f --no-pause

That's it! Like magic, no more "unknown certificate" errors and you're able to intercept (in our example) API communications:

Or download the script and run it locally

Obviously you're not constrained to running scripts uploaded to the codeshare, if you downloaded the previous script (or any other) and want to run it from the javascript file directly you just have to use the load (-l) option instead of the codeshare option, like this:

frida -l frida_multiple_unpinning.js -U -f --no-pause

Final Remarks

Obviously the above method will not work for every app, but there are other unpinning scripts that might work if that one doesn't and sometimes it might actually be necessary to write a custom unpinning script or take a different approach at the issue. So if you're interested in getting a peak as to how these scripts work it is highly encouraged for you to checkout the actual code of the above script and others you might have found.

In addition, do know that I am working on Frida specific articles for this blog were you'll be able to learn how to create Frida scripts for Android yourself. So if that sounds interesting keep an eye out for the next article. Easiest way might be to follow me on twitter.

Thanks for reading!