Android System Securing, Hardening, & Privacy

Bypass Android SSL Pinning with Frida: The Ultimate Guide (Rooted & Non-Rooted)

Google AdSense Native Placement - Horizontal Top-Post banner

Introduction to SSL Pinning and Frida

SSL Pinning is a security mechanism implemented by developers to prevent man-in-the-middle (MitM) attacks by ensuring that an application only trusts a specific, pre-defined server certificate or public key, rather than any certificate signed by a trusted root Certificate Authority (CA). While crucial for security, it often poses a challenge for security researchers, penetration testers, and developers who need to inspect network traffic for analysis or debugging.

Frida, a dynamic instrumentation toolkit, offers an incredibly powerful solution for bypassing SSL pinning on Android devices, regardless of whether they are rooted or not. It allows you to inject custom scripts into running processes, modify their behavior, and hook into functions, making it an ideal tool for this task.

Understanding SSL Pinning Implementations

Before diving into the bypass techniques, it’s essential to understand how applications implement SSL pinning. Common methods include:

  • TrustManager Customization: Overriding the default X509TrustManager to check for specific certificates.
  • OkHttp Pinning: Using OkHttp’s built-in CertificatePinner.
  • Network Security Configuration (NSC): Android 7.0 (API 24) and above allow apps to define their network security behavior declaratively in an XML file, including pinning.
  • WebView Overrides: Custom handling of SSL errors within WebViews.

Prerequisites for Frida Setup

To follow this guide, you’ll need the following:

  • Android Device/Emulator: Rooted device or an emulator (e.g., Android Studio AVD, Genymotion, NoxPlayer) for the rooted method. For non-rooted, any device will do, but you’ll need to modify the APK.
  • ADB (Android Debug Bridge): Installed and configured on your host machine.
  • Python 3: Installed on your host machine.
  • Frida Tools: Install via pip.
  • Frida Server: The corresponding frida-server binary for your device’s architecture.
  • APKTool: (Optional, for non-rooted method) For decompiling and recompiling APKs.

First, install Frida tools on your host machine:

pip install frida-tools

Next, download the appropriate frida-server for your Android device’s architecture from the Frida releases page. You can find your device’s architecture using adb shell getprop ro.product.cpu.abi.

Setting Up Frida Server on Rooted Devices

1. Push frida-server to the device:

adb push /path/to/frida-server /data/local/tmp/

2. Grant execute permissions:

adb shell "chmod 755 /data/local/tmp/frida-server"

3. Run frida-server:

adb shell "/data/local/tmp/frida-server &"

You can verify it’s running by executing frida-ps -U on your host machine, which should list running processes on the USB-connected device.

Bypassing SSL Pinning on Rooted Devices

Frida allows us to hook into the application’s code and modify its behavior at runtime. The general approach is to hook the methods responsible for certificate validation and make them always return ‘true’ or simply skip the validation logic.

One of the most effective ways to bypass various pinning implementations is to use a generic script that targets common validation points. The following script attempts to hook into several known methods for SSL pinning:

// universal-android-ssl-bypass.js
Java.perform(function () {
    console.log("[*] Starting Android SSL Pinning Bypass");

    var TrustManager = Java.use('javax.net.ssl.X509TrustManager');
    var HostnameVerifier = Java.use('javax.net.ssl.HostnameVerifier');
    var SSLContext = Java.use('javax.net.ssl.SSLContext');
    var CertificatePinner = Java.use('okhttp3.CertificatePinner');
    var AndroidWebViewClient = Java.use('android.webkit.WebViewClient');

    // Bypass TrustManager
    var TrustManager_init = TrustManager.$init.overload('[Ljavax.net.ssl.X509Certificate;', 'java.lang.String');
    TrustManager_init.implementation = function (chain, authType) {
        console.log("[+] Bypassing TrustManager: init");
        return this.init(chain, authType);
    };

    var TrustManager_checkServerTrusted = TrustManager.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String');
    TrustManager_checkServerTrusted.implementation = function (chain, authType) {
        console.log("[+] Bypassing TrustManager: checkServerTrusted");
    };

    // Bypass HostnameVerifier
    var HostnameVerifier_verify = HostnameVerifier.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession');
    HostnameVerifier_verify.implementation = function (hostname, session) {
        console.log("[+] Bypassing HostnameVerifier: verify");
        return true;
    };

    // Bypass CertificatePinner (OkHttp3)
    try {
        var CertificatePinner_check = CertificatePinner.check.overload('java.lang.String', 'java.util.List');
        CertificatePinner_check.implementation = function (hostname, certificates) {
            console.log("[+] Bypassing CertificatePinner: check (OkHttp3)");
        };
        var CertificatePinner_check_2 = CertificatePinner.check.overload('java.lang.String', '[Ljava.security.cert.Certificate;');
        CertificatePinner_check_2.implementation = function (hostname, certificates) {
            console.log("[+] Bypassing CertificatePinner: check (OkHttp3) with Certificate array");
        };
    } catch (e) {
        console.log("[-] OkHttp3 CertificatePinner not found or already bypassed.");
    }

    // Bypass Network Security Configuration (Android 7.0+)
    try {
        var NetworkSecurityConfig = Java.use('android.security.net.config.NetworkSecurityConfig');
        var NetworkSecurityConfig_is-PinningEnforced = NetworkSecurityConfig.isPinningEnforced.overload();
        NetworkSecurityConfig_is-PinningEnforced.implementation = function () {
            console.log("[+] Bypassing NetworkSecurityConfig: isPinningEnforced");
            return false;
        };
    } catch (e) {
        console.log("[-] NetworkSecurityConfig not found or not applicable.");
    }

    // Bypass WebViewClient for SSL Errors
    var AndroidWebViewClient_onReceivedSslError = AndroidWebViewClient.onReceivedSslError.overload('android.webkit.WebView', 'android.webkit.SslErrorHandler', 'android.net.http.SslError');
    AndroidWebViewClient_onReceivedSslError.implementation = function(webView, handler, error) {
        console.log("[+] Bypassing WebViewClient: onReceivedSslError");
        handler.proceed();
    };

    console.log("[*] Android SSL Pinning Bypass Finished");
});

To run this script against an application (e.g., package name com.example.app), execute:

frida -U -f com.example.app -l universal-android-ssl-bypass.js --no-pause

The --no-pause flag ensures the script is injected immediately when the app starts. Remember to replace com.example.app with the target application’s package name.

Bypassing SSL Pinning on Non-Rooted Devices (Frida Gadget)

Bypassing SSL pinning on a non-rooted device is more involved as it requires modifying the application’s APK to inject the Frida Gadget. The Frida Gadget is a precompiled library that you embed into an application, allowing Frida to instrument it without needing a running frida-server.

The steps are as follows:

  1. Decompile the APK: Use APKTool to decompile the target APK.
  2. Download Frida Gadget: Get the appropriate frida-gadget.so for your app’s architecture from the Frida releases page.
  3. Inject Frida Gadget:
    1. Place frida-gadget.so into the decompiled APK’s lib/<architecture>/ directory (e.g., lib/arm64-v8a/).
    2. Modify the application’s entry point (usually in AndroidManifest.xml or one of the first loaded activities/services) to load the Frida Gadget. This often involves editing the smali code to call System.loadLibrary("frida-gadget"). Locate the main Application class or the main Activity and add the library loading call within its onCreate method. For example, search for .method public onCreate()V and add invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V where v0 points to a string literal "frida-gadget".
  4. Recompile and Sign the APK:
  5. Install the Modified APK:
  6. Run Frida Client: Once the app starts, Frida will automatically connect.

Example Smali Injection (Simplified):

Assuming your main Application class is Lcom/example/app/MainApplication;, you would find its onCreate method. Add the following smali instructions:

.method public onCreate()V
    .locals 1
    .prologue

    invoke-super {p0}, Landroid/app/Application;->onCreate()V

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

    return-void
.end method

APKTool Commands:

# Decompile
apktool d your_app.apk -o decompiled_app

# ... (Manual injection of frida-gadget.so and smali modifications) ...

# Recompile
apktool b decompiled_app -o modified_app.apk

# Sign the APK (using apksigner or uber-apk-signer)
java -jar uber-apk-signer.jar --apks modified_app.apk

# Install
adb install modified_app-aligned-debug.apk

Once the modified app is running on your device, you can use the same Frida script as for rooted devices, but instead of -U (USB), you might use -H 127.0.0.1:27042 if using port forwarding for the gadget, or just specify the process name if running the gadget in listen mode.

# Assuming frida-gadget is configured to listen on default port
frida -H 127.0.0.1:27042 -f com.example.app -l universal-android-ssl-bypass.js --no-pause

Troubleshooting Common Issues

  • Frida-server not found or permissions denied: Ensure frida-server matches your device’s architecture and has execute permissions (chmod 755).
  • Application crashes after Frida injection: The Frida script might be hooking into an unexpected method or causing instability. Try a more targeted script or debug the script’s output.
  • SSL pinning still active: The application might be using a less common or custom SSL pinning implementation not covered by the generic script. Decompile the APK and analyze its code for specific pinning logic.
  • Frida Gadget not loading: Double-check the smali injection and ensure frida-gadget.so is in the correct lib directory for all relevant architectures.

Conclusion

Bypassing Android SSL pinning with Frida is a powerful technique for security analysis. Whether you’re working with a rooted device and direct Frida server interaction or tackling a non-rooted environment by injecting the Frida Gadget, this guide provides the foundational knowledge and practical steps to overcome this security measure. Remember to use these techniques responsibly and ethically, primarily for security research, penetration testing with explicit permission, or development purposes.

Android Mobile Specs & Compare Directory

Are you researching mobile hardware properties, processor SoCs, GPU chipsets, or RAM configurations? Access our complete specs catalog to compare up to 5 devices side-by-side!

Compare Devices Specs →
Google AdSense Inline Placement - Content Footer banner