Introduction to SSL Pinning and OkHttp3
SSL (Secure Sockets Layer) pinning is a crucial security mechanism employed by mobile applications to prevent Man-in-the-Middle (MITM) attacks. While standard HTTPS ensures that a client communicates with the intended server by verifying its certificate against a set of trusted root Certificate Authorities (CAs), SSL pinning takes this a step further. It means the application expects a specific certificate or public key from the server, hardcoded within the app itself. If the server presents a different certificate, even one issued by a trusted CA (like a Burp Suite proxy’s CA), the connection is immediately terminated.
OkHttp3 is a popular HTTP client for Java and Android applications, known for its efficiency and robust features, including built-in support for SSL pinning via its CertificatePinner class. For penetration testers and security researchers, bypassing such mechanisms is essential to intercept and analyze application network traffic, identify vulnerabilities, and assess the overall security posture of the application.
Understanding OkHttp3’s CertificatePinner
OkHttp3 implements SSL pinning using the okhttp3.CertificatePinner class. This class allows developers to specify which certificates or public keys are trusted for particular hostnames. When an OkHttp3 client makes a request, the CertificatePinner intercepts the SSL handshake, extracts the server’s certificate chain, and compares it against the pre-configured pins. If no match is found, the connection is aborted with an SSLPeerUnverifiedException.
Developers typically configure CertificatePinner by adding certificate hashes (SHA-256) or public key hashes. These hashes are often embedded directly into the application’s source code or configuration files. Here’s a common way it’s implemented in an Android application:
import okhttp3.CertificatePinner;import okhttp3.OkHttpClient;import okhttp3.Request;public class PinnedHttpClient { public static void main(String[] args) { CertificatePinner certificatePinner = new CertificatePinner.Builder() .add("example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=") .add("*.example.com", "sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=") .build(); OkHttpClient client = new OkHttpClient.Builder() .certificatePinner(certificatePinner) .build(); Request request = new Request.Builder() .url("https://example.com/") .build(); try { client.newCall(request).execute(); System.out.println("Connection successful!"); } catch (Exception e) { System.err.println("SSL Pinning failed: " + e.getMessage()); } }}
In this example, the client explicitly trusts only the certificates with the specified SHA-256 hashes for example.com and its subdomains. Any attempt to proxy this traffic with a self-signed certificate (like those from Burp Suite) will fail.
The Penetration Testing Challenge
When an application employs SSL pinning, traditional methods of intercepting traffic, such as configuring an HTTP proxy (e.g., Burp Suite, OWASP ZAP) and installing its root CA on the device, become ineffective. The app will detect the proxy’s certificate as untrusted (because it doesn’t match the pinned hash) and refuse to establish a connection. This is where dynamic instrumentation frameworks like Frida become indispensable.
Introducing Frida: Your Dynamic Instrumentation Ally
Frida is a dynamic instrumentation toolkit that allows you to inject snippets of JavaScript or your own library into native apps on Windows, macOS, Linux, iOS, Android, and QNX. It provides a powerful API to hook functions, modify code, and inspect runtime data. For Android penetration testing, Frida is commonly used to bypass security controls that operate at runtime, such as root detection, anti-tampering checks, and, crucially, SSL pinning.
Frida Setup Prerequisites
Before proceeding, ensure you have the following:
- Rooted Android Device or Emulator: Frida requires root privileges to inject into arbitrary processes.
- ADB (Android Debug Bridge): To interact with your Android device.
- Frida Server: The Frida server binary running on your Android device. You can download it from Frida’s GitHub releases page (e.g.,
frida-server-<version>-android-arm64) and push it to/data/local/tmp/, then execute it with./frida-server. - Frida-tools: Installed on your host machine (
pip install frida-tools).
Frida-Based Bypass Strategy for OkHttp3
The core strategy to bypass OkHttp3 SSL pinning with Frida involves hooking into the okhttp3.CertificatePinner class and modifying its behavior at runtime. Specifically, we target the check method, which is responsible for performing the actual certificate validation against the pinned values. By replacing the original implementation of this method with an empty (no-op) function, we effectively disable the pinning check, allowing any certificate (including your proxy’s) to pass.
There are two common overloads for the check method in CertificatePinner:
check(String hostname, java.util.List<java.security.cert.Certificate> peerCertificates)check(String hostname, [Ljava.security.cert.X509Certificate; peerCertificates)
We’ll typically hook both to ensure comprehensive coverage, although the first one (with List<Certificate>) is more frequently invoked in modern OkHttp3 versions.
Step-by-Step Bypass with Frida
1. Identify the Target Application
First, you need the package name of the Android application you wish to test. You can find this using ADB:
adb shell pm list packages | grep <app_name_keyword>
For example, if the app is called
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 →