SSL Validation Bypasses
SSLContext Pinning Bypass & Network-Security-Config Bypass

Tracing getTrustManager
lets trace
getTrustManager()
to see which Trust Manager this application is using.

It is using
X509TrustManager
lets see its docs to see what is responsible for certificate pinning or validating server SSL auth. https://developer.android.com/reference/javax/net/ssl/X509TrustManager#checkServerTrusted(java.security.cert.X509Certificate[], java.lang.String)according to the
X509TrustManager
docscheckServerTrusted
is responsible for validating server SSL auth. lets confirm it by tracing

Its not the same
checkServerTrusted
which we saw in X509TrustManager.
Enumerating Methods
lets enumerate this method and check where it belongs and why it is called here.
[Android Emulator 5554::io.hextree.fridatarget ]-> Java.enumerateMethods('*!*checkServerTrusted*')
[
{
"classes": [
{
"methods": [
"checkServerTrusted"
],
"name": "javax.net.ssl.X509ExtendedTrustManager"
},
{
"methods": [
"checkServerTrusted"
],
"name": "javax.net.ssl.X509TrustManager"
},
{
"methods": [
"checkServerTrusted"
],
"name": "android.net.SSLCertificateSocketFactory$1"
},
{
"methods": [
"checkServerTrusted"
],
"name": "android.security.net.config.RootTrustManager"
},
{
"methods": [
"checkServerTrusted"
],
"name": "android.net.http.X509TrustManagerExtensions"
},
{
"methods": [
"checkServerTrusted"
],
"name": "android.security.net.config.NetworkSecurityTrustManager"
},
{
"methods": [
"checkServerTrusted"
],
"name": "com.android.org.conscrypt.TrustManagerImpl"
},
{
"methods": [
"checkServerTrusted"
],
"name": "com.android.org.conscrypt.Platform"
},
{
"methods": [
"checkServerTrusted"
],
"name": "com.android.org.conscrypt.ConscryptEngineSocket$2"
}
],
"loader": null
}
]
The
checkServerTrusted
we saw belongs tocom.android.org.conscrypt.Platform
lets see what this does in Android code search https://cs.android.com/android/platform/superproject/main/+/main:external/conscrypt/repackaged/platform/src/main/java/com/android/org/conscrypt/Platform.java;l=90?q=com.android.org.conscrypt.Platform&sq=com.android.org.conscrypt.Platform
have two overloads for thisDifference in overloads parameters first -
AbstractConscryptSocket socket
, second -ConscryptEngine engine
(the last parameter is different respectively)According to what we traced via frida-trace its last param was
ConscryptEngine
static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain, String authType,
ConscryptEngine engine) throws CertificateException {
if (tm instanceof X509ExtendedTrustManager) {
X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
x509etm.checkServerTrusted(chain, authType, engine);
} else if (!checkTrusted("checkServerTrusted", tm, chain, authType, SSLEngine.class, engine)
&& !checkTrusted("checkServerTrusted", tm, chain, authType, String.class,
engine.getHandshakeSession().getPeerHost())) {
tm.checkServerTrusted(chain, authType);
}
}
This just checks if the provided trust manager supports more context i.e. is and instance of
X509ExtendedTrustManager
which its not clearly.Then it
checkTrusted
that might accept additional context (Socket and String)If everything fails it fallbacks to the original
X509TrustManager
So if we change implementation of this
checkServerTrusted
and instead of passing it for validation it will do nothing (accept any cert instead of only trusting the pinned cert)so to determine what params to pass in .overload we have 2 ways
Either we see the code and and pass it whatever params it takes i.e.
.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.ConscryptEngine')
or just run it in frida without overload it will give an error and in that error we can find all the available overloads with all its params. i.e.

Final script for SSL Context and network security config bypass:-
Java.perform(() => {
let sslcontextverify = Java.use("com.android.org.conscrypt.Platform");
sslcontextverify.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.ConscryptEngine').implementation = function(){
console.log("checkServerTrusted called");
}
})
Somehow this same worked for Network Config Pinning also
OKHTTP3 with Pinning

OkHttpCliet is using certificatePinner to pin certificates
Actual code used by certificatePiner (Extracted from the apk)
public final Builder certificatePinner(CertificatePinner certificatePinner) {
Intrinsics.checkNotNullParameter(certificatePinner, "certificatePinner");
Builder $this$apply = this;
if (!Intrinsics.areEqual(certificatePinner, $this$apply.certificatePinner)) {
$this$apply.routeDatabase = null;
}
$this$apply.certificatePinner = certificatePinner;
return this;
}
If we chage the implemetation of this certificatePinner to do nothig it will no longer eforce certificate pining.
Its return type is a actual
Builder
instance. (We are also going to usereturn this
)
Script to change the implementation of certificatePinner only:-
let okhttpBuilder = Java.use("okhttp3.OkHttpClient$Builder")
okhttpBuilder.certificatePinner.implementation = function(){
console.log("certificatePier called")
return this;
}
Only this will not work because we disabled the SSL Pinner but not the SSL validation i.e. okhttp3 will still use the TrustManager so we have to combine previous and this script to make it work
Final Script for OKHTTP3 bypass :-
Java.perform(() => {
let sslcontextverify = Java.use("com.android.org.conscrypt.Platform");
sslcontextverify.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.ConscryptEngine').implementation = function(){
console.log("checkServerTrusted called");
}
let okhttpPinner = Java.use("okhttp3.OkHttpClient$Builder")
okhttpPinner.certificatePinner.implementation = function(){
console.log("certificatePinner called")
return this;
}
})
Last updated
Was this helpful?