Introduction to SSL Pinning and OkHttp3
SSL Pinning is a security mechanism employed by client applications to prevent Man-in-the-Middle (MITM) attacks. Instead of trusting any certificate signed by a trusted Certificate Authority (CA) for a given domain, the application “pins” specific certificates or public keys. This means the application will only accept connections if the server presents one of the pre-defined, pinned certificates. If an attacker tries to intercept traffic using a proxy (like Burp Suite or OWASP ZAP) with a self-signed or different CA-signed certificate, the application will detect the mismatch and terminate the connection, effectively thwarting the MITM attempt.
OkHttp3 is a popular, high-performance HTTP client for Java and Android. It provides robust features, including an elegant way to implement SSL pinning through its CertificatePinner class. While beneficial for security, this mechanism poses a significant challenge for penetration testers and security researchers who need to intercept and analyze application traffic.
OkHttp3’s CertificatePinner Implementation
OkHttp3 allows developers to pin certificates or public keys directly within their application code. This is typically done by creating an instance of okhttp3.CertificatePinner and adding specific hostnames along with their respective SHA-256 hashes of the certificates’ public keys (SPKI pins). The client then uses this pinner when building the OkHttpClient.
Example OkHttp3 SSL Pinning Configuration:
public class PinnedHttpClient { private static OkHttpClient client; static { String hostname = "public-api.example.com"; String publicKeySha256 = "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; // Replace with actual SHA-256 hash client = new OkHttpClient.Builder() .certificatePinner( new CertificatePinner.Builder() .add(hostname, publicKeySha256) .build()) .build(); } public static OkHttpClient getClient() { return client; } }
When a connection is initiated to public-api.example.com, OkHttp3’s CertificatePinner will intercept the TLS handshake. It extracts the public key from the server’s certificate chain, computes its SHA-256 hash, and compares it against the pre-configured publicKeySha256. If they don’t match, the connection is aborted by throwing an SSLPeerUnverifiedException.
The Penetration Tester’s Dilemma
Traditional methods for intercepting HTTPS traffic on Android applications involve installing a custom root CA certificate (e.g., from Burp Suite) on the device. This allows the proxy to generate on-the-fly certificates for target domains, which the Android OS then trusts because it trusts the proxy’s root CA. However, with SSL pinning, the application bypasses the OS’s trust store and performs its own validation. Even if the proxy’s certificate is trusted by the OS, the application’s CertificatePinner will reject it, as its public key hash won’t match the pinned values.
Frida: The Dynamic Instrumentation Toolkit
Frida is a dynamic instrumentation toolkit that allows developers and security researchers to inject JavaScript code into running processes on various platforms, including Android. It’s incredibly powerful because it operates at runtime, allowing you to hook into native functions, Java methods, inspect memory, and modify behavior without recompiling the application. For SSL pinning bypasses, Frida’s ability to hook Java methods is precisely what we need.
Frida’s Surgical Approach to Neutralize OkHttp3 SSL Pinning
Our goal is to prevent OkHttp3’s CertificatePinner from performing its validation checks. The most effective way to do this is to target the check method within the okhttp3.CertificatePinner class. By hooking this method, we can force it to do nothing or always return successfully, effectively bypassing the pinning logic.
Identifying the Target Method
The core logic for certificate validation in OkHttp3 resides in the check method of okhttp3.CertificatePinner. This method typically takes a hostname and a list of certificates as arguments. If pinning is enforced and the certificates don’t match, it throws an exception. Our strategy is to hook this method and make it a no-operation (no-op) or simply return without throwing an exception.
Frida Scripting – Bypassing `CertificatePinner.check`
Here’s a Frida script designed to bypass OkHttp3’s SSL pinning:
Java.perform(function() { console.log("[*] Starting Frida SSL Pinning Bypass for OkHttp3..."); try { var CertificatePinner = Java.use('okhttp3.CertificatePinner'); console.log("[+] Found okhttp3.CertificatePinner class."); // Hook the 'check' method that takes a hostname and a list of certificates CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function (hostname, certificates) { console.warn("[!!!] OkHttp3 CertificatePinner.check bypassed for: " + hostname); // Call the original method optionally, but for a bypass, we just return. // If you want to log original behavior, you can call this.check(hostname, certificates); // Simply return without throwing any exception, effectively bypassing pinning. }; console.log("[+] Hooked okhttp3.CertificatePinner.check method successfully."); } catch (e) { console.error("[-] Error hooking OkHttp3 CertificatePinner: " + e.message); } // Also hook the check method that takes a hostname and a X509Certificate (for older OkHttp versions or other variants) try { var CertificatePinner = Java.use('okhttp3.CertificatePinner'); CertificatePinner.check.overload('java.lang.String', 'java.security.cert.X509Certificate').implementation = function (hostname, certificate) { console.warn("[!!!] OkHttp3 CertificatePinner.check (single cert) bypassed for: " + hostname); }; console.log("[+] Hooked okhttp3.CertificatePinner.check (single cert) method successfully."); } catch (e) { console.error("[-] Error hooking OkHttp3 CertificatePinner (single cert): " + e.message); } console.log("[*] OkHttp3 SSL Pinning bypass script loaded."); });
Explanation of the Script:
Java.perform(function() { ... });: This ensures that our JavaScript code runs within the context of the target application’s Java VM.var CertificatePinner = Java.use('okhttp3.CertificatePinner');: This line obtains a reference to theokhttp3.CertificatePinnerclass, allowing us to interact with its methods.CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function (...) { ... };: This is the core of the bypass. We are targeting the specific overload of thecheckmethod that accepts aString(hostname) and ajava.util.List(of certificates). We then replace its original implementation with our custom function.- Inside our custom
implementation, we simply log a message indicating the bypass and then do nothing else. By not calling the original method (e.g.,this.check(hostname, certificates);) and by not throwing any exception, we effectively make thecheckmethod a no-op from the perspective of the application’s pinning logic. - The second
try-catchblock attempts to hook another common overload of thecheckmethod, just in case the application uses it or an older OkHttp version.
Deployment and Execution
To use this script, you’ll need a rooted Android device or an emulator with Frida-server running. Follow these steps:
- Setup Frida-server on Android:
Download the appropriatefrida-serverfor your device’s architecture (e.g.,frida-server-*-android-arm64) from the Frida releases page.adb push /path/to/frida-server /data/local/tmp/frida-server adb shell "chmod 755 /data/local/tmp/frida-server" adb shell "/data/local/tmp/frida-server &" - Identify the target application’s package name:
adb shell pm list packages -3 - Run the Frida script: Save the JavaScript code above as
okhttp3_bypass.js.frida -U -f com.example.targetapp --no-pause -l okhttp3_bypass.jsReplace
com.example.targetappwith the actual package name of the application you are testing. The--no-pauseflag ensures the app starts immediately with the script injected. - Configure your proxy: Set up your device to proxy all traffic through Burp Suite (or your preferred proxy).
- Verify the bypass: Launch the application. You should now see its HTTPS traffic flowing through Burp Suite without SSL pinning errors, and your Frida console will show the bypass messages.
Limitations and Further Considerations
- Pinning Detection: Some advanced applications might include mechanisms to detect Frida’s presence or monitor for tampering with security-critical methods.
- Other Pinning Libraries: This script specifically targets OkHttp3. Other HTTP clients (e.g., Retrofit, Volley using custom TrustManagers) or custom implementations of
X509TrustManagerwould require different Frida hooks. - Native Pinning: If the application implements SSL pinning in native code (JNI, C/C++), a Java-based Frida hook will not be sufficient, and you would need to explore native hooking techniques.
Conclusion
SSL pinning is a robust security measure, but tools like Frida empower security researchers to understand and, when necessary, bypass these controls for legitimate testing purposes. By surgically hooking into OkHttp3’s CertificatePinner.check method, we can effectively neutralize its SSL pinning, enabling comprehensive traffic analysis. This technique highlights the importance of understanding both security implementation details and the powerful capabilities of dynamic instrumentation in modern application security.
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 →