I. Introduction
This document is intended to provide detailed instructions for bypass certificate pinning via custom Root CA. It covers all the required topics for understanding this method. The proof of concept will help visualize and perform bypass certificate pinning, specially in modern applications now and in the future.
II. About Certificate Pinning
By default, an application trusts all the CAs shipped with Operating System (pre-installed CAs), it is around 140 trusted root CA included now [1].
Even though there is only a very small possibility for this to happen: if any of these CAs issue a fraudulent certificate [2],the application is at risk of being hacked by Man-in-the-Middle attack [3]. In addition, the users could be compromised with a rogue certificate installed on their device through social engineering.
To prevent that, the developers has 2 options:

  • Limit the set of certificates they accept by either limiting the set of CAs they trust.
  • Implement certificate pinning.
    By enhanced security, the cost is negligible and easy to deal with, most developers choose certificate pinning for their applications. The developers embed (or pinning) a list of trustful certificates to their application during development, then use them to compare against the server certificates during runtime. In case of mismatch, the TCP connection will be disrupted, and no further user data will be sent to that server.

III. How does it affect us (Pentester)?
In the phase of static analysis, it has no effect on this process at all. But in dynamic analysis, it can be a huge problem. You cannot observe or intercept the request/response between the application and the server when they communicate with each other, even worse is that the application will not work. If you can’t solve this problem, your pentesting process stops here.
But in fact, we can easily bypass it at this very moment, following this instruction:

  1. Using a device or an emulator with Android version 6.0 – API level 23 or below. I don’t have any physical device with this Android version so I’m using an emulator, let’s create it:

2. Configure emulator to work with Burpsuite’s proxy server:

3. Push the Burp’s certificate and install on emulator:

Settings > Install certificates > Install certificates > Choose the Burp’s certificate > Create a PIN > Install the certificate and all done.
Now in trusted credentials, in the USER tab, you will see the PortSwigger CA beside SYSTEM root CA:

4. Install your application you like to pentesting on this emulator, in this case I’m using the Airtable application, they have a bug bounty program on Hackerone [4], so this is legal:

5. Now the app is running and you can fully intercept all the requests and responses between the Airtable and its server.

This is the easiest way, but personally I think this method won’t work in the near future.

Root cause:
The first reason is this method only works on devices/emulators with Android version 6.0 – API level 23 or below. This is because of “Apps that target API Level 24 and above no longer trust user or admin-added CAs for secure connections, by default” [5].
I know that not too many physical devices are still running android 6. In the case of insufficient facilities, you can use an emulator. But lots of applications do not allow installation on virtualized devices, you can bypass it by some method, but it makes things more complicated.

Second reason and most importantly: in Android software development, the minSdkVersion is increasing every year. Android 6 – API level 23 was released in October 2015, it’s been over 4 years.

In the application we installed above (Airtable), it has the minSDK version 21. So the method above is still working. I tested some other popular apps, most of them have the minSdk version 21 as well. But just in the next few years, it will change.

IV. Bypass Certificate Pinning via custom Root CA
Let’s say you don’t have a device with Android 6 or lower, or that the app doesn’t allow installation on devices with API level 23 or below. How can you dynamic penetration testing this app?
This method will remove every obstacle in the way, or I can say in the future way.
By pentesting on a rooted device, you can manually install a Root CA on your phone. Then you can intercept all the requests and responses easily, like the way I just mentioned.
But you cannot use the existing Burp’s CA certificate, let me show you:

  1. First, I’ll install the Root Certificate Manager ROOT application and push the Burp’s CA certificate to my device: Redmi Note 6 Pro – running Android 8.1 – API level 27.
  2. Burp’s CA certificate installation successfully via Root Certificate Manager :
  1. Configure device to work with Burpsuite’s proxy server:

But when I try to access any HTTPs website in browser, I get the following error: NET::ERR_CERT_VALIDITY_TOO_LONG

Open the app and it’s totally blank:

The root cause is Burpsuite’s CA certificate validity too long and regenerating the certificate could not solve the problem.

According to the Postwigger pages [6] I can import my custom CA certificate and they also have a brief guide. But here is the full tutorial to help you during dynamic pentesting the apps:

  1. Create a folder
    mkdir cert && cd cert
  2. Install openssl
    sudo apt-get install openssl
  3. Find the default openssl config file and copy the default openssl.cnf
    cp /etc/ssl/openssl.cnf ./
  4. Create a private key, the “days” value is 730 means it < 2 year of validity. Then fill out some fields:
    openssl req -x509 -days 730 -nodes -newkey rsa:2048 -outform der -keyout server.key -out ca.der -extensions v3_ca -config openssl.cnf
  1. Convert to der format:
    openssl rsa -in server.key -inform pem -out server.key.der -outform der
  1. Convert key to pkcs8 format:
    openssl pkcs8 -topk8 -in server.key.der -inform der -out server.key.pkcs8.der -outform der -nocrypt
    Now we had 5 files in our cert folder:

7. Push certificate to device and install it, I named it Sun:

8. Importing these files to Burp’s proxy server: “ca.der” and server.key.pkcs8.der:

Now all done and you can intercept all the traffic: