Certificate Pinning Bypass Techniques in Mobile API Clients

Attackers can bypass mobile certificate pinning by targeting the trust-decision layer.

· 8 min read
Cover illustration for “Certificate Pinning Bypass Techniques in Mobile API Clients”
Cryptographic Failures · October 27, 2026 · 8 min read · 1,782 words

The bypass must reach the trust-decision layer. Standard tools already cover much of that ground, so pinning is client-side hardening. The server must remain secure even when the caller has removed every check in the app.

Why certificate pinning is not the security boundary engineers think it is

With pinning, normal CA chain validation still runs, but the app adds a check against an embedded certificate or key value. A mismatch aborts the connection. So installing Burp Suite’s CA in the system trust store is not enough for it to intercept traffic. That establishes only that the app rejects an unexpected certificate in that environment.

Pinning guards against a rogue CA that issues a certificate that passes normal chain validation. That happened in DigiNotar’s 2011 compromise, when fraudulent certificates were issued for Google, Skype, and CIA domains. But pinning cannot stop direct server compromise or a device owner who changes the client’s trust decision. The dangerous gap is between environments. Internal tests exercise the shipped app, while a tester can run a repackaged APK with Frida Gadget inside it.

How pinning is implemented — and why the implementation layer determines which bypass works

Android has three main implementation layers in this map. Older apps commonly make the Java-level trust decision through javax.net.ssl.TrustManager or X509TrustManager. Modern apps often use OkHttp3’s CertificatePinner.check() instead. Native implementations move the check into Conscrypt or BoringSSL inside a .so library. That changes the work required, because a Java hook cannot be assumed to reach a C-level decision that needs static analysis before instrumentation.

The iOS map has four layers, but they overlap. NSAppTransportSecurity in Info.plist governs CA restrictions and cleartext exceptions, while a NSURLSession delegate can manually validate certificates through didReceiveChallenge. TrustKit, Alamofire, and AFNetworking add their own logic above the OS implementation. Below them, Apple’s Security.framework has native functions such as SecTrustEvaluate and SecTrustEvaluateWithError. These shared functions give testers a lower point at which to intervene.

The active layer determines which bypass fits. Static analysis of the binary or decompiled source identifies that layer. Automated hooks can still work without manual analysis because they cover several common implementations at once. When they fail, however, repeating the same command does not resolve the mismatch.

The one-command bypass that defeats most Android apps

In a prepared Gadget environment, a tester opens objection --gadget com.target.app explore and runs android sslpinning disable. The command hooks OkHttp’s CertificatePinner.check(), X509TrustManagerExtensions, and the native OpenSSL layer in one pass. This automated path works for most production Android apps. The bypass itself needs no custom scripting or manual analysis, though the app still has to be prepared to run Gadget.

The pcipolloni universal-android-ssl-pinning-bypass script approaches Java trust differently. It hooks SSLContext.init() and substitutes a custom TrustManager that trusts a user-supplied CA certificate. This changes the trust setup rather than directly overwriting checkServerTrusted(). Apps with BoringSSL or Conscrypt pinning at the C layer can require a separate native Frida hook targeting the correct function offset after static analysis. OWASP MASTG-TECH-0012 also documents the Xposed modules TrustMeAlready and SSLUnpinning as alternatives when Frida is unavailable.

iOS pinning converges on one framework, which is why one Frida script defeats four layers

Upper-layer implementations eventually reach native Security.framework functions such as SecTrustEvaluate, SecTrustEvaluateWithError, and SecCertificateCopyData. That convergence lets one well-written Frida script defeat checks in Alamofire, URLSession, and Apple’s internal AKCertificatePinning. Four implementation layers do not mean four independent barriers. A hook below the app’s chosen library can change the result on which that library relies.

The ios-ssl-pinning-bypass.js script, tested with Frida 17.8.3 on iOS 17.4.1, hooks ten points across trust evaluation, custom verification, protocol verification, and library-specific checks. Objection’s ios sslpinning disable covers roughly the first three layers in standard apps. When it reports success but Burp receives nothing from certain endpoints, a custom Security.framework delegate or Network.framework needs targeted instrumentation. Older paths are no refuge either. SecTrustEvaluate was deprecated in iOS 12, and its hook points are well documented.

For apps without device-attestation or biometric requirements, Frida can attach directly in the iOS simulator without jailbreak or re-signing. The tester still targets the trust decision, but the test environment can make reaching it much simpler.

Non-rooted and non-jailbroken devices: how Frida Gadget eliminates the root requirement

Root is not a prerequisite for changing app behavior. Frida Gadget runs inside a repackaged app’s own process, so it needs no system-level access. On Android, the preparation uses apktool to decompile the APK, places frida-gadget in the native library directory, and re-signs with jarsigner or apksigner. A tester can do that work during a typical pentesting session on a standard laptop.

Some apps use PackageManager.getPackageInfo() and Signature comparisons to check their signing certificate at runtime, and the changed certificate can break backend authentication. But that local check is another hook target. A separate Frida script can make it return the expected certificate while the pinning bypass runs. This defeats only the app’s own comparison.

The iOS non-jailbreak route uses Gadget injection with jailbreak-check patching. Because no Frida server process runs on the device, detection routines looking for that process have nothing to observe. The distinction is narrow but important for threat modeling. A device need not be jailbroken for an altered app to host instrumentation.

Flutter apps: why standard bypass scripts fail and what replaces them

Flutter presents a distinct networking problem. Its Dart HTTP stack uses dart:io HttpClient backed by BoringSSL. It also ignores system proxy configuration and certificates installed in the system trust store. Java-level Frida hooks therefore miss the relevant code. Objection’s Android pinning command, Xposed modules, and the pcipolloni TrustManager replacement do not reach this path.

The same separation can invalidate the defender’s configuration. Pinning placed in Android’s network_security_config.xml is not read by Flutter’s Dart HTTP stack. An app can therefore contain plausible pinning settings that protect none of its Dart traffic. This is not a sophisticated bypass at all.

Correctly pinned Flutter traffic requires a different toolchain. Where reFlutter fails, the fallback is manual binary patching with Ghidra or IDA Pro, or a native Frida hook targeting ssl_crypto_x509_session_verify_cert_chain() inside libflutter.so. This is where the work shifts toward expertise. The standard scripts fail because they target the wrong stack, while native analysis reaches the code making the decision.

Mutual TLS: why generic "trust-all" hooks break mTLS apps and what to do instead

Mutual TLS exposes a separate mistake in generic bypass scripts. SSLContext.init(KeyManager[], TrustManager[], SecureRandom) controls both the server-certificate check and the client credentials presented to the server. TrustManager[] validates the server, while KeyManager[] presents the client certificate and private key. Replacing both arrays can make the app accept Burp’s certificate while preventing it from presenting its own.

The handshake then fails for a different reason. That failure does not show that server-certificate pinning survived the hook. For an mTLS test, the correct strategy is to preserve the original KeyManager[] and replace only TrustManager[]. The client can then present its certificate normally while server-certificate validation is removed.

Pinning that is configured but never executes: the silent failure class

Some apps need no active pinning bypass at all. A development configuration disables SSL verification for convenience and survives into production, leaving reviewed pinning code structurally inert. The control exists in source but not in execution. Dependency flaws can create the same disconnect even when the application team intended to enforce the pin.

The curl advisory for CVE-2026-80230 describes one such failure with OpenSSL and its forks, including BoringSSL, AWS-LC, LibreSSL, and QuicTLS. When CURLOPT_PINNEDPUBLICKEY is combined with CURLOPT_SSL_VERIFYPEER=0 and CURLOPT_SSL_VERIFYHOST=0, libcurl silently fails to enforce public key pinning. The affected range is curl 7.45.0 through 8.21.0. The fix shipped in curl 8.22.0 on September 2, 2026. Stanislav Fort of Aisle Research reported the flaw.

The curl advisory for CVE-2026-87866, published October 14, 2026, describes a different failure when curl is built with wolfSSL. An attacker manipulates the certificate’s validity length field so that curl’s later parsing extracts a different embedded key from the one involved in the TLS connection. The pin is checked against the wrong key. So an unauthenticated MITM attacker can send a crafted certificate whose pinned key hash passes the check even though the TLS peer does not have that key.

What engineers pin incorrectly and why the operational choice matters as much as the bypass

The pinning target also determines how fragile deployment becomes. Most production apps pin the leaf certificate DER hash, which breaks on certificate rotation, rather than the SPKI hash that can survive rotation without an app update. Intuitive examples help perpetuate that choice. Javadoc examples and widely shared samples lead developers to choose certificate-level pinning because the target is easy to understand.

Browsers already ran a version of this experiment. Chrome deprecated HTTP Public Key Pinning in May 2018 with version 67 and removed it in Chrome 72. The Blink team said pinning could brick sites and added little to what Certificate Transparency already did. RansomPKP showed how an attacker with server-level access could use a valid HPKP header to pin an attacker-controlled key and block certificate rotation for the full max-age duration. Pinning could turn temporary access into a prolonged lockout.

Certificate Transparency addresses fraudulent issuance through public logs. Fraudulent certificates appear in those logs within 24 hours. Chrome began enforcing CT in July 2018 with Chrome 68 for certificates issued after April 30, 2018. HSTS addresses a different part of public Wi-Fi MITM risk by blocking HTTP downgrade attempts. These controls have distinct jobs, so their benefits extend beyond whether an app rejects a testing proxy.

What server-side controls must hold once pinning is bypassed

The server must assume the client is already understood and altered. That means an attacker has decompiled the binary, extracted API keys and endpoint structures, bypassed pinning, and started calling endpoints from a script. Authentication, authorization, rate limiting, input validation, and anomaly detection must hold at the API layer under that assumption. None can be delegated to the app.

Attestation adds a server-verified identity check to this model. Play Integrity API on Android and App Attest on iOS use server-to-server attestation with tokens signed by Google and Apple respectively. Local hooks cannot forge those signed tokens. If a backend requires valid Play Integrity attestation tied to package name and signing certificate together with the API key, a repackaged APK presents the wrong identity. Accepting basic-level verdicts as the security control leaves the attack surface exploited by Gadget unresolved.

A rigorous mobile API penetration test bypasses pinning and then tests every endpoint as an authenticated-but-adversarial caller. Its useful findings concern what that caller can access, change, or abuse after the client checks are gone. Pinning can slow the tester’s arrival at that point, but the API must be secure when the tester gets there.

Sources

  1. MASTG-TECH-0012: Bypassing Certificate Pinning - OWASP Mobile Application Security

More in Cryptographic Failures