Introduction / Overview
Legal & Ethical Disclaimer: This article is for education and authorized security testing only. Intercepting, modifying, or analyzing traffic from applications and accounts you do not own — or do not have explicit written permission to test — is illegal in most jurisdictions. Always operate inside a documented engagement scope (bug bounty program, signed pentest contract, or your own lab).
Modern mobile applications speak to backend APIs over TLS. For a security assessor, that traffic is the most valuable surface: authentication flows, hidden endpoints, IDOR-prone object references, and business logic all flow through it. The standard approach is a man-in-the-middle (MITM) proxy such as Burp Suite. The obstacle is certificate pinning: the app refuses to trust any certificate except the one it expects, so your proxy's CA is rejected and the TLS handshake fails.
This post walks through diagnosing pinning, defeating it with Frida and objection, and routing decrypted traffic into Burp — followed by an equally weighted blue-team section on how defenders detect and harden against exactly this technique.
How it works / Background
A normal MITM works because you install Burp's CA certificate into the device trust store; the app validates the server certificate against the system store and accepts your proxy. Certificate pinning breaks this by hardcoding an expected certificate or public key (a "pin") inside the app and comparing it directly during the handshake. Common implementations:
- OkHttp
CertificatePinner(Android) — pins SHA-256 of the SubjectPublicKeyInfo. - Android Network Security Config —
<pin-set>entries inres/xml/network_security_config.xml. - TrustManager / X509TrustManager custom implementations.
- iOS
NSURLSessiondelegates validatingserverTrust, or libraries like TrustKit.
Because the pin check happens inside the running process, the most reliable bypass is dynamic instrumentation: hook the validation functions at runtime and force them to return "valid." Frida is a binary instrumentation toolkit that injects a JavaScript engine into the target process; objection is a Frida-powered toolkit with pre-built pinning-bypass routines.
Prerequisites / Lab setup
- A rooted Android device or emulator (Genymotion or a Google APIs — not Play Store — AVD image, which is rootable via
adb root). frida-toolson the host andfrida-servermatching the host version on the device.objection, Burp Suite, andadb.
# Host tooling
pip install frida-tools objection
# Match frida-server to the client version
frida --version # e.g. 16.5.6
# Download the matching frida-server-16.5.6-android-arm64.xz from GitHub releases
adb push frida-server /data/local/tmp/
adb shell "chmod 755 /data/local/tmp/frida-server"
adb shell "/data/local/tmp/frida-server &"BashInstall Burp's CA so that non-pinned TLS still works. On Android 7+ user-installed CAs are not trusted by apps by default, so push it as a system CA (requires root):
# Export Burp CA as DER, convert, and hash-rename for the system store
openssl x509 -inform DER -in cacert.der -out burp.pem
HASH=$(openssl x509 -inform PEM -subject_hash_old -in burp.pem | head -1)
cp burp.pem "${HASH}.0"
adb root && adb remount
adb push "${HASH}.0" /system/etc/security/cacerts/
adb shell "chmod 644 /system/etc/security/cacerts/${HASH}.0"BashThen set the device proxy to your host IP and Burp's listening port (e.g. 8080), and make sure Burp listens on all interfaces.
Walkthrough / PoC
Step 1 — Confirm pinning exists. Configure the proxy, open the app, and watch Burp. If non-app browser traffic appears but the app's API calls do not — and the app shows connection errors — pinning is likely active. Confirm by listing the target's process and package:
frida-ps -Uai # list installed apps with identifiersBashStep 2 — Try the objection one-liner. objection wraps a battle-tested universal bypass that hooks OkHttp, TrustManager, and Network Security Config checks:
objection -g com.example.target explore \
--startup-command "android sslpinning disable"BashInside the objection REPL you can also run it interactively:
com.example.target on (Android: 13) [usb] # android sslpinning disable
(agent) Custom SSL pinning bypass hooking enabled.
(agent) [okhttp3] CertificatePinner.check() called, overriding to allow.PlaintextStep 3 — Fall back to a Frida script when objection's generic hooks miss a custom implementation. The community-standard script is frida-multiple-unpinning:
frida -U -f com.example.target \
-l frida-multiple-unpinning.js --no-pauseBashStep 4 — Write a targeted hook when bespoke pinning resists the generic scripts. Decompile the APK first (see Reverse Engineering Android APKs) to find the exact class and method:
// hook-okhttp.js — neutralize OkHttp CertificatePinner
Java.perform(function () {
var CertificatePinner = Java.use('okhttp3.CertificatePinner');
CertificatePinner.check.overload('java.lang.String', 'java.util.List')
.implementation = function (hostname, peerCertificates) {
console.log('[+] Bypassing pinning for: ' + hostname);
return; // skip the check entirely
};
});JavaScriptfrida -U -f com.example.target -l hook-okhttp.js --no-pauseBashStep 5 — Intercept and analyze. Once a hook lands, the app's API requests appear in Burp's HTTP history. From there you can use Repeater to fuzz endpoints, test authorization on object references, and map the API. For broader endpoint discovery, pair this with passive Mobile App OSINT and Endpoint Discovery.
A frequent gotcha: some apps use Flutter, which bundles its own BoringSSL and ignores the system proxy entirely. For those, use Frida to hook ssl_verify_peer_cert in libflutter.so and force the proxy via iptables/ProxyDroid.
Mermaid diagram

The diagram shows the decision path from a failed handshake to a successful interception: try generic bypasses first, and only reverse-engineer a custom hook when those fail.
Detection & Defense (Blue Team)
Pinning bypass is a runtime attack on a rooted, instrumented device. Defenders should assume a motivated attacker can defeat any single client-side control, so layer the following.
1. Detect the instrumentation environment.
- Root/jailbreak detection: check for
su, Magisk, common root paths, and writable system partitions. - Frida detection: scan loaded libraries for
frida-agent, look for the defaultfrida-serverport 27042, and detect theD-Bus AUTHhandshake string in process memory. Frida's named pipes and thread names (gum-js-loop,gmain) are also signals. - Debugger/emulator detection: check
ro.build.fingerprint, QEMU artifacts, andptraceattachment.
// Minimal Frida named-pipe / library check (illustrative)
for (String lib : getLoadedLibraries()) {
if (lib.contains("frida") || lib.contains("gum")) {
reportTamperAndExit();
}
}Java2. Harden the pinning itself. Use two pins (leaf + backup intermediate) so certificate rotation does not brick the app, and prefer Android's declarative network_security_config.xml <pin-set> over hand-rolled TrustManagers, which are easy to mis-hook:
<network-security-config>
<domain-config>
<domain includeSubdomains="true">api.example.com</domain>
<pin-set expiration="2026-01-01">
<pin digest="SHA-256">k3XnEYQCK9...leaf...=</pin>
<pin digest="SHA-256">backuppinABC...=</pin>
</pin-set>
</domain-config>
</network-security-config>XML3. Move trust server-side. Client-side pinning only protects confidentiality of the channel; it does not authenticate the client. Enforce strong server-side authorization, short-lived tokens, mutual TLS (mTLS) with client certificates stored in hardware-backed keystores (Android Keystore / iOS Secure Enclave), and per-request signing (e.g., HMAC over the body with a key derived at runtime) so a proxied request still fails integrity checks.
4. Use attestation. Google Play Integrity API and Apple App Attest / DeviceCheck let the backend reject requests from rooted, emulated, or repackaged clients. Combined with RASP (runtime application self-protection) and code obfuscation (DexGuard, R8 with aggressive shrinking), the cost of a bypass rises sharply.
5. Monitor at the backend. Watch for anomalies that indicate interception/replay: requests from data-center IPs, missing or stale attestation tokens, unusual TLS fingerprints (JA3/JA4 not matching the official app), and high-velocity endpoint enumeration.
Mapping to MITRE ATT&CK for Mobile: this technique relates to T1626/T1404 (privilege escalation via rooting) and the broader "Network Traffic Capture / Manipulation" tradecraft; attestation and integrity checks counter T1633 (Virtualization/Sandbox Evasion) weaknesses.
Conclusion
Certificate pinning is a speed bump, not a wall. With Frida and objection a tester can neutralize generic pinning in seconds and write a targeted hook for custom implementations after a short reverse-engineering pass. Defenders should treat pinning as one layer in a defense-in-depth stack — paired with root/Frida detection, server-side authorization, mTLS, and platform attestation — rather than a single control. The most resilient APIs are those that never trust the client in the first place.
References
- MITRE ATT&CK for Mobile — https://attack.mitre.org/matrices/mobile/
- OWASP MASTG, "Testing Network Communication" — https://mas.owasp.org/MASTG/
- Frida documentation — https://frida.re/docs/home/
- objection — https://github.com/sensepost/objection
- HackTricks, Android SSL Pinning Bypass — https://book.hacktricks.xyz/mobile-pentesting/android-app-pentesting
- Android Network Security Configuration — https://developer.android.com/privacy-and-security/security-config
- Google Play Integrity API — https://developer.android.com/google/play/integrity
You Might Also Like
If you found this useful, these related deep-dives cover adjacent techniques and their defenses:



Comments