Skip to content

Instantly share code, notes, and snippets.

@coaxial
Last active May 3, 2024 20:06
Show Gist options
  • Save coaxial/f08314685685b14dbce339e57ef51518 to your computer and use it in GitHub Desktop.
Save coaxial/f08314685685b14dbce339e57ef51518 to your computer and use it in GitHub Desktop.
unpinning SSL certs on Android apps with Frida to inspect network traffic with mitmproxy

Most of the time, applications won't pin the certificate. Running mitmproxy and passing all Android traffic through it is as simple as adb connect <IP> && adb shell settings put global http_proxy <mitmproxy host>:<mitmproxy port> (or use Android's UI)

Some applications, however, pin the certificate and will refuse to do any network calls if using mitmproxy.

Luckily, Frida is here!

This assumes Android x86 is running in a VM, that you are a developer in Android (tap the build version enough times), adb debugging is enabled, and that android tools are installed on the host.

  • start mitmproxy on host
  • visit mitm.it on the target (after setting the proxy) and install the spoofed cert
  • start adb adb connect <android vm IP>
  • install the app either in the play store, or with adb push myapp.apk && adb install mypapp.apk (see apk-dl.com for getting the APK)
  • get frida server (https://github.com/frida/frida/releases)
  • run it on android: adb root && adb push <frida-server binary> /data/local/tmp/frida-server && adb shell chmod 755 /data/local/tmp/frida-server && adb shell /data/local/tmp/frida-server --listen 0.0.0.0 &
  • find out the package name by running on the host adb shell 'pm list packages -f | grep myapp'
  • run it frida --codeshare akabe1/frida-multiple-unpinning -U -f com.example.myapp --no-pause

And you're done, traffic now shows up in mitmproxy and you can start poking around.

These instructions are based on this guide: https://v0x.nl/articles/bypass-ssl-pinning-android, updated with a working unpinning script and up to date frida versions.

// Copied from https://codeshare.frida.re/@akabe1/frida-multiple-unpinning/ at the time of writing this gist.
// Copied here in case codeshare ever goes down/disapears.
/* Android ssl certificate pinning bypass script for various methods
by Maurizio Siddu
Run with:
frida -U -f [APP_ID] -l frida_multiple_unpinning.js --no-pause
*/
setTimeout(function() {
Java.perform(function () {
console.log('');
console.log('======');
console.log('[#] Android Bypass for various Certificate Pinning methods [#]');
console.log('======');
var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager');
var SSLContext = Java.use('javax.net.ssl.SSLContext');
// TrustManager (Android < 7)
var TrustManager = Java.registerClass({
// Implement a custom TrustManager
name: 'dev.asd.test.TrustManager',
implements: [X509TrustManager],
methods: {
checkClientTrusted: function (chain, authType) {},
checkServerTrusted: function (chain, authType) {},
getAcceptedIssuers: function () {return []; }
}
});
// Prepare the TrustManager array to pass to SSLContext.init()
var TrustManagers = [TrustManager.$new()];
// Get a handle on the init() on the SSLContext class
var SSLContext_init = SSLContext.init.overload(
'[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom');
try {
// Override the init method, specifying the custom TrustManager
SSLContext_init.implementation = function(keyManager, trustManager, secureRandom) {
console.log('[+] Bypassing Trustmanager (Android < 7) request');
SSLContext_init.call(this, keyManager, TrustManagers, secureRandom);
};
} catch (err) {
console.log('[-] TrustManager (Android < 7) pinner not found');
//console.log(err);
}
// OkHTTPv3 (double bypass)
try {
var okhttp3_Activity = Java.use('okhttp3.CertificatePinner');
okhttp3_Activity.check.overload('java.lang.String', 'java.util.List').implementation = function (str) {
console.log('[+] Bypassing OkHTTPv3 {1}: ' + str);
return true;
};
// This method of CertificatePinner.check could be found in some old Android app
okhttp3_Activity.check.overload('java.lang.String', 'java.security.cert.Certificate').implementation = function (str) {
console.log('[+] Bypassing OkHTTPv3 {2}: ' + str);
return true;
};
} catch (err) {
console.log('[-] OkHTTPv3 pinner not found');
//console.log(err);
}
// Trustkit (triple bypass)
try {
var trustkit_Activity = Java.use('com.datatheorem.android.trustkit.pinning.OkHostnameVerifier');
trustkit_Activity.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession').implementation = function (str) {
console.log('[+] Bypassing Trustkit {1}: ' + str);
return true;
};
trustkit_Activity.verify.overload('java.lang.String', 'java.security.cert.X509Certificate').implementation = function (str) {
console.log('[+] Bypassing Trustkit {2}: ' + str);
return true;
};
var trustkit_PinningTrustManager = Java.use('com.datatheorem.android.trustkit.pinning.PinningTrustManager');
trustkit_PinningTrustManager.checkServerTrusted.implementation = function () {
console.log('[+] Bypassing Trustkit {3}');
};
} catch (err) {
console.log('[-] Trustkit pinner not found');
//console.log(err);
}
// TrustManagerImpl (Android > 7)
try {
var TrustManagerImpl = Java.use('com.android.org.conscrypt.TrustManagerImpl');
TrustManagerImpl.verifyChain.implementation = function (untrustedChain, trustAnchorChain, host, clientAuth, ocspData, tlsSctData) {
console.log('[+] Bypassing TrustManagerImpl (Android > 7): ' + host);
return untrustedChain;
};
} catch (err) {
console.log('[-] TrustManagerImpl (Android > 7) pinner not found');
//console.log(err);
}
// Appcelerator Titanium
try {
var appcelerator_PinningTrustManager = Java.use('appcelerator.https.PinningTrustManager');
appcelerator_PinningTrustManager.checkServerTrusted.implementation = function () {
console.log('[+] Bypassing Appcelerator PinningTrustManager');
};
} catch (err) {
console.log('[-] Appcelerator PinningTrustManager pinner not found');
//console.log(err);
}
// OpenSSLSocketImpl Conscrypt
try {
var OpenSSLSocketImpl = Java.use('com.android.org.conscrypt.OpenSSLSocketImpl');
OpenSSLSocketImpl.verifyCertificateChain.implementation = function (certRefs, JavaObject, authMethod) {
console.log('[+] Bypassing OpenSSLSocketImpl Conscrypt');
};
} catch (err) {
console.log('[-] OpenSSLSocketImpl Conscrypt pinner not found');
//console.log(err);
}
// OpenSSLEngineSocketImpl Conscrypt
try {
var OpenSSLEngineSocketImpl_Activity = Java.use('com.android.org.conscrypt.OpenSSLEngineSocketImpl');
OpenSSLSocketImpl_Activity.verifyCertificateChain.overload('[Ljava.lang.Long;', 'java.lang.String').implementation = function (str1, str2) {
console.log('[+] Bypassing OpenSSLEngineSocketImpl Conscrypt: ' + str2);
};
} catch (err) {
console.log('[-] OpenSSLEngineSocketImpl Conscrypt pinner not found');
//console.log(err);
}
// OpenSSLSocketImpl Apache Harmony
try {
var OpenSSLSocketImpl_Harmony = Java.use('org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl');
OpenSSLSocketImpl_Harmony.verifyCertificateChain.implementation = function (asn1DerEncodedCertificateChain, authMethod) {
console.log('[+] Bypassing OpenSSLSocketImpl Apache Harmony');
};
} catch (err) {
console.log('[-] OpenSSLSocketImpl Apache Harmony pinner not found');
//console.log(err);
}
// PhoneGap sslCertificateChecker (https://github.com/EddyVerbruggen/SSLCertificateChecker-PhoneGap-Plugin)
try {
var phonegap_Activity = Java.use('nl.xservices.plugins.sslCertificateChecker');
phonegap_Activity.execute.overload('java.lang.String', 'org.json.JSONArray', 'org.apache.cordova.CallbackContext').implementation = function (str) {
console.log('[+] Bypassing PhoneGap sslCertificateChecker: ' + str);
return true;
};
} catch (err) {
console.log('[-] PhoneGap sslCertificateChecker pinner not found');
//console.log(err);
}
// IBM MobileFirst pinTrustedCertificatePublicKey (double bypass)
try {
var WLClient_Activity = Java.use('com.worklight.wlclient.api.WLClient');
WLClient_Activity.getInstance().pinTrustedCertificatePublicKey.overload('java.lang.String').implementation = function (cert) {
console.log('[+] Bypassing IBM MobileFirst pinTrustedCertificatePublicKey {1}: ' + cert);
return;
};
WLClient_Activity.getInstance().pinTrustedCertificatePublicKey.overload('[Ljava.lang.String;').implementation = function (cert) {
console.log('[+] Bypassing IBM MobileFirst pinTrustedCertificatePublicKey {2}: ' + cert);
return;
};
} catch (err) {
console.log('[-] IBM MobileFirst pinTrustedCertificatePublicKey pinner not found');
//console.log(err);
}
// IBM WorkLight (ancestor of MobileFirst) HostNameVerifierWithCertificatePinning (quadruple bypass)
try {
var worklight_Activity = Java.use('com.worklight.wlclient.certificatepinning.HostNameVerifierWithCertificatePinning');
worklight_Activity.verify.overload('java.lang.String', 'javax.net.ssl.SSLSocket').implementation = function (str) {
console.log('[+] Bypassing IBM WorkLight HostNameVerifierWithCertificatePinning {1}: ' + str);
return;
};
worklight_Activity.verify.overload('java.lang.String', 'java.security.cert.X509Certificate').implementation = function (str) {
console.log('[+] Bypassing IBM WorkLight HostNameVerifierWithCertificatePinning {2}: ' + str);
return;
};
worklight_Activity.verify.overload('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;').implementation = function (str) {
console.log('[+] Bypassing IBM WorkLight HostNameVerifierWithCertificatePinning {3}: ' + str);
return;
};
worklight_Activity.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession').implementation = function (str) {
console.log('[+] Bypassing IBM WorkLight HostNameVerifierWithCertificatePinning {4}: ' + str);
return true;
};
} catch (err) {
console.log('[-] IBM WorkLight HostNameVerifierWithCertificatePinning pinner not found');
//console.log(err);
}
// Conscrypt CertPinManager
try {
var conscrypt_CertPinManager_Activity = Java.use('com.android.org.conscrypt.CertPinManager');
conscrypt_CertPinManager_Activity.isChainValid.overload('java.lang.String', 'java.util.List').implementation = function (str) {
console.log('[+] Bypassing Conscrypt CertPinManager: ' + str);
return true;
};
} catch (err) {
console.log('[-] Conscrypt CertPinManager pinner not found');
//console.log(err);
}
// CWAC-Netsecurity (unofficial back-port pinner for Android < 4.2) CertPinManager
try {
var cwac_CertPinManager_Activity = Java.use('com.commonsware.cwac.netsecurity.conscrypt.CertPinManager');
cwac_CertPinManager_Activity.isChainValid.overload('java.lang.String', 'java.util.List').implementation = function (str) {
console.log('[+] Bypassing CWAC-Netsecurity CertPinManager: ' + str);
return true;
};
} catch (err) {
console.log('[-] CWAC-Netsecurity CertPinManager pinner not found');
//console.log(err);
}
// Worklight Androidgap WLCertificatePinningPlugin
try {
var androidgap_WLCertificatePinningPlugin_Activity = Java.use('com.worklight.androidgap.plugin.WLCertificatePinningPlugin');
androidgap_WLCertificatePinningPlugin_Activity.execute.overload('java.lang.String', 'org.json.JSONArray', 'org.apache.cordova.CallbackContext').implementation = function (str) {
console.log('[+] Bypassing Worklight Androidgap WLCertificatePinningPlugin: ' + str);
return true;
};
} catch (err) {
console.log('[-] Worklight Androidgap WLCertificatePinningPlugin pinner not found');
//console.log(err);
}
// Netty FingerprintTrustManagerFactory
try {
var netty_FingerprintTrustManagerFactory = Java.use('io.netty.handler.ssl.util.FingerprintTrustManagerFactory');
//NOTE: sometimes this below implementation could be useful
//var netty_FingerprintTrustManagerFactory = Java.use('org.jboss.netty.handler.ssl.util.FingerprintTrustManagerFactory');
netty_FingerprintTrustManagerFactory.checkTrusted.implementation = function (type, chain) {
console.log('[+] Bypassing Netty FingerprintTrustManagerFactory');
};
} catch (err) {
console.log('[-] Netty FingerprintTrustManagerFactory pinner not found');
//console.log(err);
}
// Squareup CertificatePinner [OkHTTP < v3] (double bypass)
try {
var Squareup_CertificatePinner_Activity = Java.use('com.squareup.okhttp.CertificatePinner');
Squareup_CertificatePinner_Activity.check.overload('java.lang.String', 'java.security.cert.Certificate').implementation = function (str1, str2) {
console.log('[+] Bypassing Squareup CertificatePinner {1}: ' + str1);
return;
};
Squareup_CertificatePinner_Activity.check.overload('java.lang.String', 'java.util.List').implementation = function (str1, str2) {
console.log('[+] Bypassing Squareup CertificatePinner {2}: ' + str1);
return;
};
} catch (err) {
console.log('[-] Squareup CertificatePinner pinner not found');
//console.log(err);
}
// Squareup OkHostnameVerifier [OkHTTP v3] (double bypass)
try {
var Squareup_OkHostnameVerifier_Activity = Java.use('com.squareup.okhttp.internal.tls.OkHostnameVerifier');
Squareup_OkHostnameVerifier_Activity.verify.overload('java.lang.String', 'java.security.cert.X509Certificate').implementation = function (str1, str2) {
console.log('[+] Bypassing Squareup OkHostnameVerifier {1}: ' + str1);
return true;
};
Squareup_OkHostnameVerifier_Activity.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession').implementation = function (str1, str2) {
console.log('[+] Bypassing Squareup OkHostnameVerifier {2}: ' + str1);
return true;
};
} catch (err) {
console.log('[-] Squareup OkHostnameVerifier pinner not found');
//console.log(err);
}
// Android WebViewClient
try {
var AndroidWebViewClient_Activity = Java.use('android.webkit.WebViewClient');
AndroidWebViewClient_Activity.onReceivedSslError.overload('android.webkit.WebView', 'android.webkit.SslErrorHandler', 'android.net.http.SslError').implementation = function (obj1, obj2, obj3) {
console.log('[+] Bypassing Android WebViewClient');
};
} catch (err) {
console.log('[-] Android WebViewClient pinner not found');
//console.log(err);
}
// Apache Cordova WebViewClient
try {
var CordovaWebViewClient_Activity = Java.use('org.apache.cordova.CordovaWebViewClient');
CordovaWebViewClient_Activity.onReceivedSslError.overload('android.webkit.WebView', 'android.webkit.SslErrorHandler', 'android.net.http.SslError').implementation = function (obj1, obj2, obj3) {
console.log('[+] Bypassing Apache Cordova WebViewClient');
obj3.proceed();
};
} catch (err) {
console.log('[-] Apache Cordova WebViewClient pinner not found');
//console.log(err):
}
// Boye AbstractVerifier
try {
var boye_AbstractVerifier = Java.use('ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier');
boye_AbstractVerifier.verify.implementation = function (host, ssl) {
console.log('[+] Bypassing Boye AbstractVerifier: ' + host);
};
} catch (err) {
console.log('[-] Boye AbstractVerifier pinner not found');
//console.log(err):
}
});
}, 0);
@luk0y
Copy link

luk0y commented Mar 5, 2022

Unable to solve google recaptcha(In app) while connected to the burpsuite. I used Frida with the ssl unpinner. The error I got is unexpected ssl verification failed at "com.android.org.conscrypt.ActiveSession->checkPeerCertificatesPresent"

@marktane
Copy link

very useful, by the way . google play store is till a trouble to solve

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment