Certificate Pinning Bypass
Certificate pinning is a security mechanism that restricts which certificates are trusted for a particular connection. Bypassing it is essential for intercepting HTTPS traffic during mobile app pentesting.
What is Certificate Pinning?
Types of Certificate Pinning
Certificate Pinning
Pins the entire certificate. If the certificate changes (e.g., renewal), the pin must be updated.
Public Key Pinning
Pins only the public key (SPKI). The same key can be used across certificate renewals.
Android Bypass Techniques
1. Frida Universal Bypass
The most reliable method for bypassing certificate pinning on Android:
// Universal Android SSL Pinning Bypass
Java.perform(function() {
// TrustManagerImpl bypass
var TrustManagerImpl = Java.use('com.android.org.conscrypt.TrustManagerImpl');
TrustManagerImpl.verifyChain.implementation = function(untrustedChain, trustAnchorChain, host, clientAuth, ocspData, tlsSctData) {
console.log('[+] Bypassing TrustManagerImpl.verifyChain for: ' + host);
return untrustedChain;
};
// OkHttp3 CertificatePinner bypass
try {
var CertificatePinner = Java.use('okhttp3.CertificatePinner');
CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function(hostname, peerCertificates) {
console.log('[+] Bypassing OkHttp3 CertificatePinner for: ' + hostname);
return;
};
} catch(e) {
console.log('[-] OkHttp3 not found');
}
// Retrofit/OkHttp bypass
try {
var OkHttpClient = Java.use('okhttp3.OkHttpClient$Builder');
OkHttpClient.certificatePinner.implementation = function(certificatePinner) {
console.log('[+] Bypassing OkHttpClient.certificatePinner');
return this;
};
} catch(e) {
console.log('[-] OkHttpClient not found');
}
// Apache HTTP client (legacy)
try {
var HttpsURLConnection = Java.use('javax.net.ssl.HttpsURLConnection');
HttpsURLConnection.setDefaultHostnameVerifier.implementation = function(hostnameVerifier) {
console.log('[+] Bypassing HttpsURLConnection hostname verifier');
return;
};
} catch(e) {}
});Run with Frida:
frida -U -f com.target.app -l android_ssl_bypass.js --no-pause2. Objection Bypass
Quick bypass using objection:
# Start objection
objection -g com.target.app explore
# Inside objection shell:
android sslpinning disable
# Or spawn with bypass enabled
objection -g com.target.app explore --startup-command "android sslpinning disable"3. Network Security Config (Android 7+)
Modify the app to trust user certificates by editing the APK:
# Decompile APK
apktool d target.apk -o target_decompiled
# Create/edit res/xml/network_security_config.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config>
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
</trust-anchors>
</base-config>
</network-security-config>
# Add to AndroidManifest.xml in <application> tag:
# android:networkSecurityConfig="@xml/network_security_config"
# Rebuild and sign
apktool b target_decompiled -o target_patched.apk
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my.keystore target_patched.apk alias_name
zipalign -v 4 target_patched.apk target_final.apkiOS Bypass Techniques
1. Frida iOS Bypass
// iOS Universal SSL Pinning Bypass
if (ObjC.available) {
// Bypass NSURLSession
var NSURLSession = ObjC.classes.NSURLSession;
Interceptor.attach(NSURLSession['- URLSession:didReceiveChallenge:completionHandler:'].implementation, {
onEnter: function(args) {
console.log('[+] Intercepting NSURLSession challenge');
var completionHandler = new ObjC.Block(args[4]);
var NSURLSessionAuthChallengeUseCredential = 0;
completionHandler.implementation = function(disposition, credential) {
console.log('[+] Accepting credential');
completionHandler(NSURLSessionAuthChallengeUseCredential, null);
};
}
});
// Bypass AFNetworking
try {
var AFSecurityPolicy = ObjC.classes.AFSecurityPolicy;
AFSecurityPolicy['- setSSLPinningMode:'].implementation = function(mode) {
console.log('[+] Bypassing AFNetworking pinning mode');
this.setSSLPinningMode_(0);
};
AFSecurityPolicy['- setAllowInvalidCertificates:'].implementation = function(allow) {
console.log('[+] Allowing invalid certificates');
this.setAllowInvalidCertificates_(1);
};
} catch(e) {
console.log('[-] AFNetworking not found');
}
// Bypass TrustKit
try {
var TrustKit = ObjC.classes.TrustKit;
TrustKit['+ setUpSharedInstanceWithConfiguration:'].implementation = function(config) {
console.log('[+] Bypassing TrustKit initialization');
return;
};
} catch(e) {
console.log('[-] TrustKit not found');
}
}2. Objection iOS Bypass
# Connect to iOS app
objection -g com.target.app explore
# Disable SSL pinning
ios sslpinning disable
# Hook specific pinning methods
ios sslpinning disable --quiet3. SSL Kill Switch 2 (Cydia)
On jailbroken devices, install SSL Kill Switch 2 from Cydia to globally disable certificate validation.
# Add repository in Cydia:
https://repo.rpetri.ch/beta/
# Or manually install deb:
dpkg -i com.nablac0d3.sslkillswitch2_0.14.deb
killall -9 SpringBoardBypassing Specific Implementations
| Library | Platform | Bypass Method |
|---|---|---|
| OkHttp3 | Android | Hook CertificatePinner.check() |
| Retrofit | Android | Hook OkHttp (uses OkHttp internally) |
| AFNetworking | iOS | Hook AFSecurityPolicy methods |
| TrustKit | iOS | Hook TSKPinVerifier |
| Flutter | Both | Patch ssl_x509.cc in libflutter.so |
| React Native | Both | Hook native HTTP client (OkHttp/NSURLSession) |
Proxy Configuration
Before Bypassing
Android CA Install
# Export Burp CA as DER
# Convert to PEM
openssl x509 -inform DER -in cacert.der -out cacert.pem
# Get hash
openssl x509 -inform PEM -subject_hash_old -in cacert.pem | head -1
# Rename and push
mv cacert.pem 9a5ba575.0
adb root
adb remount
adb push 9a5ba575.0 /system/etc/security/cacerts/
adb shell chmod 644 /system/etc/security/cacerts/9a5ba575.0iOS CA Install
# 1. Export Burp CA
# 2. Host on local server:
python3 -m http.server 8080
# 3. On iOS device:
# Navigate to http://YOUR_IP:8080/cert.der
# Install profile in Settings
# 4. Trust certificate:
# Settings > General > About >
# Certificate Trust Settings >
# Enable "Burp Certificate"Troubleshooting
Bypass not working?
- • Check if the app uses a custom HTTP library
- • Try multiple bypass scripts in combination
- • Look for obfuscated pinning code
- • Check if the app has root/jailbreak detection
App crashes after bypass?
- • The bypass script might be too aggressive
- • Try a more targeted approach for specific libraries
- • Check Frida console for errors
- • App might have integrity checks