SSL Validation Bypasses

SSLContext Pinning Bypass & Network-Security-Config Bypass

image.png

Tracing getTrustManager

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

image.png
image.png
  • 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
    }
]
    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.

image.png

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

image.png
  • 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 use return 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?