Certificate Pinning Bypass
SSL/TLS certificate pinning is a critical defense mechanism that protects mobile applications from man-in-the-middle attacks by ensuring the app only trusts specific, pre-defined certificates or public keys. When pinning is bypassed or poorly implemented, attackers can intercept, read, and modify all encrypted traffic between the app and its backend servers, completely undermining the security of HTTPS.
What Is Certificate Pinning Bypass?
Certificate pinning is a security technique where a mobile application is configured to only accept a specific certificate or public key when establishing an HTTPS connection with its backend server. Instead of relying solely on the device's trust store, which contains hundreds of Certificate Authority (CA) certificates, the app hardcodes or embeds the expected certificate fingerprint or public key hash. This means that even if an attacker obtains a valid certificate from a trusted CA for the target domain, the app will reject it because it does not match the pinned certificate.
Certificate pinning bypass refers to the techniques attackers use to disable or circumvent this protection. On iOS, attackers commonly use dynamic instrumentation frameworks like Frida or Objection to hook into the app's SSL/TLS validation methods at runtime, replacing the pinning logic with code that accepts any certificate. Alternatively, attackers may use static analysis to locate and patch the pinning checks directly in the app binary, or they may exploit weaknesses in the pinning implementation such as incomplete validation, missing backup pins, or failure to pin intermediate certificates.
The consequences of a successful pinning bypass are severe. Once an attacker can intercept HTTPS traffic, they gain full visibility into API calls, authentication tokens, session cookies, personal data, and any other information transmitted between the app and server. This is particularly dangerous for financial applications, healthcare apps, and any software that handles sensitive user data. The OWASP Mobile Top 10 categorizes this under M5 (Insecure Communication), recognizing it as one of the most critical mobile security risks.
How It Works
The developer configures the app to validate the server's SSL/TLS certificate against a pinned certificate or public key hash. On iOS, this is typically done through a URLSessionDelegate that implements urlSession(_:didReceive:completionHandler:) to check the server's certificate chain against known-good values.
The attacker downloads the app's IPA file from the App Store or extracts it from a jailbroken device. They may also set up a proxy server (such as Burp Suite or mitmproxy) and configure the device to route all traffic through it, observing that HTTPS connections fail due to certificate pinning.
Using tools like Frida, the attacker injects JavaScript code into the running app process to hook the SSL validation methods and force them to always return success. With Objection, a single command like ios sslpinning disable automates this process. Alternatively, the attacker may decompile the binary and patch out the pinning checks entirely.
With pinning disabled, the app's SSL/TLS validation falls back to the system's default behavior, which trusts any certificate signed by a CA in the device's trust store. The attacker installs their proxy's CA certificate on the device, and the app happily establishes a "secure" connection through the attacker's proxy.
The proxy decrypts incoming traffic from the app, logs it in plaintext, optionally modifies it, and then re-encrypts it before forwarding it to the real server. The attacker now has full visibility into API requests and responses, including authentication tokens, personal data, and business logic, enabling credential theft, session hijacking, and API abuse.
Vulnerable Code Example
import Foundation
class APIService: NSObject, URLSessionDelegate {
func fetchUserData(completion: @escaping (Data?) -> Void) {
let url = URL(string: "https://api.example.com/user/profile")!
let session = URLSession(
configuration: .default,
delegate: self,
delegateQueue: nil
)
let task = session.dataTask(with: url) { data, _, _ in
completion(data)
}
task.resume()
}
// VULNERABLE: This delegate method accepts ALL certificates
// without any validation, completely disabling TLS security
func urlSession(
_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void
) {
// Blindly trust any server certificate
if let serverTrust = challenge.protectionSpace.serverTrust {
let credential = URLCredential(trust: serverTrust)
completionHandler(.useCredential, credential)
}
}
}
// This implementation accepts ANY certificate presented by the server,
// including self-signed certificates from an attacker's proxy.
// There is no pinning, no certificate validation, and no chain
// verification. An attacker with network access can trivially
// perform a man-in-the-middle attack.Secure Code Example
import Foundation
import CommonCrypto
class SecureAPIService: NSObject, URLSessionDelegate {
// Pinned public key hashes (SHA-256, base64-encoded)
// Include a backup pin for certificate rotation
private let pinnedHashes: Set<String> = [
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=", // Primary
"CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC=" // Backup
]
func fetchUserData(completion: @escaping (Data?) -> Void) {
let url = URL(string: "https://api.example.com/user/profile")!
let session = URLSession(
configuration: .default,
delegate: self,
delegateQueue: nil
)
let task = session.dataTask(with: url) { data, _, _ in
completion(data)
}
task.resume()
}
// SECURE: Validate the server certificate against pinned hashes
func urlSession(
_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void
) {
guard challenge.protectionSpace.authenticationMethod
== NSURLAuthenticationMethodServerTrust,
let serverTrust = challenge.protectionSpace.serverTrust
else {
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
// Evaluate the trust chain against system CA roots
let policy = SecPolicyCreateSSL(true, challenge.protectionSpace.host as CFString)
SecTrustSetPolicies(serverTrust, policy)
var error: CFError?
guard SecTrustEvaluateWithError(serverTrust, &error) else {
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
// Extract and hash the server's public key
let certCount = SecTrustGetCertificateCount(serverTrust)
var pinMatched = false
for i in 0..<certCount {
guard let cert = SecTrustCopyCertificateChain(serverTrust)
.map({ ($0 as! [SecCertificate])[i] }),
let publicKey = SecCertificateCopyKey(cert),
let publicKeyData = SecKeyCopyExternalRepresentation(publicKey, nil)
else { continue }
let keyHash = sha256(data: publicKeyData as Data)
if pinnedHashes.contains(keyHash) {
pinMatched = true
break
}
}
if pinMatched {
let credential = URLCredential(trust: serverTrust)
completionHandler(.useCredential, credential)
} else {
// Pin validation failed — reject the connection
completionHandler(.cancelAuthenticationChallenge, nil)
reportPinningFailure(host: challenge.protectionSpace.host)
}
}
private func sha256(data: Data) -> String {
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
data.withUnsafeBytes { buffer in
_ = CC_SHA256(buffer.baseAddress, CC_LONG(data.count), &hash)
}
return Data(hash).base64EncodedString()
}
private func reportPinningFailure(host: String) {
// Log pinning failure to server for monitoring
print("Certificate pinning failed for host: \(host)")
}
}
// This implementation validates the server's public key hash
// against a set of known-good pinned hashes. Even if an attacker
// has a valid CA-signed certificate, the connection is rejected
// unless the public key matches one of the pinned values.Types of Certificate Pinning Bypass
Static Analysis Bypass
The attacker decompiles or disassembles the app binary to locate the certificate pinning logic, then patches it out before repackaging and re-signing the app. On iOS, tools like Hopper Disassembler or IDA Pro are used to find the URLSessionDelegate methods or third-party pinning library calls. The attacker modifies the binary to NOP out (replace with no-operation instructions) the validation checks, or changes conditional branches so the pinning check always succeeds. This approach produces a permanently modified app that never validates certificates.
Dynamic Instrumentation
The most common bypass technique, using frameworks like Frida or Objection to hook into the running app's process and override SSL/TLS validation methods at runtime. Frida injects a JavaScript engine into the target process, allowing the attacker to intercept function calls and modify their behavior without changing the app binary. For iOS, this involves hooking methods like SecTrustEvaluateWithError or URLSession:didReceiveChallenge: to always return a successful validation result. This technique requires a jailbroken device or developer provisioning.
Certificate Manipulation
This approach targets the device's trust store rather than the app itself. On jailbroken iOS devices, attackers can install custom CA certificates as trusted root authorities and manipulate the system-level certificate validation. Some attacks exploit weaknesses in how the app handles certificate chains, such as only pinning the leaf certificate but not intermediate CAs, or failing to validate the entire chain. Attackers may also exploit certificate transparency gaps or leverage compromised subordinate CAs to generate certificates that pass partial validation checks.
Impact
A successful certificate pinning bypass gives an attacker full access to the encrypted communication channel between a mobile app and its backend servers. The severity depends on the sensitivity of the data transmitted and the attacker's objectives.
Once pinning is bypassed, the attacker can intercept all HTTPS traffic in plaintext. This includes personal information, financial data, health records, private messages, and any other sensitive data the app transmits. The user has no indication that their traffic is being intercepted, as the connection still appears secure from the app's perspective.
Attackers can capture login credentials, OAuth tokens, session cookies, and API keys as they are transmitted between the app and server. With these credentials, the attacker can impersonate the user, access their account from any device, and perform actions on their behalf without their knowledge.
Mobile apps often embed API keys, client secrets, and authentication tokens in their network requests. By intercepting traffic, attackers can extract these secrets and use them to access backend APIs directly, bypass rate limiting, or abuse paid services. This is especially damaging for apps that rely on API keys for billing or access control.
Beyond passive interception, the attacker can actively modify requests and responses in transit. They can alter transaction amounts, change recipient addresses, inject malicious content into server responses, bypass client-side validation, and manipulate the app's behavior in ways the developers never intended. This effectively gives the attacker full control over the app's communication layer.
Prevention Checklist
Pin the Subject Public Key Info (SPKI) hash rather than the entire certificate. Public key pinning survives certificate renewals as long as the same key pair is used. Use SHA-256 hashes of the public key and validate them in your URLSessionDelegate implementation. This is the most resilient form of pinning.
Always include at least one backup pin for a key that is not yet in production. This ensures the app continues to work if you need to rotate certificates due to compromise or expiration. Without backup pins, a certificate rotation could lock all users out of the app until they update.
Verify that server certificates have been logged in public Certificate Transparency (CT) logs. This helps detect misissued or fraudulent certificates. On iOS, you can leverage Apple's built-in CT enforcement and additionally validate CT signed certificate timestamps (SCTs) in your app's pinning logic.
Implement jailbreak detection to identify compromised environments where dynamic instrumentation tools can run freely. Check for common jailbreak artifacts such as Cydia, Sileo, unauthorized file system access, and the ability to fork processes. While not foolproof, this raises the bar significantly for attackers attempting to use Frida or similar tools.
Implement runtime protections that detect when the app's code has been modified or when a debugger or instrumentation framework is attached. Use techniques like code signing validation, inline integrity checks, and anti-Frida detection to make dynamic hooking significantly more difficult. Commercial solutions like Guardsquare or Zimperium provide comprehensive protection.
When a pinning validation fails, report it to a dedicated server endpoint. Track the frequency, geographic distribution, and device characteristics of pinning failures. A sudden spike in failures from a specific region or device type may indicate an active attack. Use this telemetry to trigger additional security measures or alert your security team.
Real-World Examples
Tesco Bank Mobile App
Security researchers discovered that the Tesco Bank iOS and Android apps had weak certificate pinning implementations. Attackers exploited this weakness as part of a broader campaign that led to fraudulent transactions affecting approximately 9,000 customer accounts, with losses totaling around 2.5 million pounds. The UK Financial Conduct Authority later fined Tesco Bank 16.4 million pounds for failing to protect its customers.
Uber Certificate Pinning Flaw
Security researchers demonstrated that Uber's mobile app had a certificate pinning implementation that could be bypassed using Frida. By hooking into the app's SSL validation methods, researchers could intercept all traffic between the app and Uber's servers, exposing rider locations, payment information, and driver details. Uber subsequently strengthened their pinning implementation and added additional anti-tampering protections.
Indian Banking Apps
A comprehensive security audit by researchers at CIST revealed that multiple major Indian banking apps, including those from SBI, ICICI, and Axis Bank, had either missing or poorly implemented certificate pinning. Attackers could intercept login credentials and banking transactions using simple proxy tools after a trivial Frida-based bypass. The Reserve Bank of India subsequently issued guidelines mandating stronger mobile security controls.
Clubhouse API Exposure
Researchers bypassed certificate pinning in the Clubhouse social audio app to reveal that the app was transmitting user IDs and chatroom data to servers hosted by Agora, a Chinese company, without adequate encryption or pinning protections. This raised significant privacy concerns, as the intercepted traffic could potentially allow user conversations to be monitored. The incident prompted Clubhouse to implement stronger pinning and encryption measures.
Ready to Test Your Knowledge?
Put what you have learned into practice. Try identifying and fixing certificate pinning vulnerabilities in our interactive coding challenges, or explore more security guides to deepen your understanding.