Android App Penetration Testing & Frida Hooks

Frida Universal SSL Pinning Bypass: The Ultimate Android Penetration Testing Guide

Google AdSense Native Placement - Horizontal Top-Post banner

Introduction to SSL Pinning and Its Purpose

SSL Pinning, or Certificate Pinning, is a security mechanism implemented within mobile applications to prevent man-in-the-middle (MITM) attacks. Unlike standard SSL/TLS validation where the client trusts any certificate signed by a trusted Certificate Authority (CA), SSL pinning involves the application explicitly checking if the server’s certificate or its public key matches a pre-defined, ‘pinned’ certificate or public key embedded within the application itself. If the certificate doesn’t match the pinned one, the application refuses to establish a connection, even if the certificate is otherwise valid and signed by a trusted CA. This prevents attackers from intercepting traffic by presenting their own CA-signed certificate, common in penetration testing setups with tools like Burp Suite or OWASP ZAP.

While beneficial for security, SSL pinning poses a significant challenge for security researchers and penetration testers who need to intercept and analyze application traffic for vulnerabilities. Bypassing SSL pinning is often a prerequisite for thorough security assessments.

Why Bypass SSL Pinning?

For penetration testers, bypassing SSL pinning is crucial for several reasons:

  • Traffic Analysis: Intercepting HTTP/S traffic reveals API endpoints, data formats, authentication mechanisms, and potential sensitive data leakage.
  • Vulnerability Discovery: Many vulnerabilities (e.g., injection flaws, improper authorization, logic errors) manifest in the communication between the client and server.
  • API Testing: Once traffic is intercepted, testers can manipulate requests and responses to test the robustness of backend APIs.
  • Understanding Application Logic: Observing live data flow helps in understanding how the application interacts with its backend services.

Traditional methods like installing a proxy’s CA certificate on the device often fail against pinned applications, necessitating more advanced techniques like dynamic instrumentation.

Introducing Frida for Dynamic Instrumentation

Frida is a dynamic instrumentation toolkit that allows you to inject scripts into running processes on Windows, macOS, Linux, iOS, Android, and QNX. It gives you the ability to hook into application functions, read and write memory, and even call arbitrary functions. For Android penetration testing, Frida is an indispensable tool, especially for bypassing security mechanisms like SSL pinning, root detection, and signature verification.

Prerequisites for Frida SSL Pinning Bypass

Before proceeding, ensure you have the following:

  • Rooted Android Device or Emulator: Frida requires root privileges to inject scripts into arbitrary processes.
  • ADB (Android Debug Bridge): Installed and configured on your host machine to communicate with the Android device/emulator.
  • Frida-tools: Installed on your host machine (`pip install frida-tools`).
  • Frida-server: Running on your Android device/emulator.

Setting Up Frida-Server on Android

1. Download Frida-server: Visit the Frida releases page and download the appropriate `frida-server` binary for your Android device’s architecture (e.g., `arm`, `arm64`, `x86`, `x86_64`). You can determine your device’s architecture using `adb shell getprop ro.product.cpu.abi`.

adb shell getprop ro.product.cpu.abi

2. Push to Device: Push the downloaded `frida-server` binary to a writable location on your device, such as `/data/local/tmp`.

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

3. Set Permissions: Make the `frida-server` executable.

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

4. Run Frida-server: Start the `frida-server` in the background. It needs to run with root privileges.

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

You can verify it’s running by checking `adb logcat` or `frida-ps -U` on your host machine.

The Universal SSL Pinning Bypass Script

Modern Android applications can implement SSL pinning using various methods: Android’s `TrustManager` API, OkHttp’s `CertificatePinner`, various WebView implementations, or even custom native code. A robust bypass script needs to target these common mechanisms. Below is a widely used, comprehensive Frida script designed to bypass most common SSL pinning implementations by hooking relevant Java and Native methods.

Java.perform(function() {   var CertificateFactory = Java.use("java.security.cert.CertificateFactory");   var FileInputStream = Java.use("java.io.FileInputStream");   var BufferedInputStream = Java.use("java.io.BufferedInputStream");   var X509Certificate = Java.use("java.security.cert.X509Certificate");   var KeyStore = Java.use("java.security.KeyStore");   var TrustManagerFactory = Java.use("javax.net.ssl.TrustManagerFactory");   var SSLContext = Java.use("javax.net.ssl.SSLContext");   // Custom TrustManager   var TrustManager = Java.use("javax.net.ssl.X509TrustManager");   var TrustManagerImpl = Java.registerClass({       name: 'android.app.TrustManagerImpl',       implements: [TrustManager],       methods: {           checkClientTrusted: function(chain, authType) {},           checkServerTrusted: function(chain, authType) {},           getAcceptedIssuers: function() {               return [];           }       }   });   // Bypass TrustManagerImpl (Android N and above)   var TrustManagerImpl_ = Java.use('com.android.org.conscrypt.TrustManagerImpl');   TrustManagerImpl_.verifyChain.implementation = function(chain, authType, host, clientAuth, ocspData, tlsSkt) {       console.log('Bypassing TrustManagerImpl.verifyChain() for ' + host);       return chain;   };   // Bypass various forms of OkHttp CertificatePinner   try {       var CertificatePinner = Java.use('okhttp3.CertificatePinner');       CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function() {           console.log('Bypassing OkHttp3 CertificatePinner.check()');           return;       };       CertificatePinner.check.overload('java.lang.String', 'java.security.cert.Certificate').implementation = function() {           console.log('Bypassing OkHttp3 CertificatePinner.check()');           return;       };   } catch (e) {       console.log('okhttp3.CertificatePinner not found or already hooked: ' + e);   }   // Bypass Volley and other custom TrustManagers   try {       var HttpsURLConnection = Java.use("javax.net.ssl.HttpsURLConnection");       HttpsURLConnection.setDefaultHostnameVerifier.implementation = function(hostnameVerifier) {           console.log('Bypassing HttpsURLConnection.setDefaultHostnameVerifier()');           return;       };       HttpsURLConnection.setSSLSocketFactory.implementation = function(sslSocketFactory) {           console.log('Bypassing HttpsURLConnection.setSSLSocketFactory()');           return;       };       HttpsURLConnection.setHostnameVerifier.implementation = function(hostnameVerifier) {           console.log('Bypassing HttpsURLConnection.setHostnameVerifier()');           return;       };   } catch (e) {       console.log('HttpsURLConnection hooks not applied: ' + e);   }   // Bypass WebView pinning   try {       var WebView = Java.use('android.webkit.WebView');       WebView.setWebContentsDebuggingEnabled.overload('boolean').implementation = function(enabled) {           console.log('Enabling WebView debugging.');           this.setWebContentsDebuggingEnabled(true);       };       var WebViewClient = Java.use('android.webkit.WebViewClient');       WebViewClient.onReceivedSslError.overload('android.webkit.WebView', 'android.webkit.SslErrorHandler', 'android.net.http.SslError').implementation = function(view, handler, error) {           console.log('Bypassing WebViewClient.onReceivedSslError()');           handler.proceed();       };       WebViewClient.onReceivedHttpAuthRequest.overload('android.webkit.WebView', 'android.webkit.HttpAuthHandler', 'java.lang.String', 'java.lang.String').implementation = function(view, handler, host, realm) {           console.log('Bypassing WebViewClient.onReceivedHttpAuthRequest()');           handler.proceed();       };   } catch (e) {       console.log('WebView hooks not applied: ' + e);   }   // SSLContext init hook to bypass various custom implementations   SSLContext.init.overload('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom').implementation = function(keyManagers, trustManagers, secureRandom) {       console.log('Bypassing SSLContext.init()');       this.init(keyManagers, [TrustManagerImpl.$new()], secureRandom);   };});

Execution Steps

1. Save the Script: Save the above JavaScript code to a file, for example, `frida-ssl-bypass.js`.

2. Identify Package Name: Find the package name of the target application. You can use `frida-ps -U` to list all running processes on your device and find the desired application. Alternatively, you can use `adb shell pm list packages` to list all installed packages.

frida-ps -U

3. Run Frida with the Script: Execute Frida to inject your script into the target application. The `-U` flag targets a USB device, `-f` spawns the application, `-l` loads your script, and `–no-pause` allows the application to start immediately without waiting for Frida to attach (useful for early hooks).

frida -U -f com.example.targetapp -l frida-ssl-bypass.js --no-pause

Replace `com.example.targetapp` with the actual package name of your target application.

4. Verify Bypass: With the script running, configure your device’s proxy settings to point to your intercepting proxy (e.g., Burp Suite, OWASP ZAP) on your host machine. Now, open the target application and try to perform network requests. You should observe its traffic flowing through your proxy without SSL pinning errors. Look for messages like “Bypassing OkHttp3 CertificatePinner.check()” in your Frida console, indicating the hooks are active.

Troubleshooting Common Issues

  • Frida-server not running: Double-check that `frida-server` is running on your device and has execute permissions. Ensure no other instance is running on the same port.
  • Script not hooking: If the console doesn’t show

    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