Android Certificate Pinning

Certificate pinning (also known as TLS pinning) is a security technique used in mobile applications to protect against man-in-the-middle attacks. It works by hard coding the server’s SSL certificate or public key into the application, so only connections to a trusted server are accepted.
Instead of trusting all TLS certificates issued by a Certificate Authority, the mobile application stores the certificate or public key of a specific server in advance.

This article will be looking at disabling certificate pinning for Android applications. Doing this allows you to use traffic inspection tools such as BurpSuite, to identify vulnerabilities in the web requests being transmitted.


Intercepting Traffic with BurpSuite

First, let’s configured BurpSuite to intercept traffic from an emulated Android device without certificate pinning in place.

Download and install BurpSuite community edition. Open BurpSuite and set the proxy so it’s listening on all IP addresses (not just localhost).

In Android Studio go to File > Settings, and set the IP address of our computer running BurpSuite.

Start an Android emulator, and navigate to the address of the BurpSuite host on port 8080. Download the certificate.

In the emulator, go into Android settings and install the downloaded certificate.

Browse to a website of your choosing using the Chrome web browser.

You should see this traffic is being routed through BurpSuite for inspection.

We can intercept Chrome’s traffic, since it’s referring to the operating systems certificate store. Applications that implement certificate pinning use their own certificate store, so this method won’t work.

InjuredAndroid has a challenge that implements certificate pinning. Open the application and navigate to flag 17.

When we enter information into the text box and click submit, nothing will happen – and we won’t see any traffic in BurpSuite. We’re going to need to bypass certificate pinning to get around this.


Bypassing TLS Pinning

Frida is a dynamic instrumentation toolkit that allows you to intercept and modify function calls in real-time. Objection is a runtime mobile exploration toolkit that builds on Frida. We’re going to be using Objection to bypass certificate pinning.

There are two ways of disabling certificate pinning, either using a rooted device or by recompiling the target application.


Using a Rooted Device

First, install objection and frida-tools in a pip virtual environment.

1
2
3
4
python3 -m venv OBJECTION
source OBJECTION/bin/activate
cd OBJECTION/
./bin/pip3 install objection frida-tools

Install some required packages for objection.

1
sudo apt install apksigner zipalign

Check the installed Frida version.

1
2
./bin/frida --version
16.6.6

Setup an Android Nexus 6 virtual machine, release name S, API version 31 – x86_64.

Next, we need to install frida-server on the Anroid device. Note, the version of frida-server downloaded needs to match the version of frida we installed with pip.

1
2
3
4
5
6
7
8
9
user@ubuntu:~$ adb root
restarting adbd as root
user@ubuntu:~$ wget https://github.com/frida/frida/releases/download/16.6.6/frida-server-16.6.6-android-x86_64.xz
user@ubuntu:~$ unxz frida-server-16.6.6-android-x86_64.xz
user@ubuntu:~$ mv frida-server-16.6.6-android-x86_64 frida-server
user@ubuntu:~$ adb push frida-server /data/local/tmp/
frida-server: 1 file pushed, 0 skipped. 367.7 MB/s (114016280 bytes in 0.296s)
user@ubuntu:~$ adb shell "chmod 755 /data/local/tmp/frida-server"
user@ubuntu:~$ adb shell "/data/local/tmp/frida-server &"

With frida-server uploaded to the rooted device, we can now use objection to interact with the installed APK and patch out certificate pinning.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
user@ubuntu:~/Android/OBJECTION$ ./bin/frida-ps -U | grep -i injured
6388  InjuredAndroid                                              
user@ubuntu:~/Android/OBJECTION$ ./bin/objection -g InjuredAndroid explore
Using USB device `Android Emulator 5556`
Agent injected and responds ok!
 
     _   _         _   _
 ___| |_|_|___ ___| |_|_|___ ___
| . | . | | -_|  _|  _| | . |   |
|___|___| |___|___|_| |_|___|_|_|
      |___|(object)inject(ion) v1.11.0
 
     Runtime Mobile Exploration
        by: @leonjza from @sensepost
 
[tab] for command suggestions
b3nac.injuredandroid on (google: 12) [usb] # android sslpinning disable
(agent) Custom TrustManager ready, overriding SSLContext.init()
(agent) Found com.android.org.conscrypt.TrustManagerImpl, overriding TrustManagerImpl.verifyChain()
(agent) Found com.android.org.conscrypt.TrustManagerImpl, overriding TrustManagerImpl.checkTrustedRecursive()
(agent) Registering job 393082. Type: android-sslpinning-disable
b3nac.injuredandroid on (google: 12) [usb] #

You should now be able to intercept the challenge flag.


Application Patching

We can use Objection to patch the APK file to disable certificate pinning, without needing a rooted device.

Before starting Objection, make sure you have an Android device emulator running.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
./bin/objection patchapk -s InjuredAndroid.apk
No architecture specified. Determining it using `adb`...
Detected target device architecture as: x86_64
Using latest Github gadget version: 16.6.6
Patcher will be using Gadget version: 16.6.6
Detected apktool version as: 2.11.0
Running apktool empty-framework-dir...
I: Removing framework file: 1.apk
Unpacking InjuredAndroid.apk
App already has android.permission.INTERNET
Target class not specified, searching for launchable activity instead...
Reading smali from: /tmp/tmplya0inl5.apktemp/smali/b3nac/injuredandroid/MainActivity.smali
Injecting loadLibrary call at line: 10
Attempting to fix the constructors .locals count
Current locals value is 0, updating to 1:
Writing patched smali back to: /tmp/tmplya0inl5.apktemp/smali/b3nac/injuredandroid/MainActivity.smali
Copying Frida gadget to libs path...
Rebuilding the APK with the frida-gadget loaded...
Built new APK with injected loadLibrary and frida-gadget
Performing zipalign
Zipalign completed
Signing new APK.
Signed the new APK
Copying final apk from /tmp/tmplya0inl5.apktemp.aligned.objection.apk to InjuredAndroid.objection.apk in current directory...
Cleaning up temp files...

If you get any errors, you probably want to remove the apt version of apktool, and install the latest version by following these instructions.

Now, remove the un-patched APK file from the emulated device.

1
2
3
4
5
adb shell
emu64xa16k:/ $ pm list packages | grep -i injured                                                                                                           
package:b3nac.injuredandroid
emu64xa16k:/ $ pm uninstall b3nac.injuredandroid
Success

Then install the new APK package.

1
2
3
adb install InjuredAndroid.objection.apk
Performing Streamed Install
Success
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
./bin/objection explore
Using USB device `Android Emulator 5554`
Agent injected and responds ok!
 
     _   _         _   _
 ___| |_|_|___ ___| |_|_|___ ___
| . | . | | -_|  _|  _| | . |   |
|___|___| |___|___|_| |_|___|_|_|
      |___|(object)inject(ion) v1.11.0
 
     Runtime Mobile Exploration
        by: @leonjza from @sensepost
 
[tab] for command suggestions
b3nac.injuredandroid on (google: 12) [usb] #
b3nac.injuredandroid on (google: 12) [usb] # android sslpinning disable
(agent) Custom TrustManager ready, overriding SSLContext.init()
(agent) Found com.android.org.conscrypt.TrustManagerImpl, overriding TrustManagerImpl.verifyChain()
(agent) Found com.android.org.conscrypt.TrustManagerImpl, overriding TrustManagerImpl.checkTrustedRecursive()
(agent) Registering job 119346. Type: android-sslpinning-disable
b3nac.injuredandroid on (google: 12) [usb] #

In Conclusion

Bypassing certificate pinning using a rooted device often proves the most reliable method. To get either method to work will probably require running an older Android API version, as Frida needs to add API support, and it’s a moving target.