Easy Way to Bypass SSL Pinning with Objection & Frida [Beginner Friendly]
After creating an article “Let’s Bypass SSL Pinning By Manual Modification”, I’ve thought of making an easy way for beginner in learning how to bypass ssl pinning with uncomplicated targets and using automatic method. In this article I will talk about objection for bypassing ssl pinning.
Objection is a runtime mobile exploration toolkit, powered by Frida, built to help you assess the security posture of your mobile applications, without needing a jailbreak.
- Supports both iOS and Android.
- Inspect and interact with container file systems.
- Bypass SSL pinning.
- Dump keychains.
- Perform memory related tasks, such as dumping & patching.
- Explore and manipulate objects on the heap.
- And much, much more…
We need to install and setup Frida because objection uses Frida as the instrumentation. For more information about Frida, you can read at https://frida.re/
Install frida
I’m using virtualenv in MacOS, but you can follow as your environment. Install frida-tools from pip
❯ pip -V
pip 22.1.2 from /Users/petruknisme/.venv/lib/python3.9/site-packages/pip (python 3.9)
❯ python -V
Python 3.9.13❯ pip install frida-tools
Collecting frida-tools
Using cached frida_tools-11.0.0-py3-none-any.whl
Requirement already satisfied: prompt-toolkit<4.0.0,>=2.0.0 in /Users/petruknisme/.venv/lib/python3.9/site-packages (from frida-tools) (3.0.19)
Requirement already satisfied: colorama<1.0.0,>=0.2.7 in /Users/petruknisme/.venv/lib/python3.9/site-packages (from frida-tools) (0.4.4)
Collecting frida<16.0.0,>=15.2.0
Using cached frida-15.2.2-cp39-cp39-macosx_10_12_x86_64.whl
Requirement already satisfied: pygments<3.0.0,>=2.0.2 in /Users/petruknisme/.venv/lib/python3.9/site-packages (from frida-tools) (2.11.1)
Requirement already satisfied: setuptools in /Users/petruknisme/.venv/lib/python3.9/site-packages (from frida<16.0.0,>=15.2.0->frida-tools) (58.1.0)
Requirement already satisfied: wcwidth in /Users/petruknisme/.venv/lib/python3.9/site-packages (from prompt-toolkit<4.0.0,>=2.0.0->frida-tools) (0.2.5)
Installing collected packages: frida, frida-tools
Successfully installed frida-15.2.2 frida-tools-11.0.0
We’ve successfully installed frida-15.2.2 and frida-tools-11.0.0. For frida to work in our android/ios, we need to setup the frida-server.
Setup frida-server in Android
Go to https://github.com/frida/frida/releases and choose the frida-server-version-android-arm.xz, change the version with your installed frida version like I bold above. Frida client and server must have the same version.
Download the frida to host machine
❯ wget https://github.com/frida/frida/releases/download/15.2.2/frida-server-15.2.2-android-arm64.xz
Length: 6824388 (6.5M) [application/octet-stream]
Saving to: ‘frida-server-15.2.2-android-arm64.xz’frida-server-15.2.2-andr 100%[==================================>] 6.51M 6.88MB/s in 0.9s2022-08-08 18:01:59 (6.88 MB/s) - ‘frida-server-15.2.2-android-arm64.xz’ saved [6824388/6824388]
Extract xz file
❯ xz -d frida-server-15.2.2-android-arm64.xz
❯ ls | grep frida
frida-server-15.2.2-android-arm64
❯ mv frida-server-15.2.2-android-arm64 frida-server
After downloading, rename the file to frida-server, the next step is to copy the file to android. In my device, I copy to /data/local/tmp/
❯ adb push frida-server /data/local/tmp
frida-server: 1 file pushed, 0 skipped. 23.1 MB/s (19972988 bytes in 0.824s)
Run the frida-server
❯ adb shell
jasmine_sprout:/ $ su
jasmine_sprout:/ # cd /data/local/tmp/
jasmine_sprout:/data/local/tmp # chmod +x frida-server
jasmine_sprout:/data/local/tmp # ./frida-server &
If running sucessfully, we can check with ps and grep
1|jasmine_sprout:/data/local/tmp # ps | grep frida
root 23299 23280 39280 19640 poll_schedule_timeout f10486e0 S frida-server
We’ve done with setuping frida in android. Now, we will setup the objection.
Install Objection
❯ pip install -U objection
Collecting objection
Downloading objection-1.11.0.tar.gz (327 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 327.2/327.2 kB
Successfully built objection
Installing collected packages: objection
Successfully installed objection-1.11.0
Testing if Objection is working properly with frida-server from our android
❯ objection --gadget "com.android.settings" device-type
Using USB device `Mi A2`
Agent injected and responds ok!
Connection: USB
Name: com.android.settings
System: jasmine_sprout
Model: xiaomi
Version: 10
Asking jobs to stop...
Unloading objection agent...
From the above message, it tells us that it’s successfully injected the agent and showing device information.
In this article, we will use a sample application to learn how objection bypass the ssl pinning. Download and install the application from https://github.com/aancw/android-ssl-pinning-signed-demo
❯ wget https://github.com/aancw/android-ssl-pinning-signed-demo/releases/download/v1.0.9/pinning-signed.apk❯ adb install pinning-signed.apk
Performing Streamed Install
Success
When we open the application, the button color will be purple. But when we click the button, it will change the color to red(fail)/green(success) when doing https request to the pinned server.
Proxy Setup
We need to test the app using a proxy to know the app is fail or success when doing http request. Run burp suite app and set listen address
Explanation:
- Setting bind port to 8090, this port will be used in android setting
- Setting listen address to our LAN IP
Setting the android wifi with manual proxy setting
Click save and run the application again. When we click the button, it will be showing an error message telling us the pinning process failed
The main topic for this article is bypassing ssl pinning with different pinning method/library like okhttp, trustkit, volley and much more.
Bypass SSL Pinning
Check the target running app package name
❯ frida-ps -Uia | grep pinning
21332 SSL Pinning Demo tech.httptoolkit.pinning_demo
Running objection and injecting the agent. Our target application will be running again with the new session. When we click the button, it will still be showing us an error message when doing pinning request.
❯ objection --gadget "tech.httptoolkit.pinning_demo" explore
Using USB device `Mi A2`
Agent injected and responds ok!_ _ _ _
___| |_|_|___ ___| |_|_|___ ___
| . | . | | -_| _| _| | . | |
|___|___| |___|___|_| |_|___|_|_|
|___|(object)inject(ion) v1.11.0Runtime Mobile Exploration
by: @leonjza from @sensepost[tab] for command suggestions
tech.httptoolkit.pinning_demo on (xiaomi: 10) [usb] #
For doing ssl pinning bypass, we can use the command android sslpinning disable
tech.httptoolkit.pinning_demo on (xiaomi: 10) [usb] # android sslpinning disable
(agent) Custom TrustManager ready, overriding SSLContext.init()
(agent) Found okhttp3.CertificatePinner, overriding CertificatePinner.check()
(agent) Found okhttp3.CertificatePinner, overriding CertificatePinner.check$okhttp()
(agent) Found com.android.org.conscrypt.TrustManagerImpl, overriding TrustManagerImpl.verifyChain()
(agent) Found com.android.org.conscrypt.TrustManagerImpl, overriding TrustManagerImpl.checkTrustedRecursive()
(agent) Registering job 751373. Type: android-sslpinning-disable
As you can see in the video above, we successfully bypassed the ssl pinning in our demo app. Only the Okhttp that still fail when bypassing ssl pinning using Objection.
As far as I know, the objection method can bypass ssl pinning for Okhttp. But for this target, it seems missing some function to hook. Let’s start to dig into the problem.
Debugging with Frida
We will use Frida to debug our demo application to know what is causing objection so can’t bypass the ssl pinning. From our last experiment above, it tells us that application throwing an error at javax.net.ssl.SSLPeerUnverifiedException
.
Frida supports Javascript API for instrumenting the binary. So we can patch and modify the binary at runtime with javascript. In this case, we will create a javascript file to debug our application and try to bypass the ssl pinning that was missed by objection.
Because we are already spawned the application before, so we will use attach mode for frida to launch the script. More information about spawning vs attaching for frida: https://summit-labs.frida.ninja/frida-tool-reference/frida
Get PID of application that running with objection in previous section
❯ frida-ps -Uia | grep pinning_demo
8988 SSL Pinning Demo tech.httptoolkit.pinning_demo
Our application PID is 8988. Note this number for later.
Creating our Frida Javascript
Create an instance of javax.net.ssl.SSLPeerUnverifiedException and initialize it. This instance is from our previous error.
Java.perform(function () {const UnverifiedCertError = Java.use('javax.net.ssl.SSLPeerUnverifiedException');
});
We will hook the constructor of a class using $init
as the method name and hook Method in a class and set the implementation to the custom code.
UnverifiedCertError.$init.implementation = function (str) {
};
In the above function, we will add java stack trace. So we know which function/method causing the previous error
UnverifiedCertError.$init.implementation = function (str) {const stackTrace = Java.use('java.lang.Thread').currentThread().getStackTrace();
};
After that we will filter the stack trace to only index the javax.net.ssl.SSLPeerUnverifiedException
UnverifiedCertError.$init.implementation = function (str) {const stackTrace = Java.use('java.lang.Thread').currentThread().getStackTrace();
const exceptionStackIndex = stackTrace.findIndex(stack =>
stack.getClassName() === "javax.net.ssl.SSLPeerUnverifiedException"
);
const callingFunctionStack = stackTrace[exceptionStackIndex + 1];
};
Then, we will map the className and methodName that causing the problem
UnverifiedCertError.$init.implementation = function (str) {const stackTrace = Java.use('java.lang.Thread').currentThread().getStackTrace();
const exceptionStackIndex = stackTrace.findIndex(stack =>
stack.getClassName() === "javax.net.ssl.SSLPeerUnverifiedException"
);
const callingFunctionStack = stackTrace[exceptionStackIndex + 1];const className = callingFunctionStack.getClassName();
const methodName = callingFunctionStack.getMethodName();
};
We are done for debugging the stack trace. The final code for debugging the stack trace:
Save the file as ssl-error.js or whatever you name it. Run frida with our javascript
❯ frida -U -l <js_file> --no-pause -p <PID_Number>
Frida will attach the script to our running process
____
/ _ | Frida 15.1.17 - A world-class dynamic instrumentoolkit
| (_| |
> _ | Commands:
/_/ |_| help -> Displays the help system
. . . . object? -> Display information about 'object'
. . . . exit/quit -> Exit
. . . .
. . . . More info at https://frida.re/docs/home/[Mi A2::PID::8988]->
Switch to android ssl pinning demo application and then click Okhttp Pinning Request. This is what I get from frida
From the message above, it is clear that error message is throwing by okhttp3.CertificatePinner->check$okhttp.
After hours googling, I found that someone experience this issue too when using objection https://github.com/sensepost/objection/issues/475
From the above message, he suspect non-obfuscated Kotlin apps need little change for function hook in the script. Our app is non-obfuscated kotlin app too, so it’s relevant issue.
Creating Bypass Script for non-obfuscated Kotlin
We will add the script from objection but change the function as thgoebel mentioned before.
Save and click the Okhttp Pinned Request again. No need to exit and relaunch frida, because it will automatically reload the script.
Our final script will success to bypass the ssl pinning from okhttp as below
Sadly, we reach the end of the article. I hope this article will be guidance for newcomer when doing mobile application pentest but you don’t know what to do to bypass the ssl pinning. If you like what you read, you can share this article to everyone you think need to know.
Feedback are always welcome because it’s valuable thing and there will be always room for improvement. Thank you!