Introduction to SSL Pinning and Its Bypass
SSL Pinning is a critical security mechanism implemented in mobile applications to prevent man-in-the-middle (MITM) attacks. It ensures that an app communicates only with trusted servers by validating the server’s certificate or public key against a predefined set of trusted entities embedded within the application itself. While excellent for security, this poses a significant challenge for penetration testers and security researchers who need to intercept and analyze app traffic. OkHttp3, a popular HTTP client for Android, frequently incorporates SSL pinning using its CertificatePinner class.
This article provides an expert-level guide on leveraging Frida, a dynamic instrumentation toolkit, to bypass OkHttp3 SSL pinning in Android applications. We will explore specific scripting techniques targeting OkHttp3’s core pinning mechanisms, offering practical examples and step-by-step instructions for successful traffic interception during penetration testing.
Understanding OkHttp3’s CertificatePinner
OkHttp3 implements SSL pinning primarily through its okhttp3.CertificatePinner class. Developers can configure an OkHttpClient instance to use a CertificatePinner that specifies expected SHA-256 hashes of certificates or public keys for specific hosts. When a connection is established, OkHttp3 verifies the server’s certificate chain against these pinned hashes. If a mismatch occurs, the connection is aborted, preventing communication with an untrusted server (like a proxy’s certificate).
A typical OkHttp3 pinning implementation might look like this in Java/Kotlin code:
public class MyApp { private OkHttpClient client; public MyApp() { CertificatePinner certificatePinner = new CertificatePinner.Builder() .add("*.example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=") .add("*.anothersite.com", "sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=") .build(); client = new OkHttpClient.Builder() .certificatePinner(certificatePinner) .build(); }}
The key method we’ll target in CertificatePinner for bypassing is the check() method, which performs the actual pinning validation.
Frida Fundamentals for Android Penetration Testing
Frida allows injecting JavaScript snippets into native apps on Android, iOS, Windows, macOS, and Linux. For Android, it enables us to hook into Java methods, modify their behavior, inspect arguments, and even replace entire implementations at runtime. This makes it an ideal tool for bypassing security controls like SSL pinning without modifying the application’s APK.
Setting up Frida
Before proceeding, ensure you have:
- A rooted Android device or emulator.
- Frida server running on the Android device.
- Frida tools installed on your host machine (
pip install frida-tools). adb(Android Debug Bridge) configured.
To run the Frida server:
adb push frida-server /data/local/tmp/frida-serveradb shell "chmod 755 /data/local/tmp/frida-server"adb shell "/data/local/tmp/frida-server &"
Frida Scripting Techniques for OkHttp3 SSL Pinning Bypass
Method 1: Hooking okhttp3.CertificatePinner.check()
This is the most direct approach. We’ll target the check() method within the okhttp3.CertificatePinner class and force it to do nothing, effectively disabling the pinning validation.
Java.perform(function () { console.log("[*] Starting OkHttp3 CertificatePinner Bypass..."); var CertificatePinner = Java.use('okhttp3.CertificatePinner'); CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function (hostname, peerCertificates) { console.log("[+] Bypassing CertificatePinner.check() for hostname: " + hostname); // Do nothing, effectively bypassing the pinning check // You can optionally call the original method if you want to inspect arguments // this.check(hostname, peerCertificates); }; console.log("[*] OkHttp3 CertificatePinner Bypass script loaded.");});
Explanation:
Java.perform(function () { ... });: Ensures our script runs in the context of the target Java VM.Java.use('okhttp3.CertificatePinner');: Obtains a JavaScript wrapper for theokhttp3.CertificatePinnerclass..check.overload('java.lang.String', 'java.util.List'): Specifies the exact overload of thecheck()method we want to hook. It’s crucial to match the argument types correctly..implementation = function (...) { ... };: Replaces the original implementation of thecheck()method with our custom function, which simply logs a message and returns, thus skipping the actual pinning logic.
Method 2: Hooking javax.net.ssl.X509TrustManager.checkServerTrusted()
While Method 1 targets OkHttp3 specifically, many SSL pinning implementations (including those relying on default system trust managers) eventually call methods within the X509TrustManager interface. Bypassing checkServerTrusted() offers a more generic approach that can work even if the app uses custom trust managers or other libraries for pinning, as long as they ultimately delegate to an X509TrustManager.
Java.perform(function () { console.log("[*] Starting X509TrustManager Bypass..."); var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager'); var TrustManagerImpl = Java.use('com.android.org.conscrypt.TrustManagerImpl'); // For newer Android versions var checkServerTrusted = X509TrustManager.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String'); var checkServerTrustedArgs = checkServerTrusted.argumentTypes; checkServerTrusted.implementation = function (chain, authType) { console.log("[+] Bypassing X509TrustManager.checkServerTrusted()"); // Call original method only if needed, for simplicity we bypass // this.checkServerTrusted(chain, authType); }; // Also hook TrustManagerImpl.checkServerTrusted if TrustManagerImpl is used if (TrustManagerImpl) { var checkServerTrusted_TrustManagerImpl = TrustManagerImpl.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String'); if (checkServerTrusted_TrustManagerImpl) { checkServerTrusted_TrustManagerImpl.implementation = function (chain, authType, host) { console.log("[+] Bypassing TrustManagerImpl.checkServerTrusted() for host: " + host); // this.checkServerTrusted(chain, authType, host); }; } var checkServerTrusted_TrustManagerImpl_sslEngine = TrustManagerImpl.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLEngine'); if (checkServerTrusted_TrustManagerImpl_sslEngine) { checkServerTrusted_TrustManagerImpl_sslEngine.implementation = function (chain, authType, sslEngine) { console.log("[+] Bypassing TrustManagerImpl.checkServerTrusted() for SSLEngine"); // this.checkServerTrusted(chain, authType, sslEngine); }; } } console.log("[*] X509TrustManager Bypass script loaded.");});
Explanation:
- This script targets multiple overloads of
checkServerTrusted()in both the standardX509TrustManagerand the Android-specificcom.android.org.conscrypt.TrustManagerImpl. This comprehensive approach increases the chances of successful bypass across different Android versions and app implementations. - Similar to Method 1, we replace the original implementation with an empty function, preventing certificate validation errors.
Execution Guide and Observing Results
To apply these Frida scripts:
- Save your chosen script (e.g.,
bypass.js). - Identify the target Android application’s package name (e.g.,
com.example.app). - Run Frida, attaching to the application:
frida -U -f com.example.app -l bypass.js --no-pause
The -U flag targets a USB-connected device, -f spawns and attaches to the app, -l loads your script, and --no-pause starts the app immediately without waiting. After running, interact with the app. If successful, you should see the log messages from your Frida script in the console.
To observe the intercepted traffic:
- Configure your Android device’s Wi-Fi proxy settings to point to your Burp Suite (or any other HTTP proxy) instance running on your host machine.
- Ensure Burp Suite is set up to listen on the specified port and is configured to generate an SSL CA certificate that your device trusts (typically by installing Burp’s CA certificate on the Android device).
- With the Frida script active and the proxy configured, all network traffic from the target app should now flow through Burp Suite, allowing you to inspect requests and responses.
Advanced Considerations and Troubleshooting
- Obfuscation: Apps using ProGuard or DexGuard might obfuscate class and method names. In such cases, you might need to use techniques like
Java.enumerateLoadedClasses()to find the real class names at runtime, or use wildcard patterns inJava.use()(e.g.,Java.use('/.*.CertificatePinner/')if the package name is unknown but the class name is unique). - Multiple Pinning Mechanisms: Some applications might combine OkHttp3 pinning with other forms of pinning (e.g., using Conscrypt’s
PinningTrustManageror custom network security configurations). A multi-pronged approach combining various Frida scripts might be necessary. - Timing Issues: Ensure your script is injected and active before the application initializes its network stack. Using
--no-pausewith-fhelps ensure early injection. - Error Handling: Always add
try...catchblocks in your Frida scripts to gracefully handle cases where a class or method might not exist or has a different signature than expected.
Conclusion
Bypassing SSL pinning is a fundamental skill for Android application penetration testers. Frida provides an incredibly flexible and powerful platform to achieve this without modifying the application binary. By understanding how OkHttp3 implements its CertificatePinner and leveraging the techniques discussed—particularly hooking CertificatePinner.check() and X509TrustManager.checkServerTrusted()—you can effectively neutralize this security control and gain full visibility into an app’s network communications. Always remember to apply these techniques ethically and only on applications you have explicit permission to test.
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 →