DEV Community

Syed Abrar
Syed Abrar

Posted on Originally published at andraxpentester.in

Bypassing SSL Pinning on Android 14/15 using Frida, Objection & Native Hooks (2026 Masterclass)

Originally published at AndraxPentester.in.

Modern Android application security has evolved dramatically in 2026. With Android 14 and 15 enforcing strict platform-level Network Security Configurations, ephemeral APEX CA certificate stores (/apex/com.android.conscrypt/cacerts), and advanced native certificate pinning engines embedded in C/C++ libraries (libsslcrypto.so, BoringSSL, and Conscrypt), traditional proxy interception with Burp Suite often fails out-of-the-box.

In this masterclass, we break down the first-principles architecture of SSL/TLS certificate validation in modern Android environments, map the target execution path from high-level Java frameworks (OkHttp3, TrustManager) down to native C-level BoringSSL hooks (SSL_CTX_set_verify), and demonstrate actionable, production-grade dynamic instrumentation workflows using Frida and Objection.


1. Architectural Overview: How Android Enforces Certificate Pinning

1.1 The Dual-Layer Validation Architecture

To successfully intercept HTTPS traffic on modern Android devices (Android 7.0 through Android 15), security engineers must bypass two distinct layers of certificate validation:

  1. System CA Trust Store Isolation (Android 7+ to 15): User-installed CA certificates (/data/misc/user/0/cacerts-added/) are no longer trusted by default for application network traffic unless explicitly declared in res/xml/network_security_config.xml. Furthermore, Android 14+ mounts system CA certificates read-only under the ConsCrypt APEX path (/apex/com.android.conscrypt/cacerts), preventing simple /system/etc/security/cacerts write operations without ephemeral overlay mounts.
  2. Application-Level Pinning: Applications embed cryptographic hashes (SHA-256) of expected server public keys or certificates directly within source code or native libraries, validating them independently of the OS trust store.

2. Dynamic Instrumentation: Production Frida Script

Save the following JavaScript file as universal_unpinning_2026.js:

/*
 * Universal Multi-Framework SSL Pinning Bypass 2026
 * Author: Syed Zada Abrar (Andrax Pentester)
 * Target: Android 7.0 - 15 (Java & Native BoringSSL Layers)
 */

Java.perform(function () {
    console.log("[+] Initializing Universal Multi-Framework SSL Pinning Bypass...");

    // 1. TrustManager Override (Accepts All Certificates)
    try {
        var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager');
        var SSLContext = Java.use('javax.net.ssl.SSLContext');

        var TrustManager = Java.registerClass({
            name: 'in.andraxpentester.BypassTrustManager',
            implements: [X509TrustManager],
            methods: {
                checkClientTrusted: function (chain, authType) {},
                checkServerTrusted: function (chain, authType) {},
                getAcceptedIssuers: function () { return []; }
            }
        });

        var TrustManagers = [TrustManager.$new()];
        var SSLContext_init = SSLContext.init.overload(
            '[Ljavax.net.ssl.KeyManager;',
            '[Ljavax.net.ssl.TrustManager;',
            'java.security.SecureRandom'
        );
        SSLContext_init.implementation = function (keyManager, trustManager, secureRandom) {
            console.log("[+] Intercepted SSLContext.init() -> Overriding with permissive TrustManager");
            SSLContext_init.call(this, keyManager, TrustManagers, secureRandom);
        };
    } catch (err) {
        console.log("[-] TrustManager override error: " + err);
    }

    // 2. 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("[+] Bypassed OkHttp3 CertificatePinner.check() for host: " + hostname);
            return;
        };
    } catch (err) {
        console.log("[-] OkHttp3 CertificatePinner not found or obfuscated");
    }

    // 3. Conscrypt TrustManagerImpl Bypass (Android 10 - 15)
    try {
        var TrustManagerImpl = Java.use('com.android.org.conscrypt.TrustManagerImpl');
        TrustManagerImpl.checkTrustedRecursive.implementation = function (certs, host, clientAuth, untrustedChain, trustAnchorChain, usedJaasRealm) {
            console.log("[+] Bypassed Conscrypt TrustManagerImpl.checkTrustedRecursive() for host: " + host);
            return Java.use('java.util.ArrayList').$new();
        };
    } catch (err) {
        console.log("[-] Conscrypt TrustManagerImpl hook skipped");
    }
});

// 4. Native Layer Hooking (BoringSSL libssl.so)
try {
    var libsslModule = Process.findModuleByName("libssl.so");
    if (libsslModule) {
        var ssl_ctx_set_verify = Module.findExportByName("libssl.so", "SSL_CTX_set_verify");
        if (ssl_ctx_set_verify) {
            Interceptor.attach(ssl_ctx_set_verify, {
                onEnter: function (args) {
                    console.log("[+] Native SSL_CTX_set_verify called -> Setting SSL_VERIFY_NONE (0x00)");
                    args[1] = ptr(0); // SSL_VERIFY_NONE
                }
            });
        }
    }
} catch (err) {
    console.log("[-] Native BoringSSL hook error: " + err);
}
Enter fullscreen mode Exit fullscreen mode

Executing the Script

frida -U -f com.target.enterprise.app -l universal_unpinning_2026.js
Enter fullscreen mode Exit fullscreen mode

Read the full deep-dive guide on AndraxPentester.in.

Top comments (0)