
SSL pinning is one of the most common defenses mobile app developers use to stop attackers and researchers from intercepting network traffic. For penetration testers, security researchers, and QA engineers, bypassing this protection is often the first real obstacle in any Android app assessment. Frida has become the go-to instrumentation framework for solving this problem, letting you hook into an app's TLS validation logic at runtime without touching the original APK's source code.
This guide walks through the full process: understanding how SSL pinning works, setting up your testing environment, and using Frida scripts to defeat pinning implementations across common Android libraries like OkHttp, TrustManager, and Network Security Config.
SSL pinning (also called certificate pinning) hardcodes a specific certificate, public key, or hash inside the app itself. When the app makes an HTTPS request, it checks the server's certificate against this pinned value instead of relying solely on the device's trusted certificate store.
This means that even if you install a proxy tool like Burp Suite or mitmproxy and add its CA certificate to your device, the app will still refuse the connection because the certificate presented by your proxy does not match the pinned value.
Common places pinning logic lives in an Android app include:
X509TrustManager implementationsCertificatePinner classUnderstanding where the pinning check happens is the first step before writing or choosing a Frida script to bypass it.
Before you touch any bypass scripts, your environment needs to be ready. SageFox Cybersecurity's mobile testing labs typically use a rooted Android device or emulator, since Frida's full capabilities require root access to inject into arbitrary processes.
adb devicesbash
pip install frida-tools frida
frida --versionDownload the frida-server binary that matches your device architecture from the official Frida GitHub releases page, then push it to the device.
bash
adb push frida-server-16.3.3-android-arm64 /data/local/tmp/frida-server
adb shell "chmod 755 /data/local/tmp/frida-server"
adb shell "su -c /data/local/tmp/frida-server &"Confirm the server is running and reachable from your host.
bash
frida-ps -UIf this command lists running processes on the device, your instrumentation bridge is live. If you're struggling to get consistent results across different device models, SageFox Cybersecurity's team handles this setup as part of every mobile penetration test engagement, so the technical groundwork never becomes a blocker for your assessment timeline.
Blindly running a generic bypass script sometimes works, but a more reliable approach starts with identifying exactly which pinning mechanism the target app uses.
bash
apktool d target-app.apk -o target_outputSearch the decompiled smali or Java-like output for common pinning indicators.
bash
grep -r "CertificatePinner" target_output/
grep -r "X509TrustManager" target_output/
grep -r "checkServerTrusted" target_output/You can also use jadx for a cleaner Java-style view of the decompiled code.
bash
jadx -d jadx_output target-app.apkThis step matters because OkHttp-based pinning, custom TrustManager pinning, and native-layer pinning each require a different Frida hooking strategy. Skipping this reconnaissance step is the most common reason bypass attempts fail on the first try.
The most widely used starting point in the community is a universal bypass script that hooks multiple known pinning classes simultaneously. Below is a working example targeting several common implementations.
javascript
Java.perform(function () {
console.log("[*] Starting universal SSL pinning bypass");
// Bypass OkHttp3 CertificatePinner
var CertificatePinner = Java.use("okhttp3.CertificatePinner");
CertificatePinner.check.overload("java.lang.String", "java.util.List").implementation = function (hostname, certs) {
console.log("[+] OkHttp3 CertificatePinner.check bypassed for " + hostname);
return;
};
// Bypass custom TrustManager checkServerTrusted
var TrustManagerImpl = Java.use("com.android.org.conscrypt.TrustManagerImpl");
TrustManagerImpl.verifyChain.implementation = function (untrustedChain, trustAnchorChain, host, clientAuth, ocspData, tlsSctData) {
console.log("[+] TrustManagerImpl.verifyChain bypassed for " + host);
return untrustedChain;
};
// Bypass HostnameVerifier
var HostnameVerifier = Java.use("javax.net.ssl.HostnameVerifier");
HostnameVerifier.verify.implementation = function (hostname, session) {
console.log("[+] HostnameVerifier bypassed for " + hostname);
return true;
};
});Save this as universal_bypass.js and run it against the target application.
bash
frida -U -f com.target.package -l universal_bypass.js --no-pauseThe --no-pause flag lets the application launch and run naturally while the hooks are applied at startup, which is important since many pinning checks fire during the app's initial network handshake.
Some apps rely heavily on OkHttp's CertificatePinner, especially newer apps built with Retrofit. If the universal script above does not fully work due to overload mismatches, a more targeted hook is often needed.
javascript
Java.perform(function () {
var CertificatePinner = Java.use("okhttp3.CertificatePinner");
var overloads = CertificatePinner.check.overloads;
overloads.forEach(function (overload) {
overload.implementation = function () {
console.log("[+] Overload bypassed: " + overload.argumentTypes.map(t => t.className).join(", "));
};
});
});This approach dynamically loops through every overloaded version of the check method, which is useful when app developers use less common method signatures that a static script might miss.
Some high-security apps, particularly in banking and fintech, implement pinning inside native C/C++ code compiled into .so libraries. This is significantly harder to bypass because there is no Java method to hook directly.
bash
frida-trace -U -i "SSL_CTX_set_verify" com.target.packageThis traces calls to OpenSSL or BoringSSL functions responsible for certificate verification.
javascript
var sslLib = Process.findModuleByName("libssl.so");
var sslVerifyAddr = sslLib.findExportByName("SSL_get_verify_result");
Interceptor.attach(sslVerifyAddr, {
onLeave: function (retval) {
console.log("[+] SSL_get_verify_result forced to X509_V_OK");
retval.replace(0);
}
});Native bypasses like this require a solid understanding of the OpenSSL verification flow, and results vary between BoringSSL and OpenSSL builds. For teams without in-house reverse engineering depth, this is exactly the kind of engagement SageFox Cybersecurity supports directly, working through obfuscated native binaries so your internal team can focus on remediation rather than reverse engineering.
Once your hooks are in place, the real test is whether your intercepting proxy now sees decrypted traffic.
If requests appear in your proxy with full request and response bodies visible, the pinning bypass succeeded. If the app crashes or shows a network error screen, this usually indicates either an incomplete hook (missing an overload or a secondary check) or a native-layer check you have not yet addressed.
This usually happens due to SELinux restrictions or Magisk-based root hiding. Try running Frida server with an alternate name to avoid detection.
bash
adb shell "mv /data/local/tmp/frida-server /data/local/tmp/sagefox-agent"
adb shell "su -c /data/local/tmp/sagefox-agent &"Some apps run root detection and Frida detection checks before the pinning logic even executes. In these cases, you need an additional bypass script targeting root detection libraries such as RootBeer, alongside your SSL pinning hooks.
Double check that you have covered all overloads of the relevant method. Many pinning bypass failures come from developers using a slightly different method signature than the one targeted in a copy-pasted public script.
Consistent methodology matters more than any single script. SageFox Cybersecurity's mobile security team builds custom hook chains for each client engagement rather than relying on generic public scripts, since production apps frequently combine multiple pinning layers to slow down researchers.
SSL pinning bypass using Frida is a foundational skill for anyone performing Android application security assessments. The technique requires understanding where pinning logic lives in an app, whether that is Java-level OkHttp checks, custom TrustManager implementations, or native compiled libraries, and then applying the correct hooking strategy for that layer.
A universal script gets you started quickly, but real-world engagements often demand targeted hooks, native interception, and root detection bypasses working together. If your organization needs a thorough assessment of how your mobile application handles certificate validation and network security controls, SageFox Cybersecurity's mobile penetration testing team can identify these weaknesses before attackers do, delivering a clear remediation path your development team can act on immediately.