Author: admin

  • Zero to Bypass: Your Complete Guide to Frida for Android Custom Cert Pinning

    Introduction to Certificate Pinning and Its Challenges

    Certificate pinning is a crucial security mechanism employed by mobile applications, especially those handling sensitive data, to prevent Man-in-the-Middle (MITM) attacks. It involves embedding or ‘pinning’ a host’s expected certificate or public key within the client application. When the application attempts to communicate with the server, it verifies that the server’s presented certificate matches the pinned certificate. If there’s a mismatch, the connection is terminated, thwarting attempts by attackers to intercept traffic using rogue certificates.

    While standard certificate pinning, often implemented via common network libraries like OkHttp or through Android’s default `Network Security Configuration`, can often be bypassed with generic Frida scripts or Magisk modules, custom certificate pinning presents a tougher challenge. Developers sometimes implement their own `X509TrustManager` or `SSLSocketFactory` to perform certificate validation, checking specific attributes like issuer, subject, or even raw public key hashes. This bespoke logic makes a ‘one-size-fits-all’ bypass ineffective, demanding a targeted approach using dynamic instrumentation tools like Frida.

    Prerequisites for Your Pinning Bypass Journey

    Before diving into the bypass techniques, ensure you have the following setup:

    • Rooted Android Device/Emulator: Frida requires root privileges to inject into target processes.
    • ADB (Android Debug Bridge): Essential for interacting with your Android device (pushing files, running shell commands).
    • Python & pip: For installing Frida-tools on your host machine.
    • Frida-tools: Install using `pip install frida-tools`.
    • Frida-server: The server component that runs on the Android device. Download the correct architecture-specific version from Frida’s GitHub releases.
    • Proxy Tool: Burp Suite, OWASP ZAP, or another proxy for observing traffic.
    • Decompiler: Jadx-GUI (https://github.com/skylot/jadx) is highly recommended for static analysis.

    Setting Up Your Frida Environment

    1. Push Frida-server to Device:

    adb push /path/to/frida-server /data/local/tmp/frida-server

    2. Set Permissions and Run:

    adb shellsu -c

  • Frida Masterclass: Bypassing Custom Certificate Pinning in Android Apps, Step-by-Step

    Introduction to Certificate Pinning and Frida

    Certificate pinning is a crucial security mechanism employed by Android applications to prevent Man-in-the-Middle (MITM) attacks. By associating a host with its expected X.509 certificate or public key, an app can verify that the server it’s communicating with is indeed the legitimate one, even if the device’s trust store has been compromised or a rogue certificate authority issues a fraudulent certificate. While highly effective for security, it poses a significant challenge for penetration testers and security researchers who need to intercept and analyze application traffic.

    Understanding Certificate Pinning Implementations

    Android apps can implement certificate pinning in several ways:

    • Network Security Configuration (NSC): Introduced in Android 7.0 (API level 24), this XML-based configuration allows developers to declare trusted CAs or specific certificates/keys within the app’s manifest, making it a relatively straightforward and common method.
    • TrustManager APIs: Developers can provide custom implementations of `javax.net.ssl.X509TrustManager` or extend default ones to enforce pinning logic. This involves overriding methods like `checkServerTrusted` to validate certificates against a predefined set.
    • HostnameVerifier: The `javax.net.ssl.HostnameVerifier` interface can be implemented to verify the hostname against a certificate’s subject alternative names or common name.
    • Low-Level TLS/SSL Configuration: More complex applications might interact directly with `SSLSocketFactory` or even native (JNI) code to manage trust and certificate validation.

    The Challenge of Custom Implementations

    While generic Frida scripts and tools like Burp Suite’s Mobile Assistant or various Magisk modules can bypass standard pinning (especially NSC or common library implementations like OkHttp’s), custom certificate pinning logic often evades these solutions. When an app employs its own unique `TrustManager` or key storage, a targeted and dynamic approach using a powerful instrumentation toolkit like Frida becomes indispensable.

    Prerequisites: Setting Up Your Environment

    Before diving into the bypass, ensure your environment is correctly set up.

    Required Tools

    • Frida: The dynamic instrumentation toolkit.
    • Android SDK Platform Tools: For `adb` (Android Debug Bridge) commands.
    • Rooted Android Device or Emulator: Frida requires root access to inject into processes.
    • Static Analysis Tool: Jadx-GUI or Ghidra for decompiling and analyzing APKs.
    • Traffic Proxy: Burp Suite or OWASP ZAP for intercepting traffic.

    Frida Installation & Setup

    Install Frida tools on your host machine:

    pip install frida-tools

    Download the appropriate `frida-server` for your device’s architecture (e.g., `frida-server-*-android-arm64`) from Frida releases. Push it to your device and run it:

    adb push frida-server /data/local/tmp/frida-server
    adb shell

  • Deep Dive: Understanding & Bypassing OkHttp3 SSL Pinning with Frida on Android

    Introduction to SSL Pinning and OkHttp3

    SSL (Secure Sockets Layer) pinning is a crucial security mechanism employed by mobile applications to prevent Man-in-the-Middle (MITM) attacks. While standard HTTPS ensures that a client communicates with the intended server by verifying its certificate against a set of trusted root Certificate Authorities (CAs), SSL pinning takes this a step further. It means the application expects a specific certificate or public key from the server, hardcoded within the app itself. If the server presents a different certificate, even one issued by a trusted CA (like a Burp Suite proxy’s CA), the connection is immediately terminated.

    OkHttp3 is a popular HTTP client for Java and Android applications, known for its efficiency and robust features, including built-in support for SSL pinning via its CertificatePinner class. For penetration testers and security researchers, bypassing such mechanisms is essential to intercept and analyze application network traffic, identify vulnerabilities, and assess the overall security posture of the application.

    Understanding OkHttp3’s CertificatePinner

    OkHttp3 implements SSL pinning using the okhttp3.CertificatePinner class. This class allows developers to specify which certificates or public keys are trusted for particular hostnames. When an OkHttp3 client makes a request, the CertificatePinner intercepts the SSL handshake, extracts the server’s certificate chain, and compares it against the pre-configured pins. If no match is found, the connection is aborted with an SSLPeerUnverifiedException.

    Developers typically configure CertificatePinner by adding certificate hashes (SHA-256) or public key hashes. These hashes are often embedded directly into the application’s source code or configuration files. Here’s a common way it’s implemented in an Android application:

    import okhttp3.CertificatePinner;import okhttp3.OkHttpClient;import okhttp3.Request;public class PinnedHttpClient {    public static void main(String[] args) {        CertificatePinner certificatePinner = new CertificatePinner.Builder()                .add("example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")                .add("*.example.com", "sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=")                .build();        OkHttpClient client = new OkHttpClient.Builder()                .certificatePinner(certificatePinner)                .build();        Request request = new Request.Builder()                .url("https://example.com/")                .build();        try {            client.newCall(request).execute();            System.out.println("Connection successful!");        } catch (Exception e) {            System.err.println("SSL Pinning failed: " + e.getMessage());        }    }}

    In this example, the client explicitly trusts only the certificates with the specified SHA-256 hashes for example.com and its subdomains. Any attempt to proxy this traffic with a self-signed certificate (like those from Burp Suite) will fail.

    The Penetration Testing Challenge

    When an application employs SSL pinning, traditional methods of intercepting traffic, such as configuring an HTTP proxy (e.g., Burp Suite, OWASP ZAP) and installing its root CA on the device, become ineffective. The app will detect the proxy’s certificate as untrusted (because it doesn’t match the pinned hash) and refuse to establish a connection. This is where dynamic instrumentation frameworks like Frida become indispensable.

    Introducing Frida: Your Dynamic Instrumentation Ally

    Frida is a dynamic instrumentation toolkit that allows you to inject snippets of JavaScript or your own library into native apps on Windows, macOS, Linux, iOS, Android, and QNX. It provides a powerful API to hook functions, modify code, and inspect runtime data. For Android penetration testing, Frida is commonly used to bypass security controls that operate at runtime, such as root detection, anti-tampering checks, and, crucially, SSL pinning.

    Frida Setup Prerequisites

    Before proceeding, ensure you have the following:

    1. Rooted Android Device or Emulator: Frida requires root privileges to inject into arbitrary processes.
    2. ADB (Android Debug Bridge): To interact with your Android device.
    3. Frida Server: The Frida server binary running on your Android device. You can download it from Frida’s GitHub releases page (e.g., frida-server-<version>-android-arm64) and push it to /data/local/tmp/, then execute it with ./frida-server.
    4. Frida-tools: Installed on your host machine (pip install frida-tools).

    Frida-Based Bypass Strategy for OkHttp3

    The core strategy to bypass OkHttp3 SSL pinning with Frida involves hooking into the okhttp3.CertificatePinner class and modifying its behavior at runtime. Specifically, we target the check method, which is responsible for performing the actual certificate validation against the pinned values. By replacing the original implementation of this method with an empty (no-op) function, we effectively disable the pinning check, allowing any certificate (including your proxy’s) to pass.

    There are two common overloads for the check method in CertificatePinner:

    • check(String hostname, java.util.List<java.security.cert.Certificate> peerCertificates)
    • check(String hostname, [Ljava.security.cert.X509Certificate; peerCertificates)

    We’ll typically hook both to ensure comprehensive coverage, although the first one (with List<Certificate>) is more frequently invoked in modern OkHttp3 versions.

    Step-by-Step Bypass with Frida

    1. Identify the Target Application

    First, you need the package name of the Android application you wish to test. You can find this using ADB:

    adb shell pm list packages | grep <app_name_keyword>

    For example, if the app is called

  • The Ultimate Toolkit: Frida Scripts & Techniques for OkHttp3 SSL Pinning Bypass

    Introduction to SSL Pinning and Its Bypass

    SSL Pinning is a critical security mechanism implemented in mobile applications to prevent man-in-the-middle (MITM) attacks. It ensures that an app communicates only with trusted servers by validating the server’s certificate or public key against a predefined set of trusted entities embedded within the application itself. While excellent for security, this poses a significant challenge for penetration testers and security researchers who need to intercept and analyze app traffic. OkHttp3, a popular HTTP client for Android, frequently incorporates SSL pinning using its CertificatePinner class.

    This article provides an expert-level guide on leveraging Frida, a dynamic instrumentation toolkit, to bypass OkHttp3 SSL pinning in Android applications. We will explore specific scripting techniques targeting OkHttp3’s core pinning mechanisms, offering practical examples and step-by-step instructions for successful traffic interception during penetration testing.

    Understanding OkHttp3’s CertificatePinner

    OkHttp3 implements SSL pinning primarily through its okhttp3.CertificatePinner class. Developers can configure an OkHttpClient instance to use a CertificatePinner that specifies expected SHA-256 hashes of certificates or public keys for specific hosts. When a connection is established, OkHttp3 verifies the server’s certificate chain against these pinned hashes. If a mismatch occurs, the connection is aborted, preventing communication with an untrusted server (like a proxy’s certificate).

    A typical OkHttp3 pinning implementation might look like this in Java/Kotlin code:

    public class MyApp {  private OkHttpClient client;  public MyApp() {    CertificatePinner certificatePinner = new CertificatePinner.Builder()      .add("*.example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")      .add("*.anothersite.com", "sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=")      .build();    client = new OkHttpClient.Builder()      .certificatePinner(certificatePinner)      .build();  }}

    The key method we’ll target in CertificatePinner for bypassing is the check() method, which performs the actual pinning validation.

    Frida Fundamentals for Android Penetration Testing

    Frida allows injecting JavaScript snippets into native apps on Android, iOS, Windows, macOS, and Linux. For Android, it enables us to hook into Java methods, modify their behavior, inspect arguments, and even replace entire implementations at runtime. This makes it an ideal tool for bypassing security controls like SSL pinning without modifying the application’s APK.

    Setting up Frida

    Before proceeding, ensure you have:

    • A rooted Android device or emulator.
    • Frida server running on the Android device.
    • Frida tools installed on your host machine (pip install frida-tools).
    • adb (Android Debug Bridge) configured.

    To run the Frida server:

    adb push frida-server /data/local/tmp/frida-serveradb shell "chmod 755 /data/local/tmp/frida-server"adb shell "/data/local/tmp/frida-server &"

    Frida Scripting Techniques for OkHttp3 SSL Pinning Bypass

    Method 1: Hooking okhttp3.CertificatePinner.check()

    This is the most direct approach. We’ll target the check() method within the okhttp3.CertificatePinner class and force it to do nothing, effectively disabling the pinning validation.

    Java.perform(function () {    console.log("[*] Starting OkHttp3 CertificatePinner Bypass...");    var CertificatePinner = Java.use('okhttp3.CertificatePinner');    CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function (hostname, peerCertificates) {        console.log("[+] Bypassing CertificatePinner.check() for hostname: " + hostname);        // Do nothing, effectively bypassing the pinning check        // You can optionally call the original method if you want to inspect arguments        // this.check(hostname, peerCertificates);    };    console.log("[*] OkHttp3 CertificatePinner Bypass script loaded.");});

    Explanation:

    1. Java.perform(function () { ... });: Ensures our script runs in the context of the target Java VM.
    2. Java.use('okhttp3.CertificatePinner');: Obtains a JavaScript wrapper for the okhttp3.CertificatePinner class.
    3. .check.overload('java.lang.String', 'java.util.List'): Specifies the exact overload of the check() method we want to hook. It’s crucial to match the argument types correctly.
    4. .implementation = function (...) { ... };: Replaces the original implementation of the check() method with our custom function, which simply logs a message and returns, thus skipping the actual pinning logic.

    Method 2: Hooking javax.net.ssl.X509TrustManager.checkServerTrusted()

    While Method 1 targets OkHttp3 specifically, many SSL pinning implementations (including those relying on default system trust managers) eventually call methods within the X509TrustManager interface. Bypassing checkServerTrusted() offers a more generic approach that can work even if the app uses custom trust managers or other libraries for pinning, as long as they ultimately delegate to an X509TrustManager.

    Java.perform(function () {    console.log("[*] Starting X509TrustManager Bypass...");    var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager');    var TrustManagerImpl = Java.use('com.android.org.conscrypt.TrustManagerImpl'); // For newer Android versions    var checkServerTrusted = X509TrustManager.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String');    var checkServerTrustedArgs = checkServerTrusted.argumentTypes;    checkServerTrusted.implementation = function (chain, authType) {        console.log("[+] Bypassing X509TrustManager.checkServerTrusted()");        // Call original method only if needed, for simplicity we bypass        // this.checkServerTrusted(chain, authType);    };    // Also hook TrustManagerImpl.checkServerTrusted if TrustManagerImpl is used    if (TrustManagerImpl) {        var checkServerTrusted_TrustManagerImpl = TrustManagerImpl.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String');        if (checkServerTrusted_TrustManagerImpl) {            checkServerTrusted_TrustManagerImpl.implementation = function (chain, authType, host) {                console.log("[+] Bypassing TrustManagerImpl.checkServerTrusted() for host: " + host);                // this.checkServerTrusted(chain, authType, host);            };        }        var checkServerTrusted_TrustManagerImpl_sslEngine = TrustManagerImpl.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLEngine');        if (checkServerTrusted_TrustManagerImpl_sslEngine) {            checkServerTrusted_TrustManagerImpl_sslEngine.implementation = function (chain, authType, sslEngine) {                console.log("[+] Bypassing TrustManagerImpl.checkServerTrusted() for SSLEngine");                // this.checkServerTrusted(chain, authType, sslEngine);            };        }    }    console.log("[*] X509TrustManager Bypass script loaded.");});

    Explanation:

    1. This script targets multiple overloads of checkServerTrusted() in both the standard X509TrustManager and the Android-specific com.android.org.conscrypt.TrustManagerImpl. This comprehensive approach increases the chances of successful bypass across different Android versions and app implementations.
    2. Similar to Method 1, we replace the original implementation with an empty function, preventing certificate validation errors.

    Execution Guide and Observing Results

    To apply these Frida scripts:

    1. Save your chosen script (e.g., bypass.js).
    2. Identify the target Android application’s package name (e.g., com.example.app).
    3. Run Frida, attaching to the application:
    frida -U -f com.example.app -l bypass.js --no-pause

    The -U flag targets a USB-connected device, -f spawns and attaches to the app, -l loads your script, and --no-pause starts the app immediately without waiting. After running, interact with the app. If successful, you should see the log messages from your Frida script in the console.

    To observe the intercepted traffic:

    1. Configure your Android device’s Wi-Fi proxy settings to point to your Burp Suite (or any other HTTP proxy) instance running on your host machine.
    2. Ensure Burp Suite is set up to listen on the specified port and is configured to generate an SSL CA certificate that your device trusts (typically by installing Burp’s CA certificate on the Android device).
    3. With the Frida script active and the proxy configured, all network traffic from the target app should now flow through Burp Suite, allowing you to inspect requests and responses.

    Advanced Considerations and Troubleshooting

    • Obfuscation: Apps using ProGuard or DexGuard might obfuscate class and method names. In such cases, you might need to use techniques like Java.enumerateLoadedClasses() to find the real class names at runtime, or use wildcard patterns in Java.use() (e.g., Java.use('/.*.CertificatePinner/') if the package name is unknown but the class name is unique).
    • Multiple Pinning Mechanisms: Some applications might combine OkHttp3 pinning with other forms of pinning (e.g., using Conscrypt’s PinningTrustManager or custom network security configurations). A multi-pronged approach combining various Frida scripts might be necessary.
    • Timing Issues: Ensure your script is injected and active before the application initializes its network stack. Using --no-pause with -f helps ensure early injection.
    • Error Handling: Always add try...catch blocks in your Frida scripts to gracefully handle cases where a class or method might not exist or has a different signature than expected.

    Conclusion

    Bypassing SSL pinning is a fundamental skill for Android application penetration testers. Frida provides an incredibly flexible and powerful platform to achieve this without modifying the application binary. By understanding how OkHttp3 implements its CertificatePinner and leveraging the techniques discussed—particularly hooking CertificatePinner.check() and X509TrustManager.checkServerTrusted()—you can effectively neutralize this security control and gain full visibility into an app’s network communications. Always remember to apply these techniques ethically and only on applications you have explicit permission to test.

  • Beyond Basic: Unveiling the Mechanics of Frida’s OkHttp3 SSL Pinning Bypass

    Introduction

    SSL (Secure Sockets Layer) pinning is a crucial security mechanism employed by Android applications to prevent Man-in-the-Middle (MiTM) attacks. By ‘pinning’ the application to specific trusted certificates or public keys, it ensures that even if a user has installed a malicious root certificate on their device, the application will only trust the predefined server certificates. While vital for security, this poses a significant hurdle for penetration testers and security researchers who need to intercept and analyze network traffic for vulnerabilities. This article dives deep into bypassing SSL pinning specifically implemented using OkHttp3, a popular HTTP client library in Android, leveraging the dynamic instrumentation framework Frida.

    Generic SSL unpinning scripts often fall short when an application uses robust pinning mechanisms like those found in OkHttp3. We’ll explore the internals of OkHttp3’s `CertificatePinner` and craft a precise Frida script to effectively disable it, enabling successful traffic interception.

    The Challenge of OkHttp3 SSL Pinning

    How OkHttp3 Implements Pinning

    OkHttp3 offers a powerful and flexible way to implement SSL pinning through its `CertificatePinner` class. Developers can specify a list of acceptable SHA-256 hashes of certificates (or public keys) that the application should trust for a given hostname. During an HTTPS handshake, after the server presents its certificate chain, OkHttp3’s `CertificatePinner` verifies if any of the server’s certificates match the predefined pins. If no match is found, the connection is aborted, regardless of whether the device’s system trust store validates the certificate.

    OkHttpClient client = new OkHttpClient.Builder()
        .certificatePinner(new CertificatePinner.Builder()
            .add("example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
            .build())
        .build();
    

    This mechanism is highly effective because it operates at a lower level than just relying on the `X509TrustManager`, which is often targeted by generic bypasses. The `CertificatePinner`’s check occurs *after* the initial certificate chain validation by the `TrustManager` but *before* the connection is fully established, making it a formidable barrier.

    Why Generic Bypasses Fail

    Most common Frida SSL bypass scripts target the `X509TrustManager`’s `checkServerTrusted` methods. These methods are responsible for validating the server’s certificate chain against the device’s installed trust anchors. By hooking and modifying these methods to always accept any certificate, a penetration tester can usually bypass standard SSL validation.

    However, when `CertificatePinner` is in play, bypassing the `TrustManager` alone is insufficient. Even if the `TrustManager` is convinced to trust a proxy’s certificate (like Burp Suite’s CA), the `CertificatePinner` will still perform its independent check. If the proxy’s certificate hash does not match the hardcoded pins, the connection will fail. This means we need a more targeted approach.

    Frida: Your Dynamic Instrumentation Ally

    Frida is a dynamic instrumentation toolkit that allows you to inject JavaScript code into running processes. This powerful capability enables real-time observation, modification, and manipulation of application logic, making it an invaluable tool for reverse engineering, security research, and penetration testing. For SSL pinning bypass, Frida allows us to reach into the application’s memory, identify the `CertificatePinner` instance, and alter its behavior or replace it entirely.

    Deconstructing the OkHttp3 Bypass Strategy

    Targeting the `OkHttpClient.Builder`

    The most effective strategy for bypassing OkHttp3’s `CertificatePinner` is to intercept the construction of the `OkHttpClient` instance itself. OkHttp3 applications typically build their `OkHttpClient` instance using the `OkHttpClient.Builder` class. This builder pattern allows developers to configure various aspects of the HTTP client, including setting a custom `CertificatePinner`.

    Our goal is to hook the `build()` method of the `OkHttpClient.Builder` class. Before the original `build()` method is executed, we will programmatically reset or disable any `CertificatePinner` that might have been configured. This ensures that when the `OkHttpClient` instance is finally created and returned, it will not have an active `CertificatePinner`, effectively disabling the pinning mechanism.

    The Role of `CertificatePinner.DEFAULT`

    The `okhttp3.CertificatePinner` class provides a static field called `DEFAULT`. This `DEFAULT` instance represents a `CertificatePinner` that performs no pinning whatsoever. By setting the `CertificatePinner` of our `OkHttpClient.Builder` to `CertificatePinner.DEFAULT`, we instruct OkHttp3 to proceed with standard SSL validation (via the `TrustManager`) without any additional certificate hash checks. This is the key to our bypass.

    Implementing the Frida Bypass Script

    Prerequisites

    • Rooted Android Device or Emulator: Frida server needs root access to inject into processes.
    • ADB (Android Debug Bridge): To push Frida server to the device and interact with it.
    • Frida CLI Tools: `frida-server` (on device) and `frida` / `frida-ps` / `frida-trace` (on host machine).
    • Target Android Application: An app known to use OkHttp3 for network requests and implements SSL pinning.
    • Intercepting Proxy (e.g., Burp Suite): To verify the bypass by capturing traffic.

    The Frida JavaScript Code

    Here’s a comprehensive Frida script designed to bypass OkHttp3 SSL pinning by targeting the `OkHttpClient.Builder`:

    Java.perform(function() {
        console.log("[*] Starting OkHttp3 SSL Pinning Bypass...");
    
        try {
            // Get a reference to the OkHttpClient$Builder class
            var OkHttpClientBuilder = Java.use("okhttp3.OkHttpClient$Builder");
            var CertificatePinner = Java.use("okhttp3.CertificatePinner");
    
            // Hook the build() method of OkHttpClient.Builder
            OkHttpClientBuilder.build.implementation = function() {
                console.log("[+] Hooking OkHttpClient.Builder.build()");
    
                // Set the CertificatePinner to CertificatePinner.DEFAULT (no pinning)
                // This effectively disables certificate pinning for this builder instance
                this.certificatePinner(CertificatePinner.DEFAULT);
                console.log("[+] CertificatePinner set to DEFAULT.");
    
                // Call the original build() method to complete the client creation
                var okHttpClient = this.build();
                console.log("[+] OkHttpClient built without pinning.");
                return okHttpClient;
            };
            console.log("[*] Successfully hooked okhttp3.OkHttpClient$Builder.build().");
    
            // Also hook the CertificatePinner.check() methods to be extra safe for edge cases
            // This might not always be necessary with the Builder hook, but provides redundancy.
            CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function (hostname, certificates) {
                console.log("[+] Bypassing CertificatePinner.check(String, List) for: " + hostname);
                // Do nothing, effectively trusting all certificates
            };
    
            CertificatePinner.check.overload('java.lang.String', '[Ljava.security.cert.Certificate;').implementation = function (hostname, certificates) {
                console.log("[+] Bypassing CertificatePinner.check(String, Certificate[]) for: " + hostname);
                // Do nothing, effectively trusting all certificates
            };
            console.log("[*] Successfully hooked okhttp3.CertificatePinner.check() methods.");
    
        } catch (e) {
            console.error("[-] Error during OkHttp3 pinning bypass setup: " + e.message);
        }
    
        // Generic TrustManager bypass (good to include as a fallback or for other libraries)
        try {
            var TrustManager = Java.use('javax.net.ssl.X509TrustManager');
            var TrustManagerImpl = Java.use('com.android.org.conscrypt.TrustManagerImpl');
    
            var trustMethods = ['checkClientTrusted', 'checkServerTrusted'];
            var emptyArray = Java.array('Ljava.security.cert.X509Certificate;', []);
    
            TrustManager.checkServerTrusted.implementation = function(chain, authType) {
                console.log("[+] Bypassing X509TrustManager.checkServerTrusted for: " + chain[0].getSubjectDN().getName());
                // Always trust
            };
    
            TrustManager.checkClientTrusted.implementation = function(chain, authType) {
                console.log("[+] Bypassing X509TrustManager.checkClientTrusted for: " + chain[0].getSubjectDN().getName());
                // Always trust
            };
    
            if (TrustManagerImpl) {
                TrustManagerImpl.checkServerTrusted.implementation = function(chain, authType, host) {
                    console.log("[+] Bypassing TrustManagerImpl.checkServerTrusted for: " + host);
                    // Always trust
                    return chain;
                };
            }
    
            console.log("[*] Generic X509TrustManager bypass applied.");
    
        } catch (e) {
            console.error("[-] Error during generic TrustManager bypass setup: " + e.message);
        }
    
        console.log("[*] OkHttp3 SSL Pinning Bypass script loaded.");
    });
    

    Executing the Script

    1. Start Frida Server: Ensure `frida-server` is running on your Android device/emulator.
    2. Identify Application Package: Find the package name of the target application (e.g., `com.example.app`).
    3. Run Frida with the Script:
    frida -U -f com.example.app -l okhttp3_bypass.js --no-pause
    

    Replace `com.example.app` with the actual package name of your target application and `okhttp3_bypass.js` with the filename of your Frida script. The `–no-pause` flag ensures the application starts immediately after injection.

    Verifying the Bypass

    Once Frida is running and the script is injected, configure your Android device’s Wi-Fi proxy settings to point to your intercepting proxy (e.g., Burp Suite, OWASP ZAP). Launch the application and attempt to perform network-dependent actions. If the bypass is successful, you should now observe the application’s HTTPS traffic flowing through your proxy, allowing you to inspect requests and responses.

    Look for the `[+]` messages in your Frida console output, indicating that the hooks have been hit and the `CertificatePinner` has been reset.

    Advanced Considerations and Limitations

    • Obfuscation: If the application is heavily obfuscated using ProGuard or R8, the class and method names (e.g., `okhttp3.OkHttpClient$Builder`, `build`) might be renamed. In such cases, you would need to use static analysis (e.g., Jadx, Ghidra) or dynamic analysis (e.g., `frida-trace`) to identify the obfuscated names.
    • Alternative Pinning Implementations: Some applications might implement pinning outside of standard OkHttp3 mechanisms or use custom network stacks. This script specifically targets OkHttp3’s `CertificatePinner`.
    • Frida Detection: Advanced anti-tampering measures might detect the presence of Frida and prevent the application from running or making network requests. Further techniques would be needed to bypass such detections.
    • Android N/Q/R/S Trust Changes: For Android 7.0 (N) and above, applications might restrict user-installed CAs. Ensure your application’s `network_security_config.xml` allows user-added CAs for debugging or target API levels are considered.

    Conclusion

    Bypassing SSL pinning in Android applications requires a deep understanding of how these security mechanisms are implemented. While generic `X509TrustManager` bypasses are often the first step, sophisticated libraries like OkHttp3’s `CertificatePinner` demand a more precise approach. By leveraging Frida’s dynamic instrumentation capabilities, we can effectively disable the `CertificatePinner` at the point of `OkHttpClient` creation, opening up the application’s network traffic for comprehensive security analysis. This expert-level technique underscores the power of dynamic instrumentation in overcoming modern mobile security challenges, empowering penetration testers to uncover hidden vulnerabilities.

  • Automating Root Bypass: Building a Dynamic Frida Script for Android App Pen Testing

    Introduction to Android Root Detection

    Android applications often implement root detection mechanisms to enhance security, protect intellectual property, and comply with licensing agreements. Rooted devices, while offering users greater control, pose a significant risk to app developers as they can bypass security controls, inject malicious code, and access sensitive data. For penetration testers, however, bypassing these checks is a critical step in assessing an application’s true security posture, identifying vulnerabilities that might otherwise remain hidden.

    Traditional methods of bypassing root detection often involve manual patching of APKs, using Xposed modules, or complex reverse engineering. While effective, these methods can be time-consuming and difficult to adapt to different applications or updated detection logic. This article will guide you through building a dynamic and robust Frida script to automate the bypass of common Android root detection techniques, streamlining your mobile application penetration testing workflow.

    Why Frida for Root Bypass?

    Frida is a dynamic instrumentation toolkit that allows you to inject snippets of JavaScript or your own library into native apps on Windows, macOS, Linux, iOS, Android, and QNX. Its ability to hook into functions, inspect memory, and modify execution flow at runtime makes it an unparalleled tool for security research, reverse engineering, and bypassing security mechanisms like root detection without modifying the application binary itself.

    Key advantages of Frida for root bypass:

    • Dynamic Instrumentation: Modify app behavior during runtime without recompilation.
    • Cross-Platform: Works seamlessly across various Android versions and architectures.
    • Powerful API: Provides comprehensive APIs for hooking Java and native functions, memory manipulation, and interacting with the application context.
    • Rapid Prototyping: Write and test bypass logic quickly using JavaScript.

    Common Root Detection Techniques

    Before we build our bypass script, it’s essential to understand the common methods applications use to detect root. A robust Frida script should ideally address a combination of these:

    1. File and Directory Checks

      Applications often look for the presence of common root-related binaries or directories:

      • /system/bin/su
      • /system/xbin/su
      • /sbin/su
      • /data/local/su
      • /system/app/Superuser.apk
      • /data/data/com.noshufou.android.su
      • /system/xbin/busybox
      • /dev/bus/usb/001/001 (for USB debugging/ADB)
    2. Package Checks

      Checking for the installation of known root management apps:

      • com.noshufou.android.su (Superuser)
      • eu.chainfire.supersu (SuperSU)
      • com.topjohnwu.magisk (Magisk Manager)
    3. Property Checks

      Inspecting system properties for indicators of a rooted or emulator environment:

      • ro.boot.flash.locked (often 0 on rooted devices)
      • ro.secure (often 0 on rooted devices)
      • ro.debuggable (often 1 on development/rooted devices)
      • ro.build.tags (test-keys on custom ROMs/rooted devices)
    4. Command Execution Checks

      Executing commands like su -c id or which su and analyzing the output for indicators of root access.

    5. Signature/Certificate Checks

      Verifying the integrity of system applications or certificates, which can be altered on rooted devices.

    Building a Dynamic Frida Root Bypass Script

    Our Frida script will leverage the Java.perform context to hook into critical Android APIs. We’ll build a modular script that can be easily extended.

    1. Initializing the Frida Script

    All Frida Android Java hooks start within Java.perform.

    Java.perform(function() {    console.log('[+] Frida Root Bypass Script Loaded');    // Your bypass logic goes here});

    2. Bypassing File-Based Checks

    We’ll hook the java.io.File constructor and the exists() method to intercept checks for root-related files.

        var File = Java.use('java.io.File');    var paths = [        '/system/bin/su',        '/system/xbin/su',        '/sbin/su',        '/data/local/su',        '/system/app/Superuser.apk',        '/data/data/com.noshufou.android.su',        '/system/xbin/busybox',        '/dev/bus/usb/001/001',        '/system/sbin/su',        '/vendor/bin/su'    ];    File.exists.implementation = function() {        var path = this.getAbsolutePath();        if (paths.indexOf(path) > -1) {            console.log('[-] Root file detected via exists(): ' + path + ', returning false.');            return false;        }        return this.exists();    };    File.$init.overload('java.lang.String').implementation = function(path) {        if (paths.indexOf(path) > -1) {            console.log('[-] Root file detected via File constructor: ' + path + ', returning a dummy file.');            return this.$init('/dummy/path/not/exists'); // Initialize with a non-existent path        }        return this.$init(path);    };

    3. Bypassing Package Checks

    Hooking PackageManager.getPackageInfo and getApplicationInfo to hide root management packages.

        var PackageManager = Java.use('android.app.ApplicationPackageManager');    var rootPackages = [        'com.noshufou.android.su',        'eu.chainfire.supersu',        'com.topjohnwu.magisk',        'com.koushikdutta.rommanager',        'com.ramdroid.appquarantine'    ];    PackageManager.getPackageInfo.overload('java.lang.String', 'int').implementation = function(packageName, flags) {        if (rootPackages.indexOf(packageName) > -1) {            console.log('[-] Root package detected via getPackageInfo: ' + packageName + ', throwing an exception.');            throw PackageManager.NameNotFoundException.$new('Package ' + packageName + ' not found.');        }        return this.getPackageInfo(packageName, flags);    };    PackageManager.getApplicationInfo.overload('java.lang.String', 'int').implementation = function(packageName, flags) {        if (rootPackages.indexOf(packageName) > -1) {            console.log('[-] Root package detected via getApplicationInfo: ' + packageName + ', throwing an exception.');            throw PackageManager.NameNotFoundException.$new('Package ' + packageName + ' not found.');        }        return this.getApplicationInfo(packageName, flags);    };

    4. Bypassing Property Checks

    Intercepting android.os.SystemProperties.get and modifying android.os.Build.TAGS.

        var SystemProperties = Java.use('android.os.SystemProperties');    SystemProperties.get.overload('java.lang.String').implementation = function(key) {        if (key === 'ro.build.tags') {            console.log('[-] Property ' + key + ' requested, returning release-keys.');            return 'release-keys';        }        if (key === 'ro.debuggable' || key === 'ro.secure') {            console.log('[-] Property ' + key + ' requested, returning 0.');            return '0';        }        return this.get(key);    };    SystemProperties.get.overload('java.lang.String', 'java.lang.String').implementation = function(key, defaultValue) {        if (key === 'ro.build.tags') {            console.log('[-] Property ' + key + ' requested, returning release-keys.');            return 'release-keys';        }        if (key === 'ro.debuggable' || key === 'ro.secure') {            console.log('[-] Property ' + key + ' requested, returning 0.');            return '0';        }        return this.get(key, defaultValue);    };    // Also hook Build.TAGS directly if an app uses it    var Build = Java.use('android.os.Build');    Object.defineProperty(Build, 'TAGS', {        get: function() {            console.log('[-] Build.TAGS accessed, returning release-keys.');            return 'release-keys';        }    });

    5. Bypassing Command Execution Checks

    Hooking java.lang.Runtime.exec to prevent execution of root-checking commands.

        var Runtime = Java.use('java.lang.Runtime');    Runtime.exec.overload('java.lang.String').implementation = function(command) {        if (command.indexOf('su') > -1 || command.indexOf('busybox') > -1) {            console.log('[-] Command execution detected: ' + command + ', returning dummy process.');            // Return a dummy Process object or throw an exception            // For simplicity, we'll return a process that immediately finishes            return Java.use('java.lang.Process').$new();        }        return this.exec(command);    };    Runtime.exec.overload('[Ljava.lang.String;').implementation = function(cmdarray) {        var command = Java.cast(cmdarray[0], Java.use('java.lang.String'));        if (command.indexOf('su') > -1 || command.indexOf('busybox') > -1) {            console.log('[-] Command execution detected (array): ' + command + ', returning dummy process.');            return Java.use('java.lang.Process').$new();        }        return this.exec(cmdarray);    };

    Putting it all Together: The Comprehensive Script

    Here’s a consolidated version of the Frida script:

    // frida_root_bypass.jsJava.perform(function() {    console.log('[+] Frida Root Bypass Script Loaded');    // --- File-based Checks Bypass ---    var File = Java.use('java.io.File');    var rootPaths = [        '/system/bin/su', '/system/xbin/su', '/sbin/su', '/data/local/su',        '/system/app/Superuser.apk', '/data/data/com.noshufou.android.su',        '/system/xbin/busybox', '/dev/bus/usb/001/001', '/system/sbin/su',        '/vendor/bin/su', '/su/bin/su', '/su/xbin/su', '/data/su'    ];    File.exists.implementation = function() {        var path = this.getAbsolutePath();        if (rootPaths.indexOf(path) > -1) {            console.log('[-] Root file detected via exists(): ' + path + ', returning false.');            return false;        }        return this.exists();    };    File.$init.overload('java.lang.String').implementation = function(path) {        if (rootPaths.indexOf(path) > -1) {            console.log('[-] Root file detected via File constructor: ' + path + ', initializing with a dummy path.');            return this.$init('/dummy/path/not/exists');        }        return this.$init(path);    };    File.$init.overload('java.lang.String', 'java.lang.String').implementation = function(parent, child) {        var path = parent + '/' + child;        if (rootPaths.indexOf(path) > -1) {            console.log('[-] Root file detected via File constructor (parent, child): ' + path + ', initializing with a dummy path.');            return this.$init('/dummy/path/not/exists');        }        return this.$init(parent, child);    };    // --- Package-based Checks Bypass ---    var PackageManager = Java.use('android.app.ApplicationPackageManager');    var rootPackages = [        'com.noshufou.android.su', 'eu.chainfire.supersu', 'com.topjohnwu.magisk',        'com.koushikdutta.rommanager', 'com.ramdroid.appquarantine',        'com.devadvance.rootverifier', 'com.joeykrim.rootcheck'    ];    PackageManager.getPackageInfo.overload('java.lang.String', 'int').implementation = function(packageName, flags) {        if (rootPackages.indexOf(packageName) > -1) {            console.log('[-] Root package detected via getPackageInfo: ' + packageName + ', throwing exception.');            throw PackageManager.NameNotFoundException.$new('Package ' + packageName + ' not found.');        }        return this.getPackageInfo(packageName, flags);    };    PackageManager.getApplicationInfo.overload('java.lang.String', 'int').implementation = function(packageName, flags) {        if (rootPackages.indexOf(packageName) > -1) {            console.log('[-] Root package detected via getApplicationInfo: ' + packageName + ', throwing exception.');            throw PackageManager.NameNotFoundException.$new('Package ' + packageName + ' not found.');        }        return this.getApplicationInfo(packageName, flags);    };    // --- Property-based Checks Bypass ---    var SystemProperties = Java.use('android.os.SystemProperties');    SystemProperties.get.overload('java.lang.String').implementation = function(key) {        if (key === 'ro.build.tags') {            console.log('[-] Property ' + key + ' requested, returning release-keys.');            return 'release-keys';        }        if (key === 'ro.debuggable' || key === 'ro.secure') {            console.log('[-] Property ' + key + ' requested, returning 0.');            return '0';        }        return this.get(key);    };    SystemProperties.get.overload('java.lang.String', 'java.lang.String').implementation = function(key, defaultValue) {        if (key === 'ro.build.tags') {            console.log('[-] Property ' + key + ' requested, returning release-keys.');            return 'release-keys';        }        if (key === 'ro.debuggable' || key === 'ro.secure') {            console.log('[-] Property ' + key + ' requested, returning 0.');            return '0';        }        return this.get(key, defaultValue);    };    var Build = Java.use('android.os.Build');    Object.defineProperty(Build, 'TAGS', {        get: function() {            console.log('[-] Build.TAGS accessed, returning release-keys.');            return 'release-keys';        }    });    // --- Command Execution Checks Bypass ---    var Runtime = Java.use('java.lang.Runtime');    Runtime.exec.overload('java.lang.String').implementation = function(command) {        if (command.indexOf('su') > -1 || command.indexOf('busybox') > -1 || command.indexOf('which su') > -1) {            console.log('[-] Command execution detected: ' + command + ', returning dummy process.');            // Return a dummy Process object that implies success but does nothing            var process = Java.use('java.lang.Process').$new();            Java.scheduleOnMainThread(function() {                Java.cast(process, Java.use('java.lang.Object')).wait(10); // Simulate brief execution            });            return process;        }        return this.exec(command);    };    Runtime.exec.overload('[Ljava.lang.String;').implementation = function(cmdarray) {        var command = Java.cast(cmdarray[0], Java.use('java.lang.String'));        if (command.indexOf('su') > -1 || command.indexOf('busybox') > -1 || command.indexOf('which su') > -1) {            console.log('[-] Command execution detected (array): ' + command + ', returning dummy process.');            var process = Java.use('java.lang.Process').$new();            Java.scheduleOnMainThread(function() {                Java.cast(process, Java.use('java.lang.Object')).wait(10);            });            return process;        }        return this.exec(cmdarray);    };    // Add more advanced hooks here as needed, e.g., native library hooking});

    Running Your Frida Script

    To use this script, save it as frida_root_bypass.js. Ensure you have the Frida server running on your Android device (either physically connected via USB or on an emulator) and the Frida client installed on your host machine.

    First, find the package name of the target application. For example, if the app is called

  • Crafting Custom Frida Scripts for OkHttp3 SSL Pinning Bypass on Android

    Introduction to SSL Pinning and OkHttp3

    SSL (Secure Sockets Layer) pinning, more accurately TLS pinning, is a security mechanism employed by mobile applications to prevent man-in-the-middle (MITM) attacks. Instead of relying solely on the device’s trust store to validate server certificates, apps with SSL pinning embed or ‘pin’ the expected server certificate or public key directly within their codebase. This means that even if a malicious or proxy certificate is installed on the device’s trust store, the application will reject connections to any server whose certificate does not match the pre-defined pin, effectively thwarting attempts to intercept encrypted traffic.

    OkHttp3 is a popular, high-performance HTTP client for Android and Java applications, widely adopted for its efficiency and robust feature set. Many developers leverage OkHttp3’s built-in CertificatePinner class to implement SSL pinning, making it a common target for penetration testers needing to bypass this security control to analyze network traffic.

    For security researchers and penetration testers, bypassing SSL pinning is a critical step in understanding an application’s backend communication, identifying potential vulnerabilities, and assessing its overall security posture. While generic SSL bypass scripts exist, they often fall short when dealing with specific implementations like OkHttp3 due to varying versions, obfuscation, or custom configurations. This article provides a detailed guide on crafting custom Frida scripts to effectively bypass OkHttp3 SSL pinning on Android applications.

    Prerequisites for Android App Penetration Testing with Frida

    Before diving into the custom script development, ensure you have the following tools and setup ready:

    • Rooted Android Device or Emulator: Necessary for running Frida-server.
    • Android Debug Bridge (ADB): For interacting with the Android device (pushing files, running shell commands).
    • Frida-server: The Frida agent running on the Android device. Download the correct architecture from Frida releases.
    • Frida-tools: The Python tools (frida, frida-trace) installed on your host machine (pip install frida-tools).
    • Intercepting Proxy (e.g., Burp Suite, OWASP ZAP): To capture and analyze HTTP/HTTPS traffic. Ensure its CA certificate is installed on your Android device.
    • Decompiler (e.g., Jadx-GUI, Ghidra): Useful for static analysis to understand the application’s code structure and identify relevant classes/methods.

    Understanding OkHttp3’s SSL Pinning Mechanism

    How OkHttp3 Implements Pinning

    OkHttp3 implements SSL pinning primarily through its okhttp3.CertificatePinner class. Developers instantiate this class, often via a CertificatePinner.Builder, and add pins (SHA256 hashes of a certificate’s public key) for specific hostnames. When an HTTP request is made to a pinned host, OkHttp3 invokes the check() method of the CertificatePinner instance to validate the server’s certificate chain against the stored pins. If no match is found, the connection is aborted, preventing interception.

    A typical OkHttp3 client with pinning might look like this in Java/Kotlin:

    OkHttpClient client = new OkHttpClient.Builder() .certificatePinner(new CertificatePinner.Builder() .add("publicobject.com", "sha256/afwiKYADmwJougNRQzCgC…") .add("publicobject.com", "sha256/abcd…") .build()) .build();

    The critical methods for our bypass strategies are often okhttp3.CertificatePinner.check() and methods within okhttp3.CertificatePinner$Builder.

    Limitations of Generic SSL Bypass Scripts

    Many widely available Frida SSL bypass scripts aim to hook common Java crypto APIs (e.g., X509TrustManager, HostnameVerifier) or native SSL/TLS functions (e.g., BoringSSL/OpenSSL). While effective for many applications, these generic scripts might fail against OkHttp3 for several reasons:

    • OkHttp3’s CertificatePinner performs its checks at a higher level, potentially bypassing the generic hooks.
    • Applications might implement custom TrustManager interfaces or use different libraries.
    • Obfuscation (e.g., ProGuard, R8) can rename classes and methods, rendering hardcoded hooks ineffective.
    • Specific OkHttp3 versions might have slightly different method signatures or internal logic.

    This necessitates a targeted approach, focusing directly on the OkHttp3 pinning logic.

    Dynamic Analysis: Identifying Pinning Logic with Frida

    Before writing a custom script, it’s beneficial to understand where the pinning occurs. This can be done via static or dynamic analysis.

    Static Analysis (Decompilation) for Clues

    Use a decompiler like Jadx-GUI. Load the application’s APK and search for strings like

  • Frida for Penetration Testers: Seamless OkHttp3 SSL Pinning Bypass on Android

    Introduction to SSL Pinning and the Penetration Tester’s Challenge

    SSL pinning is a security mechanism employed by mobile applications to prevent man-in-the-middle (MiTM) attacks on network traffic. Instead of relying solely on the device’s trust store, applications ‘pin’ specific server certificates or public keys. This means the app will only trust connections to servers presenting one of these pre-defined certificates, effectively rejecting any others, including those issued by a proxy like Burp Suite. While this enhances security, it poses a significant challenge for penetration testers who need to intercept and analyze app traffic.

    Traditional methods like installing custom CA certificates in the device’s trust store often fail against SSL pinning. This is where dynamic instrumentation tools like Frida come into play. Frida allows us to inject JavaScript into running processes, modify their behavior at runtime, and effectively bypass security controls like SSL pinning without needing to recompile the application.

    Understanding OkHttp3 SSL Pinning Implementation

    OkHttp3 is a popular HTTP client for Android and Java applications, renowned for its efficiency and robust features, including built-in SSL pinning capabilities. OkHttp3 implements SSL pinning primarily through its CertificatePinner class. Developers configure this pinner with a list of expected certificate hashes (SHA-1 or SHA-256) for specific hostnames. When a connection is established, the CertificatePinner intercepts the chain of certificates presented by the server and verifies if any of its public keys match the pinned hashes. If no match is found, the connection is aborted, typically resulting in an SSLHandshakeException.

    A typical OkHttp3 client configured with pinning might look like this in Java/Kotlin:

    OkHttpClient client = new OkHttpClient.Builder()
        .certificatePinner(new CertificatePinner.Builder()
            .add("example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
            .build())
        .build();
    

    Our goal is to target and neutralize the functionality of this CertificatePinner at runtime.

    Prerequisites for Frida-based Bypass

    Before diving into the bypass, ensure you have the following setup:

    • Rooted Android Device or Emulator: Frida requires root privileges to inject into applications.
    • ADB (Android Debug Bridge): For connecting to your device and pushing files.
    • Frida Server: Running on your Android device.
    • Frida Client: Installed on your host machine (pip install frida-tools).
    • Target Android Application: An APK that uses OkHttp3 and implements SSL pinning.
    • Burp Suite (or similar proxy): To intercept and verify traffic.

    Setting up Frida Server on Android

    1. Download the correct Frida server for your device’s architecture from Frida Releases (e.g., frida-server-*-android-arm64).
    2. Push it to your device and make it executable:

    adb push frida-server /data/local/tmp/
    adb shell "chmod 755 /data/local/tmp/frida-server"
    

    3. Start the Frida server on the device (you can run this in a separate adb shell or background it):

    adb shell "/data/local/tmp/frida-server &"
    

    4. Verify it’s running by listing processes:

    frida-ps -U
    

    Identifying the Target Method for Hooking

    For OkHttp3’s CertificatePinner, the critical method to hook is check. This method is responsible for performing the actual certificate validation against the pinned hashes. By hooking this method and making it return immediately, we can effectively bypass the pinning logic.

    You can verify the exact method signature using tools like `jadx-gui` or by decompiling the APK with `apktool` and inspecting the Smali code or Java source. Typically, the relevant check method in okhttp3.CertificatePinner takes a java.lang.String (hostname) and a java.util.List (of peer certificates) as arguments.

    Crafting the Frida JavaScript Hook

    Our Frida script will use `Java.perform` to ensure the hook executes within the Java VM context. We then use `Java.use` to get a handle to the `okhttp3.CertificatePinner` class. The crucial part is identifying the correct `check` method overload. The most common signature for the pinning check is `check(String hostname, List peerCertificates)`.

    Java.perform(function() {
        console.log("[+] Starting OkHttp3 SSL pinning bypass script...");
    
        try {
            var CertificatePinner = Java.use('okhttp3.CertificatePinner');
    
            // Hooking the 'check' method that takes a hostname and a list of certificates
            CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function(hostname, peerCertificates) {
                console.log("[+] OkHttp3 CertificatePinner.check() bypassed for host: " + hostname);
                // Simply return without calling the original implementation,
                // effectively disabling the pinning check for this invocation.
                return;
            };
            console.log("[+] OkHttp3 CertificatePinner.check(String, List) hook applied successfully.");
    
        } catch (e) {
            console.error("[-] Error hooking OkHttp3 CertificatePinner: " + e.message);
        }
    
        // Additionally, some apps might use other overloads or alternative pinning methods.
        // A common variant to consider is 'check(String hostname, Certificate... certificates)'
        // if the list is passed as varargs, though less common with the public API.
        // Or even direct calls to 'findMatchingCertificates'.
    
        // For completeness, if the app uses a custom TrustManager, you might need to hook
        // 'checkClientTrusted' and 'checkServerTrusted' methods in 'javax.net.ssl.X509TrustManager'.
        // However, for standard OkHttp3 pinning, the above hook is usually sufficient.
    });
    

    Explanation of the Script

    • Java.perform(function() { ... });: Ensures that our JavaScript code runs within the context of the Android application’s Java VM.
    • var CertificatePinner = Java.use('okhttp3.CertificatePinner');: Obtains a reference to the `okhttp3.CertificatePinner` class, allowing us to interact with its methods.
    • CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function(...) { ... };: This is the core of the bypass. We specifically target the `check` method that accepts a `String` (for the hostname) and a `List` (for the peer certificates). By setting its `implementation`, we replace the original method’s logic with our own. Our custom implementation simply prints a message and then returns immediately, effectively skipping the entire certificate validation logic that would otherwise throw an exception.

    Executing the Frida Script

    Save the above JavaScript code to a file, for example, `okhttp3_bypass.js`. Then, execute it against your target application using Frida:

    frida -U -f com.example.targetapp -l okhttp3_bypass.js --no-pause
    
    • -U: Specifies that Frida should attach to a USB-connected device.
    • -f com.example.targetapp: Spawns and attaches to the application with the package name `com.example.targetapp`. Replace this with your actual target app’s package name.
    • -l okhttp3_bypass.js: Loads our JavaScript bypass script.
    • --no-pause: Prevents Frida from pausing the application at startup, allowing it to run normally while our script is injected.

    Once the command is executed, Frida will inject your script, and any subsequent network connections made by the OkHttp3 client within the application should now bypass the SSL pinning checks. You will see console messages indicating the bypass is active for each host encountered.

    Verification and Interception

    With the Frida script running, configure your Android device to proxy its network traffic through Burp Suite. Open Burp Suite, ensure the proxy listener is active, and the Burp CA certificate is installed on your device (in the user trust store, if not already). Now, interact with the target application. You should be able to intercept and inspect all network requests and responses from the application in Burp Suite, confirming the successful SSL pinning bypass.

    Conclusion

    Frida is an indispensable tool for advanced mobile application penetration testing. By understanding how common libraries like OkHttp3 implement security features like SSL pinning, penetration testers can craft precise and effective runtime hooks. The ability to dynamically modify application behavior provides an unparalleled advantage in security assessments, allowing for thorough analysis of network communications even when strong defensive measures are in place. This targeted OkHttp3 bypass demonstrates the power and flexibility of Frida for overcoming modern mobile security challenges.

  • Zero to Bypass: Crafting Custom Frida Scripts for Unique Android Root Detection Challenges

    Introduction to Android Root Detection and Frida

    Android applications, especially those handling sensitive data like banking apps or games, often implement robust root detection mechanisms. These checks are designed to prevent the app from running on compromised devices, where security guarantees are diminished, and attackers might gain privileged access to manipulate app behavior or data. For penetration testers and security researchers, bypassing these checks is a fundamental skill in assessing the true security posture of an application.

    Frida, a dynamic instrumentation toolkit, stands out as the ultimate weapon in this cat-and-mouse game. It allows you to inject snippets of JavaScript (or other languages) into native apps on Windows, macOS, GNU/Linux, iOS, Android, and QNX. With Frida, you can hook into functions, inspect memory, modify arguments, and even call unexported functions, making it incredibly powerful for reversing and bypassing security controls on Android.

    Common Root Detection Methodologies

    Before we can bypass, we must understand what we’re up against. Android apps employ various techniques to detect a rooted environment:

    File System Checks

    One of the most common methods involves checking for the existence of specific files or directories commonly associated with a rooted device or rooting tools. This includes paths like:

    • /system/app/Superuser.apk
    • /sbin/su
    • /system/bin/su
    • /system/xbin/su
    • /data/local/xbin/su
    • /data/local/bin/su
    • /system/sd/xbin/su
    • /system/bin/failsafe/su
    • /data/local/su
    • /magisk/.core/magisk (for Magisk root)
    • /system/app/MagiskManager (for Magisk Manager)

    A typical Java implementation might look like this:

    public boolean isRootedFileExists() {  String[] paths = {    "/sbin/su", "/system/bin/su", "/system/xbin/su",    "/data/local/xbin/su", "/data/local/bin/su",    "/system/sd/xbin/su", "/system/bin/failsafe/su",    "/data/local/su", "/magisk/.core/magisk"  };  for (String path : paths) {    if (new File(path).exists()) {      return true;    }  }  return false;}

    Package Name Checks

    Applications can also check for the presence of known root-management apps or other security tools by querying the device’s installed packages:

    • com.noshufou.android.su (Superuser)
    • eu.chainfire.supersu (SuperSU)
    • com.topjohnwu.magisk (Magisk Manager)
    • de.robv.android.xposed.installer (Xposed Installer)

    Example Java code:

    public boolean isRootRelatedPackageInstalled(Context context) {  PackageManager pm = context.getPackageManager();  List<ApplicationInfo> packages = pm.getInstalledApplications(0);  String[] rootPackages = {    "com.noshufou.android.su", "eu.chainfire.supersu",    "com.topjohnwu.magisk", "de.robv.android.xposed.installer"  };  for (ApplicationInfo packageInfo : packages) {    for (String rootPackage : rootPackages) {      if (packageInfo.packageName.equals(rootPackage)) {        return true;      }    }  }  return false;}

    Property Checks

    Certain system properties can indicate a rooted or emulated environment, such as:

    • ro.build.tags=test-keys
    • ro.secure=0
    • ro.debuggable=1

    These can be checked using System.getProperty() or by executing shell commands like getprop.

    public boolean checkSystemProperties() {  String buildTags = android.os.Build.TAGS;  if (buildTags != null && buildTags.contains("test-keys")) {    return true;  }  // Can also execute "getprop ro.secure" via Runtime.exec() and check output  return false;}

    Signature/Certificate Checks

    Some advanced apps verify the integrity of their own signature or other system components, or even check for known custom ROM certificates. While powerful, this is generally harder to bypass with basic Frida hooks and often requires patching the APK directly.

    Setting Up Your Frida Environment

    Before diving into custom scripts, ensure your Frida environment is ready:

    Prerequisites

    • Python 3: Required for frida-tools.
    • Android SDK & ADB: For interacting with your Android device.
    • Rooted Android Device or Emulator: With USB debugging enabled.
    • Frida-server: The Frida agent running on your Android device.

    Installation Steps

    1. Install Frida Tools on your host machine:
      pip install frida-tools
    2. Download Frida-server: Go to the Frida releases page and download the frida-server binary matching your device’s architecture (e.g., arm64, x86_64) and Frida version.
    3. Push Frida-server to your device:
      adb push /path/to/frida-server /data/local/tmp/frida-server
    4. Set executable permissions and run Frida-server:
      adb shell"""chmod 755 /data/local/tmp/frida-server/data/local/tmp/frida-server &"""
    5. Verify Frida-server is running:
      frida-ps -U

      This command should list processes on your USB-connected device.

    Identifying Root Checks with Dynamic Analysis

    Identifying *where* root checks occur is crucial. We combine static and dynamic analysis.

    Initial Reconnaissance with frida-trace

    frida-trace is an excellent starting point. It allows you to hook common Android APIs and log their calls, helping you pinpoint potential root detection points. For example, to trace file system access and package manager queries:

    frida-trace -U -f com.example.app --decorate -i "*File.exists*" -i "*PackageManager.getPackageInfo*" -i "*Runtime.exec*" -i "*System.getProperty*"

    Run the app, trigger the root check (if possible), and observe the output. This will show you which methods are being called. If you see repeated calls to File.exists("/sbin/su"), you’ve found a target.

    Decompilation and Static Analysis

    Use tools like Jadx-GUI or Ghidra to decompile the target APK. Search for keywords like

  • Advanced Frida Hooks: Conquering Stubborn OkHttp3 SSL Pinning in Android Apps

    Introduction: The SSL Pinning Challenge

    SSL Pinning is a critical security measure implemented in mobile applications to prevent man-in-the-middle (MITM) attacks. By hardcoding or ‘pinning’ trusted server certificates or public keys within the application itself, an app can verify that it’s communicating with the legitimate server, even if the device’s root certificate store has been compromised. While excellent for security, this poses a significant hurdle for penetration testers and security researchers who need to intercept network traffic to analyze application behavior and vulnerabilities.

    OkHttp3, a popular HTTP client for Android and Java applications, offers robust SSL pinning capabilities through its CertificatePinner and custom X509TrustManager implementations. Generic Frida scripts or common tools like Objection often fall short against sophisticated OkHttp3 pinning, necessitating a more advanced, targeted approach using custom Frida hooks.

    Prerequisites for Battle

    Before diving into the advanced hooking techniques, ensure you have the following tools and a suitable environment:

    • A rooted Android device or emulator (e.g., AVD, Genymotion, Nox Player).
    • adb (Android Debug Bridge) installed and configured.
    • Frida client (Python package) and Frida server installed on your development machine and Android device, respectively.
    • Burp Suite or another proxy for traffic interception and verification.
    • Basic familiarity with Java/Kotlin, Android application structure, and Frida.
    # Install Frida client on your machine
    pip install frida-tools
    
    # Download Frida server for your Android device's architecture (e.g., arm64)
    # Check device architecture:
    adb shell getprop ro.product.cpu.abi
    
    # Download from GitHub releases: https://github.com/frida/frida/releases
    # Example for arm64:
    wget https://github.com/frida/frida/releases/download/16.1.4/frida-server-16.1.4-android-arm64.xz
    xz -d frida-server-16.1.4-android-arm64.xz
    
    # Push and run Frida server on device
    adb push frida-server-16.1.4-android-arm64 /data/local/tmp/
    adb shell "chmod 755 /data/local/tmp/frida-server-16.1.4-android-arm64"
    adb shell "/data/local/tmp/frida-server-16.1.4-android-arm64 &"

    Understanding OkHttp3’s Pinning Mechanisms

    OkHttp3 offers two primary ways to implement SSL pinning:

    1. CertificatePinner:

      This is OkHttp’s built-in mechanism. Developers configure a list of trusted certificates or public key hashes for specific hosts. During the TLS handshake, OkHttp checks if the server’s certificate chain contains one of the pinned certificates/keys. If not, the connection is aborted. This is typically configured directly within the OkHttpClient.Builder.

    2. Custom X509TrustManager:

      More advanced implementations might create a custom X509TrustManager. This interface is part of Java’s JSSE (Java Secure Socket Extension) and is responsible for validating certificate chains. By implementing a custom TrustManager, developers gain fine-grained control over the validation logic, potentially adding custom checks beyond simple pinning or integrating with enterprise-specific certificate authorities. This custom TrustManager is then supplied to an SSLContext, which is used by OkHttp.

    Our advanced Frida strategy will primarily target the X509TrustManager, as it’s a more fundamental component of the SSL/TLS handshake and can often bypass even robust CertificatePinner configurations if we can replace or manipulate the trust logic.

    Limitations of Generic Bypasses

    Many common Frida scripts for SSL pinning bypass target generic Android TrustManager classes or specific methods like checkServerTrusted without considering the nuances of custom implementations. Objection’s android sslpinning disable often works by hooking these common methods. However, if an application:

    • Uses its own custom, obfuscated X509TrustManager class.
    • Dynamically loads trust anchors.
    • Implements additional certificate checks outside the standard checkServerTrusted flow (e.g., directly in application logic or within custom network interceptors).

    …then these generic bypasses will likely fail, leading to connection errors or application crashes. Our goal is to achieve a more universal bypass by replacing the entire trust evaluation logic.

    Advanced Frida Hooking: Replacing the TrustManager

    The most robust way to bypass stubborn OkHttp3 SSL pinning is to completely replace the application’s X509TrustManager with our own ‘trust-all’ manager. This ensures that any certificate presented by the server, including those from our proxy, will be accepted.

    Step 1: Identifying the Target TrustManager

    Sometimes, the application might use a custom subclass of X509TrustManager. While we can try to hook javax.net.ssl.X509TrustManager directly, a more reliable approach is to target the SSLContext.init() method. This method takes an array of TrustManager objects, allowing us to inspect or replace them at the point where the secure context is initialized.

    You can use Frida’s Java.enumerateLoadedClasses() or Objection’s android hooking search classes X509TrustManager to identify potential custom TrustManager classes, but for a universal approach, focusing on SSLContext.init() is often better.

    Step 2: Crafting the Frida Script

    Our Frida script will perform the following actions:

    1. Define a custom X509TrustManager that accepts all certificates.
    2. Hook javax.net.ssl.SSLContext.init().
    3. Inside the hook, replace any existing TrustManager array with an array containing only our custom ‘trust-all’ manager.
    4. Additionally, we’ll include common generic bypasses for good measure, covering other potential pinning implementations.
    Java.perform(function () {
        console.log('[+] Starting SSL pinning bypass...');
    
        // 1. Create a custom TrustManager that trusts all certificates
        var TrustManager = Java.use('javax.net.ssl.X509TrustManager');
        var MyTrustManager = Java.registerClass({
            name: 'com.example.MyTrustManager',
            implements: [TrustManager],
            methods: {
                checkClientTrusted: function (chain, authType) {},
                checkServerTrusted: function (chain, authType) {},
                getAcceptedIssuers: function () {
                    return Java.array('java.security.cert.X509Certificate', []);
                }
            }
        });
    
        // 2. Hook SSLContext.init()
        var SSLContext = Java.use('javax.net.ssl.SSLContext');
        SSLContext.init.overload('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom').implementation = function (keyManagers, trustManagers, secureRandom) {
            console.log('[+] SSLContext.init() called. Replacing TrustManagers.');
            
            // Create an array with our custom trust manager
            var trustManagersArray = Java.array('javax.net.ssl.TrustManager', [MyTrustManager.$new()]);
    
            // Call the original init with our new trust managers
            this.init(keyManagers, trustManagersArray, secureRandom);
        };
    
        // 3. Generic bypasses for other pinning mechanisms (e.g., Conscrypt, WebView)
        try {
            var CertificateFactory = Java.use('java.security.cert.CertificateFactory');
            var FileInputStream = Java.use('java.io.FileInputStream');
            var BufferedInputStream = Java.use('java.io.BufferedInputStream');
            var X509Certificate = Java.use('java.security.cert.X509Certificate');
            var KeyStore = Java.use('java.security.KeyStore');
            var TrustManagerFactory = Java.use('javax.net.ssl.TrustManagerFactory');
            var SSLContext = Java.use('javax.net.ssl.SSLContext');
    
            // Bypass CertificateFactory.generateCertificates
            CertificateFactory.generateCertificates.implementation = function(inputStream) {
                console.log('[+] Bypassing CertificateFactory.generateCertificates');
                return Java.cast(Java.array('java.security.cert.Certificate', []), Java.use('java.security.cert.Certificate[]'));
            };
    
            // Bypass CertificatePinner in OkHttp3 directly
            try {
                var CertificatePinner = Java.use('okhttp3.CertificatePinner');
                CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function() {
                    console.log('[+] Bypassing OkHttp3 CertificatePinner.check(hostname, certificates)');
                    return;
                };
                CertificatePinner.check.overload('java.lang.String', '[Ljava.security.cert.Certificate;').implementation = function() {
                    console.log('[+] Bypassing OkHttp3 CertificatePinner.check(hostname, certs)');
                    return;
                };
                console.log('[+] OkHttp3 CertificatePinner hooks applied.');
            } catch (e) {
                console.log('[-] OkHttp3 CertificatePinner not found or hook failed: ' + e.message);
            }
    
            // Hook Android's standard TrustManagerFactory
            TrustManagerFactory.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String').implementation = function(chain, authType) {
                console.log('[+] TrustManagerFactory.checkServerTrusted Bypassed');
                return;
            };
    
        } catch (e) {
            console.log('[-] Generic bypasses failed: ' + e.message);
        }
    
        console.log('[+] SSL pinning bypass finished.');
    });

    Step 3: Running the Frida Script

    Save the script as okhttp3_ssl_bypass.js. Then, execute it using Frida, targeting your application’s package name:

    # To spawn the app and inject the script
    frida -U -f com.your.packagename -l okhttp3_ssl_bypass.js --no-pause
    
    # If the app is already running, attach to it
    # frida -U com.your.packagename -l okhttp3_ssl_bypass.js

    Replace com.your.packagename with the actual package name of the target Android application. The --no-pause flag ensures that the application doesn’t pause after spawning, allowing the hooks to be active from the start.

    Verification with Burp Suite

    Once the Frida script is running, configure your Android device to proxy all traffic through Burp Suite (or your chosen proxy). Ensure Burp Suite’s CA certificate is installed on the device (usually in the user certificate store).

    Launch the target application and observe Burp Suite. If the bypass is successful, you should see the application’s network requests appearing in Burp’s HTTP history, and the application should function normally without any SSL errors. If you still encounter issues, examine the Frida console output for errors or indications of other pinning mechanisms at play.

    Conclusion

    Bypassing advanced SSL pinning implementations in Android applications, especially those leveraging OkHttp3’s robust features or custom X509TrustManager, requires a deep understanding of Java’s security architecture and Frida’s dynamic instrumentation capabilities. By replacing the core trust evaluation mechanism via SSLContext.init(), we can achieve a highly effective and universal bypass. While this method is powerful, remember that application security is an arms race, and developers are constantly evolving their pinning strategies. Continuous research and adaptation of hooking techniques are essential for successful mobile application penetration testing.