Author: admin

  • Reverse Engineering Root Detection: Crafting Custom Objection Scripts with Frida

    Introduction: The Cat and Mouse Game of Root Detection

    Root detection mechanisms are a common defense implemented by Android application developers to prevent their apps from running on rooted devices. This is particularly prevalent in banking, gaming, and enterprise applications where integrity and security are paramount. For penetration testers and security researchers, bypassing these controls is a crucial step in assessing an application’s true security posture. While tools like Objection provide powerful built-in functionalities for bypassing common detections, the ever-evolving nature of app security often requires a more bespoke approach. This article will guide you through reverse engineering root detection techniques and crafting custom Frida scripts, seamlessly integrated with Objection, to overcome even sophisticated checks.

    Understanding Common Root Detection Mechanisms

    Before we can bypass root detection, we must understand how it works. Android apps typically employ several strategies:

    • File-based Checks: Scanning for common root binaries or files like /system/bin/su, /system/xbin/su, /data/local/su, /sbin/su, /system/app/Superuser.apk, or Magisk-related files.
    • Package-based Checks: Looking for known root management applications such as SuperSU, Magisk Manager, or Xposed Installer.
    • Property-based Checks: Inspecting system properties like ro.secure, ro.debuggable, or ro.build.tags for signs of a test build or development environment.
    • Dangerous Permissions: Checking if the app has permissions indicative of a rooted environment or that it shouldn’t normally have.
    • Signature/Certificate Checks: Verifying the integrity of system libraries or the application itself against known signatures.
    • Native Library Checks: Performing checks within native C/C++ code, which can be harder to detect and hook from Java.
    • Debugging Checks: Detecting if a debugger is attached.

    Setting Up Your Android Penetration Testing Environment

    To follow along, you’ll need:

    1. A rooted Android device or emulator (e.g., AVD, Genymotion, or a physical device with Magisk).
    2. Frida-server running on the target Android device. Ensure the architecture matches (e.g., frida-server-16.1.4-android-arm64 for an arm64 device).
      adb push frida-server /data/local/tmp/frida-serveradb shell 'chmod 755 /data/local/tmp/frida-server'adb shell '/data/local/tmp/frida-server &'
    3. Frida-tools and Objection installed on your host machine.
      pip install frida-tools objection

    Initial Bypass with Objection’s Built-in Features

    Objection provides a quick way to bypass common root detections. Let’s assume you have an app named com.example.app:

    objection -g com.example.app explore

    Once inside the Objection console, you can try:

    android root disableandroid sslpinning disable

    These commands often work for basic detections. However, more advanced applications might employ custom or obfuscated checks that these generic methods miss.

    Reverse Engineering a Custom Root Detection (Simulated Scenario)

    Let’s simulate an app that checks for root by listing directories or files. A common pattern is to check for the existence of /system/bin/su or /sbin/magisk.

    Step 1: Identify Potential Root Checks using Frida-Trace

    We can use frida-trace to monitor calls to methods that interact with the file system, such as java.io.File.exists() or java.lang.Runtime.exec().

    frida-trace -U -f com.example.app -i

  • Objection.js Masterclass: A Practical Guide to Bypassing Android Root Detection

    Introduction: Unmasking Root Detection and Empowering Pen-Testers

    Root detection is a ubiquitous security measure implemented in many Android applications, particularly those handling sensitive data like banking, payment, or DRM-protected content. Its primary purpose is to prevent the app from running on a compromised device, thereby mitigating risks associated with malware, unauthorized access to private data, or manipulation of app logic. For penetration testers and security researchers, bypassing these root detection mechanisms is a critical skill, as it allows for deeper analysis of application behavior, identification of vulnerabilities, and validation of security controls. This masterclass will dive deep into using Objection.js, a powerful runtime mobile exploration toolkit built on Frida, to effectively bypass various Android root detection techniques.

    Prerequisites: Tools of the Trade

    Before we begin our journey into bypassing root detection, ensure you have the following tools set up on your host machine and Android device/emulator:

    • Android Debug Bridge (ADB): For interacting with your Android device.
    • Frida-server: The Frida agent running on the target Android device. Ensure the architecture (ARM, ARM64, x86) matches your device.
    • Frida-tools: Python packages including frida, frida-ps, etc., installed on your host machine.
    • Objection: The main tool for our exploration, also installed on your host via pip.
    • A Rooted Android Device or Emulator: Necessary to observe root detection in action and test bypasses.

    Installation commands:

    pip install frida-tools objection

    Deploying Frida-server:

    # Download the correct frida-server for your device's architecture (e.g., frida-server-16.1.4-android-arm64)adb push /path/to/frida-server /data/local/tmp/frida-serveradb shell

  • The Ultimate Guide to Bypassing OkHttp3 SSL Pinning with Frida on Any Android App

    Introduction to SSL Pinning and Its Challenges

    SSL (Secure Sockets Layer) pinning is a security mechanism employed by applications to prevent man-in-the-middle (MITM) attacks. Instead of relying solely on the device’s trust store, applications “pin” specific certificates or public keys that they trust. During an SSL handshake, the application verifies if the server’s certificate matches one of its pinned certificates. If there’s a mismatch, the connection is terminated, safeguarding sensitive data from interception.

    While excellent for security, SSL pinning poses a significant challenge for penetration testers and security researchers. To effectively analyze an application’s network traffic for vulnerabilities, we often need to proxy its connections through tools like Burp Suite or OWASP ZAP. These proxies typically present their own certificates, which are not the ones pinned by the application, thus causing the connection to fail. This guide focuses on bypassing SSL pinning specifically implemented using OkHttp3, a popular HTTP client library in Android, by leveraging the powerful dynamic instrumentation toolkit, Frida.

    Prerequisites for the Bypass

    Before diving into the technical steps, ensure you have the following tools and setup ready:

    • Rooted Android Device or Emulator: A device with root access (e.g., Magisk) is essential for running the Frida server.
    • ADB (Android Debug Bridge): For communicating with your Android device from your host machine.
    • Frida: Both the Frida client (on your host machine) and the Frida server (on your Android device).
    • Python 3: Frida’s client-side tools are primarily Python-based.
    • Proxy Tool: Burp Suite, OWASP ZAP, or any similar tool capable of intercepting and manipulating HTTP/S traffic.
    • Target Android Application: The application you intend to bypass SSL pinning on.

    Frida Setup Quickstart

    On your host machine, install Frida:

    pip install frida-tools

    On your Android device, download the correct Frida server for your device’s architecture (e.g., frida-server-*-android-arm64). You can check your device’s architecture using adb shell getprop ro.product.cpu.abi.

    Push the Frida server to your device, make it executable, and run it:

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

  • Real-World Case Study: Bypassing SSL Pinning in Android Apps with Frida & OkHttp3 Hooks

    Introduction: The Challenge of SSL Pinning

    SSL Pinning is a security mechanism implemented by developers to prevent man-in-the-middle (MITM) attacks. Instead of relying on the system’s trust store for Certificate Authorities (CAs), applications with SSL pinning explicitly ‘pin’ or hardcode specific server certificates or public keys within their code. This means the application will only trust connections to servers presenting one of these pre-approved certificates, even if a valid CA-signed certificate is presented by an attacker’s proxy.

    Why Bypass SSL Pinning?

    For penetration testers and security researchers, bypassing SSL pinning is often a critical step in understanding an application’s network communication. Without it, intercepting traffic using tools like Burp Suite or OWASP ZAP is impossible, hindering the ability to analyze API calls, data transmission, and potential vulnerabilities. This tutorial focuses on a common scenario: bypassing SSL pinning implemented using the OkHttp3 library, a widely adopted HTTP client for Android.

    Tools of the Trade: Frida & OkHttp3

    Frida: The Dynamic Instrumentation Toolkit

    Frida is an open-source dynamic instrumentation toolkit that allows you to inject snippets of JavaScript or your own library into native apps on Windows, macOS, GNU/Linux, iOS, Android, and QNX. It’s incredibly powerful for security research, reverse engineering, and app analysis because it operates at runtime, enabling modifications to application logic and observation of internal behavior without needing to decompile, modify, and recompile the application.

    OkHttp3: A Common Target

    OkHttp3 is a popular, open-source HTTP client developed by Square, widely used in Android applications for making network requests. It offers features like connection pooling, GZIP compression, and response caching. Crucially for our purpose, OkHttp3 provides robust mechanisms for implementing SSL pinning, primarily through its CertificatePinner class, making it a frequent target for bypass attempts.

    Prerequisites for Your Lab

    Before we dive into the bypass, ensure you have the following setup:

    • Rooted Android Device or Emulator: Necessary for running Frida server with root privileges.
    • ADB (Android Debug Bridge): For interacting with your Android device.
    • Frida Server: Installed and running on your Android device.
    • Frida Client: Installed on your host machine (usually via pip install frida-tools).
    • Proxy Tool: Such as Burp Suite or mitmproxy, configured to intercept traffic from your Android device. Ensure your proxy’s CA certificate is installed on the Android device (even though pinning bypasses it, it’s good practice for other traffic).
    • Target Application: An Android application that uses OkHttp3 and implements SSL pinning.

    Step-by-Step Guide: Bypassing OkHttp3 SSL Pinning

    Step 1: Setting Up Your Environment

    First, push the Frida server to your device and run it. Replace `[frida-server-version]` with the appropriate version for your device’s architecture (e.g., `frida-server-16.1.4-android-arm64`).

    adb push frida-server-[frida-server-version]-android-arm64 /data/local/tmp/frida-serveradb shell 'chmod 755 /data/local/tmp/frida-server'adb shell '/data/local/tmp/frida-server &'

    Next, configure your Android device to proxy all traffic through your host machine’s Burp Suite (or similar tool). Go to Wi-Fi settings, modify your network, and set up a manual proxy pointing to your host machine’s IP address and Burp Suite’s listening port (e.g., 8080).

    Step 2: Identifying the Pinning Mechanism (Optional but Recommended)

    While we’re targeting OkHttp3’s CertificatePinner, a quick check can confirm if the app indeed uses it. You can decompile the APK using tools like Apktool or MobSF and search for strings like `CertificatePinner`, `okhttp3`, or `pinning`. This step helps confirm the attack vector.

    Step 3: Crafting the Frida Script for OkHttp3

    The core of our bypass lies in a Frida script that hooks into OkHttp3’s SSL pinning logic. Specifically, we will target the check method of okhttp3.CertificatePinner and force it to return without performing any checks. Additionally, it’s good practice to try to disable pinning at the X509TrustManager level, as some applications might implement custom trust logic there.

    Create a file named okhttp3_bypass.js with the following content:

    Java.perform(function() {    console.log('--- OkHttp3 SSL Pinning Bypass Script Loaded ---');    try {        // Hook CertificatePinner.check()        var CertificatePinner = Java.use('okhttp3.CertificatePinner');        if (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                return;            };            console.log('okhttp3.CertificatePinner.check() hook applied successfully.');        }    } catch (e) {        console.log('Failed to hook okhttp3.CertificatePinner.check(): ' + e.message);    }    try {        // Attempt to hook other common pinning mechanisms if they exist        // This targets the TrustManager that performs actual certificate validation.        var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager');        var SSLContext = Java.use('javax.net.ssl.SSLContext');        var TrustManagerFactory = Java.use('javax.net.ssl.TrustManagerFactory');        var KeyStore = Java.use('java.security.KeyStore');        var TrustManager = Java.use('javax.net.ssl.TrustManager');        var ArrayList = Java.use('java.util.ArrayList');        var Arrays = Java.use('java.util.Arrays');        var X509Certificate = Java.use('java.security.cert.X509Certificate');        // Custom TrustManager that accepts any certificate        var CustomTrustManager = Java.registerClass({            name: 'com.example.ssl.CustomTrustManager',            implements: [X509TrustManager],            methods: {                checkClientTrusted: function(chain, authType) {                    console.log('CustomTrustManager: checkClientTrusted - ALL TRUSTED');                },                checkServerTrusted: function(chain, authType) {                    console.log('CustomTrustManager: checkServerTrusted - ALL TRUSTED');                },                getAcceptedIssuers: function() {                    console.log('CustomTrustManager: getAcceptedIssuers');                    return [];                }            }        });        // Replace default TrustManagers with our custom one        SSLContext.init.overload('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom').implementation = function(keyManagers, trustManagers, secureRandom) {            console.log('SSLContext.init hook: Replacing TrustManagers');            var customTrustManagers = ArrayList.$new();            customTrustManagers.add(CustomTrustManager.$new());            this.init(keyManagers, customTrustManagers.toArray(), secureRandom);        };        TrustManagerFactory.init.overload('java.security.KeyStore').implementation = function(keyStore) {            console.log('TrustManagerFactory.init hook: Bypassing KeyStore init');            // We don't want the factory to initialize with a specific keystore,            // so we'll just call the original method with null or let it fail silently            // if the original is bypassed. For now, we'll try to let it pass            // to ensure other components aren't broken, but our SSLContext hook should win.            return this.init(keyStore);        };        TrustManagerFactory.getTrustManagers.implementation = function() {            console.log('TrustManagerFactory.getTrustManagers hook: Injecting CustomTrustManager');            var originalManagers = this.getTrustManagers();            var newManagers = [];            for (var i = 0; i < originalManagers.length; i++) {                if (Java.instance(originalManagers[i]).$className.indexOf('X509TrustManager') !== -1) {                    newManagers.push(CustomTrustManager.$new());                } else {                    newManagers.push(originalManagers[i]);                }            }            return newManagers;        };        console.log('Attempted to hook X509TrustManager and SSLContext for broader bypass.');    } catch (e) {        console.log('Failed to apply broader TrustManager/SSLContext hooks: ' + e.message);    }    console.log('--- OkHttp3 SSL Pinning Bypass Script Finished Loading ---');});

    This script has two main parts: first, it directly hooks okhttp3.CertificatePinner.check, which is the most common way OkHttp3 implements pinning. By overriding this method to do nothing, we effectively tell the app to trust any certificate presented. Second, it attempts a broader bypass by replacing the system’s X509TrustManager and hooking SSLContext.init to inject our own TrustManager that accepts all certificates. This provides a more robust bypass for various SSL/TLS implementations.

    Step 4: Executing the Bypass

    Now, execute the Frida script against your target application. Replace `[package_name]` with the actual package name of the Android application you’re testing (e.g., `com.example.app`).

    frida -U -f [package_name] -l okhttp3_bypass.js --no-pause
    • -U: Targets a USB-connected device.
    • -f [package_name]: Spawns the specified package name.
    • -l okhttp3_bypass.js: Loads our bypass script.
    • --no-pause: Prevents Frida from pausing the spawned application, allowing it to start immediately.

    Once the command runs, Frida will inject the script into the app process as it launches. You should see output from the script’s `console.log` statements in your terminal, indicating that the hooks have been applied.

    Step 5: Verifying the Bypass

    With the script running, interact with the application. Attempt to perform actions that involve network communication. Simultaneously, monitor your Burp Suite (or other proxy tool). If the bypass is successful, you should now see the application’s network traffic flowing through your proxy, allowing you to intercept, inspect, and modify requests and responses.

    Understanding the Frida Script Logic

    The primary hook `CertificatePinner.check.overload(‘java.lang.String’, ‘java.util.List’).implementation = function(…)` is crucial. The `CertificatePinner` class in OkHttp3 is responsible for verifying that the server’s certificate matches one of the expected pins. By overriding its `check` method and providing an empty implementation, we essentially tell the application:

  • Deep Dive: How OkHttp3 Implements SSL Pinning and Frida’s Surgical Approach to Neutralize It

    Introduction to SSL Pinning and OkHttp3

    SSL Pinning is a security mechanism employed by client applications to prevent Man-in-the-Middle (MITM) attacks. Instead of trusting any certificate signed by a trusted Certificate Authority (CA) for a given domain, the application “pins” specific certificates or public keys. This means the application will only accept connections if the server presents one of the pre-defined, pinned certificates. If an attacker tries to intercept traffic using a proxy (like Burp Suite or OWASP ZAP) with a self-signed or different CA-signed certificate, the application will detect the mismatch and terminate the connection, effectively thwarting the MITM attempt.

    OkHttp3 is a popular, high-performance HTTP client for Java and Android. It provides robust features, including an elegant way to implement SSL pinning through its CertificatePinner class. While beneficial for security, this mechanism poses a significant challenge for penetration testers and security researchers who need to intercept and analyze application traffic.

    OkHttp3’s CertificatePinner Implementation

    OkHttp3 allows developers to pin certificates or public keys directly within their application code. This is typically done by creating an instance of okhttp3.CertificatePinner and adding specific hostnames along with their respective SHA-256 hashes of the certificates’ public keys (SPKI pins). The client then uses this pinner when building the OkHttpClient.

    Example OkHttp3 SSL Pinning Configuration:

    public class PinnedHttpClient { private static OkHttpClient client; static { String hostname = "public-api.example.com"; String publicKeySha256 = "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; // Replace with actual SHA-256 hash client = new OkHttpClient.Builder() .certificatePinner( new CertificatePinner.Builder() .add(hostname, publicKeySha256) .build()) .build(); } public static OkHttpClient getClient() { return client; } }

    When a connection is initiated to public-api.example.com, OkHttp3’s CertificatePinner will intercept the TLS handshake. It extracts the public key from the server’s certificate chain, computes its SHA-256 hash, and compares it against the pre-configured publicKeySha256. If they don’t match, the connection is aborted by throwing an SSLPeerUnverifiedException.

    The Penetration Tester’s Dilemma

    Traditional methods for intercepting HTTPS traffic on Android applications involve installing a custom root CA certificate (e.g., from Burp Suite) on the device. This allows the proxy to generate on-the-fly certificates for target domains, which the Android OS then trusts because it trusts the proxy’s root CA. However, with SSL pinning, the application bypasses the OS’s trust store and performs its own validation. Even if the proxy’s certificate is trusted by the OS, the application’s CertificatePinner will reject it, as its public key hash won’t match the pinned values.

    Frida: The Dynamic Instrumentation Toolkit

    Frida is a dynamic instrumentation toolkit that allows developers and security researchers to inject JavaScript code into running processes on various platforms, including Android. It’s incredibly powerful because it operates at runtime, allowing you to hook into native functions, Java methods, inspect memory, and modify behavior without recompiling the application. For SSL pinning bypasses, Frida’s ability to hook Java methods is precisely what we need.

    Frida’s Surgical Approach to Neutralize OkHttp3 SSL Pinning

    Our goal is to prevent OkHttp3’s CertificatePinner from performing its validation checks. The most effective way to do this is to target the check method within the okhttp3.CertificatePinner class. By hooking this method, we can force it to do nothing or always return successfully, effectively bypassing the pinning logic.

    Identifying the Target Method

    The core logic for certificate validation in OkHttp3 resides in the check method of okhttp3.CertificatePinner. This method typically takes a hostname and a list of certificates as arguments. If pinning is enforced and the certificates don’t match, it throws an exception. Our strategy is to hook this method and make it a no-operation (no-op) or simply return without throwing an exception.

    Frida Scripting – Bypassing `CertificatePinner.check`

    Here’s a Frida script designed to bypass OkHttp3’s SSL pinning:

    Java.perform(function() { console.log("[*] Starting Frida SSL Pinning Bypass for OkHttp3..."); try { var CertificatePinner = Java.use('okhttp3.CertificatePinner'); console.log("[+] Found okhttp3.CertificatePinner class."); // Hook the 'check' method that takes a hostname and a list of certificates CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function (hostname, certificates) { console.warn("[!!!] OkHttp3 CertificatePinner.check bypassed for: " + hostname); // Call the original method optionally, but for a bypass, we just return. // If you want to log original behavior, you can call this.check(hostname, certificates); // Simply return without throwing any exception, effectively bypassing pinning. }; console.log("[+] Hooked okhttp3.CertificatePinner.check method successfully."); } catch (e) { console.error("[-] Error hooking OkHttp3 CertificatePinner: " + e.message); } // Also hook the check method that takes a hostname and a X509Certificate (for older OkHttp versions or other variants) try { var CertificatePinner = Java.use('okhttp3.CertificatePinner'); CertificatePinner.check.overload('java.lang.String', 'java.security.cert.X509Certificate').implementation = function (hostname, certificate) { console.warn("[!!!] OkHttp3 CertificatePinner.check (single cert) bypassed for: " + hostname); }; console.log("[+] Hooked okhttp3.CertificatePinner.check (single cert) method successfully."); } catch (e) { console.error("[-] Error hooking OkHttp3 CertificatePinner (single cert): " + e.message); } console.log("[*] OkHttp3 SSL Pinning bypass script loaded."); });

    Explanation of the Script:

    1. Java.perform(function() { ... });: This ensures that our JavaScript code runs within the context of the target application’s Java VM.
    2. var CertificatePinner = Java.use('okhttp3.CertificatePinner');: This line obtains a reference to the okhttp3.CertificatePinner class, allowing us to interact with its methods.
    3. CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function (...) { ... };: This is the core of the bypass. We are targeting the specific overload of the check method that accepts a String (hostname) and a java.util.List (of certificates). We then replace its original implementation with our custom function.
    4. Inside our custom implementation, we simply log a message indicating the bypass and then do nothing else. By not calling the original method (e.g., this.check(hostname, certificates);) and by not throwing any exception, we effectively make the check method a no-op from the perspective of the application’s pinning logic.
    5. The second try-catch block attempts to hook another common overload of the check method, just in case the application uses it or an older OkHttp version.

    Deployment and Execution

    To use this script, you’ll need a rooted Android device or an emulator with Frida-server running. Follow these steps:

    1. Setup Frida-server on Android:
      Download the appropriate frida-server for your device’s architecture (e.g., frida-server-*-android-arm64) from the Frida releases page.
      adb push /path/to/frida-server /data/local/tmp/frida-server adb shell "chmod 755 /data/local/tmp/frida-server" adb shell "/data/local/tmp/frida-server &"
    2. Identify the target application’s package name:
      adb shell pm list packages -3
    3. Run the Frida script: Save the JavaScript code above as okhttp3_bypass.js.
      frida -U -f com.example.targetapp --no-pause -l okhttp3_bypass.js

      Replace com.example.targetapp with the actual package name of the application you are testing. The --no-pause flag ensures the app starts immediately with the script injected.

    4. Configure your proxy: Set up your device to proxy all traffic through Burp Suite (or your preferred proxy).
    5. Verify the bypass: Launch the application. You should now see its HTTPS traffic flowing through Burp Suite without SSL pinning errors, and your Frida console will show the bypass messages.

    Limitations and Further Considerations

    • Pinning Detection: Some advanced applications might include mechanisms to detect Frida’s presence or monitor for tampering with security-critical methods.
    • Other Pinning Libraries: This script specifically targets OkHttp3. Other HTTP clients (e.g., Retrofit, Volley using custom TrustManagers) or custom implementations of X509TrustManager would require different Frida hooks.
    • Native Pinning: If the application implements SSL pinning in native code (JNI, C/C++), a Java-based Frida hook will not be sufficient, and you would need to explore native hooking techniques.

    Conclusion

    SSL pinning is a robust security measure, but tools like Frida empower security researchers to understand and, when necessary, bypass these controls for legitimate testing purposes. By surgically hooking into OkHttp3’s CertificatePinner.check method, we can effectively neutralize its SSL pinning, enabling comprehensive traffic analysis. This technique highlights the importance of understanding both security implementation details and the powerful capabilities of dynamic instrumentation in modern application security.

  • Frida’s Secret Weapon: One-Click OkHttp3 SSL Pinning Bypass Script for Android Pentesting

    Introduction: The SSL Pinning Gauntlet

    In the realm of mobile application security, SSL pinning stands as a formidable defense mechanism. Designed to prevent Man-in-the-Middle (MitM) attacks, SSL pinning ensures that an application only communicates with a server whose certificate matches a pre-defined set of trusted certificates or public keys embedded within the app itself. While an essential security feature, it often presents a significant hurdle for penetration testers and security researchers attempting to analyze an application’s network traffic.

    What is SSL Pinning?

    Traditionally, a mobile app trusts any certificate signed by a Certificate Authority (CA) that the operating system trusts. SSL pinning bypasses this system trust by hardcoding or embedding specific server certificates or public keys directly into the application’s code. When the app initiates an HTTPS connection, it not only verifies the certificate chain’s validity but also checks if the server’s certificate or public key matches the pinned ones. If there’s a mismatch, the connection is immediately terminated.

    Why Bypass SSL Pinning?

    For ethical hackers and pentesters, bypassing SSL pinning is crucial. It enables them to intercept and inspect encrypted traffic, identifying potential vulnerabilities such as sensitive data exposure, insecure API endpoints, or improper session management. Without the ability to proxy traffic through tools like Burp Suite or OWASP ZAP, a comprehensive security assessment of the application’s backend communication becomes impossible.

    OkHttp3 and Its Pinning Mechanism

    OkHttp3 is a popular HTTP client for Android and Java applications, renowned for its efficiency and robust feature set, including built-in support for SSL pinning. OkHttp3’s pinning is implemented primarily through its CertificatePinner class.

    Understanding OkHttp3’s CertificatePinner

    When an OkHttp3 client is configured with a CertificatePinner, every HTTPS request will go through a check method within this class. The check method takes the hostname and the list of server certificates presented during the TLS handshake. It then compares the hashes (SHA-256 or SHA-1) of these certificates against a list of pre-defined pins. If none of the server certificates match any of the configured pins, a SSLPeerUnverifiedException is thrown, and the connection fails.

    The TrustManager Challenge

    Beyond CertificatePinner, some applications might also customize their X509TrustManager to perform additional trust checks or integrate with system-level trust stores in a non-standard way. While CertificatePinner is the primary target for OkHttp3 pinning, a comprehensive bypass often considers broader trust manager hooks if the initial CertificatePinner hook proves insufficient.

    Enter 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 various platforms, including Android. Its powerful API lets you hook into arbitrary functions, read and write memory, and modify application logic at runtime, making it an indispensable tool for Android penetration testing.

    Frida Fundamentals for Android Pentesting

    Frida operates by injecting its Gadget into the target process. Once injected, you can interact with the application’s memory and execution flow through a JavaScript API. For SSL pinning bypass, we leverage Frida to:

    1. Enumerate loaded classes and methods.
    2. Hook specific methods (e.g., CertificatePinner.check).
    3. Modify the behavior of these methods (e.g., making them return early or skip validation logic).

    Crafting the OkHttp3 SSL Bypass Script

    Our goal is to create a Frida script that intercepts the CertificatePinner.check method and ensures it never throws an exception, effectively trusting any certificate presented by the server. This allows tools like Burp Suite to inject their own CA certificate without triggering the pinning mechanism.

    Targeting CertificatePinner

    The core of our bypass script focuses on the okhttp3.CertificatePinner class. We’ll identify its check method, which is responsible for enforcing the pinning policy, and replace its implementation with a no-op function.

    Addressing TrustManager Issues (Advanced)

    While the CertificatePinner hook is usually sufficient for OkHttp3, some applications might employ additional trust logic within custom X509TrustManager implementations. For a more robust bypass, especially if the OkHttp3 hook isn’t working, you might also target standard Java methods:

    • javax.net.ssl.X509TrustManager.checkClientTrusted
    • javax.net.ssl.X509TrustManager.checkServerTrusted
    • java.security.cert.X509Certificate[] getAcceptedIssuers() (return an empty array)

    By hooking these and ensuring they don’t throw exceptions, we can cover a broader range of pinning implementations. However, for a typical OkHttp3 app, the CertificatePinner hook is the primary focus.

    Step-by-Step Guide: Deploying the Bypass

    Prerequisites

    • A rooted Android device or an emulator (e.g., AVD, Genymotion).
    • Frida server running on the Android device.
    • Frida-tools installed on your host machine (pip install frida-tools).
    • A proxy tool like Burp Suite configured to intercept traffic (remember to install Burp’s CA certificate on the Android device’s user/system trust store if not using Frida’s trust manager bypasses).

    Identifying the Target App

    First, get the package name of the target application. You can use adb shell pm list packages | grep <app_name> or simply check the app’s info on the device.

    $ adb shell pm list packages | grep your.target.app
    package:your.target.appname

    Running the Frida Script

    Save the Frida JavaScript code (provided below) as okhttp3_pinning_bypass.js.

    Then, execute Frida from your host machine, attaching it to the target application:

    $ frida -U -f your.target.appname -l okhttp3_pinning_bypass.js --no-pause

    The -U flag targets a USB-connected device. -f spawns the process, and --no-pause ensures it starts immediately. Frida will inject the script and print messages as hooks are hit.

    Verifying the Bypass

    With Frida running, open your proxy tool (e.g., Burp Suite). Interact with the target Android application. If the bypass is successful, you should now see the application’s HTTPS traffic flowing through your proxy without SSL handshake errors. Look for messages from the Frida script in your terminal indicating that the hooks were applied.

    The Frida Bypass Script (Code)

    Here’s a comprehensive Frida script targeting OkHttp3’s CertificatePinner and offering optional X509TrustManager bypasses for broader coverage.

    Java.perform(function() {    
        console.log("[*] Frida: OkHttp3 SSL Pinning Bypass Script Loaded");
    
        // 1. Hook OkHttp3 CertificatePinner.check
        try {
            var CertificatePinner = Java.use('okhttp3.CertificatePinner');
            CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function (hostname, peerCertificates) {
                console.log("[*] OkHttp3 CertificatePinner.check HOOKED for hostname: " + hostname);
                // Do nothing, effectively bypassing the pinning check
            };
            console.log("[*] OkHttp3 CertificatePinner.check bypass installed successfully.");
        } catch (e) {
            console.log("[-] OkHttp3 CertificatePinner.check hook failed: " + e.message);
        }
    
        // 2. Optional: Bypass TrustManager for broader coverage (if CertificatePinner isn't enough)
        try {
            var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager');
            var TrustManagerFactory = Java.use('javax.net.ssl.TrustManagerFactory');
            var SSLContext = Java.use('javax.net.ssl.SSLContext');
    
            // Hook checkClientTrusted
            X509TrustManager.checkClientTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String').implementation = function(chain, authType) {
                console.log("[*] X509TrustManager.checkClientTrusted HOOKED.");
                // Always trust
            };
    
            // Hook checkServerTrusted
            X509TrustManager.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String').implementation = function(chain, authType) {
                console.log("[*] X509TrustManager.checkServerTrusted HOOKED.");
                // Always trust
            };
    
            // Hook getAcceptedIssuers (return an empty array)
            X509TrustManager.getAcceptedIssuers.implementation = function() {
                console.log("[*] X509TrustManager.getAcceptedIssuers HOOKED.");
                return [];
            };
    
            // Override TrustManager initialization in SSLContext
            SSLContext.init.overload('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom').implementation = function(keyManagers, trustManagers, secureRandom) {
                console.log("[*] SSLContext.init HOOKED. Replacing TrustManagers.");
                var TrustManagerArray = Java.array('javax.net.ssl.TrustManager', [
                    Java.cast(Java.registerClass({
                        name: 'com.r3c0rd.DummyTrustManager',
                        implements: [X509TrustManager],
                        methods: {
                            checkClientTrusted: function(chain, authType) {},
                            checkServerTrusted: function(chain, authType) {},
                            getAcceptedIssuers: function() { return []; }
                        }
                    }).$new(), X509TrustManager)
                ]);
                this.init(keyManagers, TrustManagerArray, secureRandom);
            };
    
            console.log("[*] X509TrustManager and SSLContext bypasses installed successfully.");
    
        } catch (e) {
            console.log("[-] X509TrustManager/SSLContext hook failed: " + e.message);
        }
    
        console.log("[*] Frida: All requested hooks attempted.");
    });
    

    Conclusion

    Frida provides an incredibly powerful and flexible platform for bypassing security mechanisms like SSL pinning in Android applications. By understanding the underlying implementation of libraries like OkHttp3 and leveraging Frida’s dynamic instrumentation capabilities, penetration testers can effectively overcome these defenses to conduct thorough security assessments. The one-click script presented here is a potent weapon in any Android pentester’s arsenal, simplifying what can often be a complex and time-consuming task.

  • Beyond the Basics: Advanced Frida Techniques for Stealthy OkHttp3 SSL Pinning Bypass

    Introduction

    SSL pinning is a critical security measure implemented by developers to prevent man-in-the-middle (MITM) attacks. By hardcoding or pre-configuring trusted certificates within an application, it ensures that the app only communicates with known, legitimate servers, even if the device’s trust store is compromised. While a robust defense, it presents a significant hurdle for penetration testers and security researchers who need to intercept and analyze network traffic. OkHttp3, a popular HTTP client for Android and Java, offers powerful SSL pinning capabilities, often making generic bypass scripts ineffective. This article delves into advanced Frida techniques to stealthily bypass OkHttp3 SSL pinning, equipping you with the tools to tackle more resilient implementations.

    Understanding OkHttp3 SSL Pinning

    OkHttp3’s primary mechanism for SSL pinning is the okhttp3.CertificatePinner class. Developers configure it with a list of expected certificate hashes (SPKI fingerprints) for specific hostnames. During the TLS handshake, OkHttp3 verifies that the server’s certificate chain contains at least one certificate matching the configured pins. If no match is found, the connection is aborted, preventing communication with an untrusted endpoint.

    A typical OkHttp3 client setup with pinning looks like this:

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

    While common Frida scripts often target the global `X509TrustManager` or `SSLContext.init()`, OkHttp3’s explicit pinning via `CertificatePinner` often bypasses these generic hooks if not handled specifically, requiring a more direct approach.

    Advanced Frida Hooking Strategies

    1. Hooking okhttp3.CertificatePinner.check()

    The most direct approach is to hook the `check()` method of the `CertificatePinner` class. This method is invoked by OkHttp3 to perform the actual pinning validation. By hooking it, we can force it to return without throwing an exception, effectively disabling the pinning check.

    Java.perform(function() {    try {        var CertificatePinner = Java.use('okhttp3.CertificatePinner');        CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function(hostname, peerCertificates) {            console.log("[+] Bypassing OkHttp3 CertificatePinner.check() for hostname: " + hostname);            // Do not call the original implementation, just return            // This effectively bypasses the pinning check            // You might want to log the certificates for debugging            return;        };        CertificatePinner.check.overload('java.lang.String', 'okhttp3.CertificateChainCleaner').implementation = function(hostname, certificateChainCleaner) {            console.log("[+] Bypassing OkHttp3 CertificatePinner.check() (CertificateChainCleaner overload) for hostname: " + hostname);            return;        };        console.log("[+] OkHttp3 CertificatePinner.check() hooked.");    } catch (e) {        console.log("[-] Error hooking CertificatePinner: " + e.message);    }});

    This script intercepts both common overloads of the `check` method, returning immediately. This is usually effective unless the application uses a heavily obfuscated `CertificatePinner` or a custom pinning mechanism.

    2. Intercepting okhttp3.OkHttpClient.Builder

    Many applications construct their `OkHttpClient` instances using the `Builder` pattern. This provides another powerful vector for bypassing pinning: preventing the `CertificatePinner` from ever being set or replacing it with a benign one.

    Hooking certificatePinner(CertificatePinner)

    We can hook the `certificatePinner()` method of `OkHttpClient.Builder` to either set a null `CertificatePinner` or replace it with an empty one, effectively disabling pinning during client construction.

    Java.perform(function() {    try {        var OkHttpClientBuilder = Java.use('okhttp3.OkHttpClient$Builder');        OkHttpClientBuilder.certificatePinner.implementation = function(certificatePinner) {            console.log("[+] OkHttpClient.Builder.certificatePinner() called. Nullifying pinner.");            // Return 'this' to maintain method chaining, but with a null or empty pinner            var emptyPinner = Java.use('okhttp3.CertificatePinner').$new(Java.use('java.util.LinkedHashSet').$new());            return this.certificatePinner(emptyPinner); // Or pass null if the app allows it        };        console.log("[+] OkHttpClient.Builder.certificatePinner() hooked.");    } catch (e) {        console.log("[-] Error hooking OkHttpClient.Builder.certificatePinner: " + e.message);    }});

    This script ensures that whenever an `OkHttpClient.Builder` attempts to set a `CertificatePinner`, it’s replaced with an empty one. This is stealthier as it modifies the client’s configuration at creation time rather than runtime checks.

    Replacing sslSocketFactory(SSLSocketFactory, X509TrustManager)

    For more resilient apps or those with custom trust managers, targeting the `sslSocketFactory` method is powerful. This allows you to inject your own `SSLSocketFactory` and a custom `X509TrustManager` that trusts all certificates or specifically your proxy’s CA certificate.

    Java.perform(function() {    try {        var OkHttpClientBuilder = Java.use('okhttp3.OkHttpClient$Builder');        OkHttpClientBuilder.sslSocketFactory.overload('javax.net.ssl.SSLSocketFactory', 'javax.net.ssl.X509TrustManager').implementation = function(sslSocketFactory, trustManager) {            console.log("[+] OkHttpClient.Builder.sslSocketFactory() called. Replacing TrustManager.");            var trustAllCerts = Java.registerClass({                name: 'com.android.TrustAllManager',                implements: [Java.use('javax.net.ssl.X509TrustManager')],                methods: {                    checkClientTrusted: function(chain, authType) {},                    checkServerTrusted: function(chain, authType) {},                    getAcceptedIssuers: function() {                        return [];                    }                }            });            var SSLContext = Java.use('javax.net.ssl.SSLContext');            var sc = SSLContext.getInstance('TLS');            sc.init(null, [trustAllCerts.$new()], new (Java.use('java.security.SecureRandom'))());            var newSslSocketFactory = sc.getSocketFactory();            return this.sslSocketFactory(newSslSocketFactory, trustAllCerts.$new());        };        console.log("[+] OkHttpClient.Builder.sslSocketFactory() hooked.");    } catch (e) {        console.log("[-] Error hooking OkHttpClient.Builder.sslSocketFactory: " + e.message);    }});

    This script creates a custom `X509TrustManager` that blindly trusts all certificates and then initializes a new `SSLContext` with it, replacing the original `SSLSocketFactory` and `TrustManager` during client build time. This is particularly effective against custom trust manager implementations.

    3. Dynamic TrustManager Replacement

    If the `OkHttpClient.Builder` is not directly accessible or the application uses a pre-configured `SSLContext` or `SSLSocketFactory`, you might need to target the underlying `X509TrustManager` instances dynamically. This involves enumerating existing instances and replacing their methods.

    Java.perform(function() {    var TrustManagerImpl = Java.use('com.android.org.conscrypt.TrustManagerImpl');    if (TrustManagerImpl) {        console.log("[+] Found TrustManagerImpl, attempting to hook checkTrusted().");        TrustManagerImpl.checkTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession', 'java.lang.String', 'java.lang.String', 'boolean').implementation = function() {            console.log("[+] Bypassing TrustManagerImpl.checkTrusted (Android N+).");            // Just return, effectively trusting everything            return;        };        TrustManagerImpl.checkTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String').implementation = function() {            console.log("[+] Bypassing TrustManagerImpl.checkTrusted (Android <N).");            return;        };    }    // For applications that use custom TrustManagers, you might need to find them    // using Java.choose() if they are instantiated and available in memory.    // Example: Java.choose('your.app.CustomTrustManager', { onMatch: function(instance){ ... hook methods ... }});});

    This approach targets the Android system’s default `TrustManagerImpl`, which is often used by default `SSLSocketFactory` implementations. For custom application-specific `TrustManager` classes, you’d need to identify and hook them specifically.

    Practical Steps and Debugging

    Setting Up Your Environment

    1. Rooted Android Device or Emulator: Necessary for running Frida server.
    2. Frida Server: Download the correct `frida-server` for your device’s architecture (e.g., `arm64`) from the Frida releases page. Push it to `/data/local/tmp/` on your device and execute it.
      adb push frida-server /data/local/tmp/frida-serveradb shell "chmod 755 /data/local/tmp/frida-server"adb shell "/data/local/tmp/frida-server &"
    3. Frida Client: Installed via pip on your host machine (`pip install frida-tools`).
    4. Proxy (e.g., Burp Suite/OWASP ZAP): Configure it to listen on all interfaces, typically port 8080.
    5. Install Proxy CA Certificate: Install your proxy’s CA certificate on the Android device as a user-installed CA. For Android 7+, you might also need to use an emulator/rooted device to install it as a system-level CA for some apps.
    6. Configure Device Proxy: Set the device’s Wi-Fi proxy to point to your host machine’s IP address and proxy port.

    Identifying the Target

    Before writing complex hooks, understand the target application’s network communication. Use tools like `frida-trace` or Frida’s `Java.enumerateLoadedClasses()` to gain insights.

    To list classes loaded that contain ‘okhttp’:

    Java.perform(function(){    Java.enumerateLoadedClasses({        'onMatch': function(name){            if(name.includes('okhttp')) {                console.log(name);            }        },        'onComplete': function(){            console.log("Done enumerating OkHttp related classes.");        }    });});

    This can help you identify obfuscated class names or confirm the presence of OkHttp3. Once identified, you can use `Java.use(‘ObfuscatedClassName’).$ownMethods` to list its methods.

    Refining Your Hooks

    • Method Overloads: Always check for method overloads using `$overload()` to ensure your hook applies to the correct method signature.
    • Instance vs. Class Hooks: If the target object is instantiated early, a class hook (`Java.use`) is sufficient. If instances are created dynamically or late, you might need `Java.choose()` to find existing instances and apply hooks.
    • Error Handling: Wrap your Frida scripts in `try-catch` blocks to gracefully handle cases where classes or methods might not exist, especially in heavily obfuscated apps.
    • Logging: Use `console.log()` liberally to debug and understand the execution flow. Log parameters and return values to verify your hooks are working as expected.

    Conclusion

    Bypassing SSL pinning in modern Android applications, especially those using robust HTTP clients like OkHttp3, demands advanced techniques beyond simple trust manager disablement. By understanding the underlying mechanisms of OkHttp3’s `CertificatePinner` and `OkHttpClient.Builder`, and by leveraging Frida’s powerful dynamic instrumentation capabilities, penetration testers can craft surgical hooks to overcome even the most resilient pinning implementations. Adaptability, careful observation of the target application’s behavior, and systematic debugging are key to success in this challenging but rewarding area of mobile application security.

  • From Datasheet to Decompiler: Creating a Ghidra Sleigh Processor for a Custom Android Microcontroller

    Introduction: The Uncharted Territory of Custom Android Microcontrollers

    In the expansive world of Android devices, reverse engineering often unveils more than just ARM or x86 architectures. Many embedded systems, particularly within dedicated peripherals, custom security modules, or specialized co-processors, leverage obscure or proprietary microcontroller architectures. These custom silicon components are critical for device functionality but present a significant challenge for analysis, as standard reverse engineering tools lack native support. This is where Ghidra’s powerful Sleigh language comes into play. By crafting a custom Ghidra Sleigh processor module, engineers can transform raw machine code from these unknown microcontrollers into understandable disassembly and even high-level pseudocode, unlocking deep insights into their operation.

    This expert-level guide will walk you through the comprehensive process of creating a Ghidra Sleigh processor module from scratch. We’ll bridge the gap between architectural specifications—whether from a datasheet or derived through meticulous reverse engineering—and a fully functional decompiler, enabling you to analyze custom Android-embedded microcontrollers effectively.

    I. Deconstructing the Target Architecture: From Datasheets to Firmware Analysis

    A. Information Gathering: Datasheets and Documentation

    The first and most critical step in creating a Sleigh processor is to meticulously gather information about the target microcontroller’s architecture. Ideally, this comes from official datasheets, programmer’s manuals, or leaked documentation. Key pieces of information to extract include:

    • Instruction Set Architecture (ISA): The full list of opcodes, their mnemonics, operands, and bit-level encoding.
    • Register File: All general-purpose registers (GPRs), special-purpose registers (SPRs), program counter (PC), stack pointer (SP), and any status/flag registers, along with their sizes.
    • Memory Organization: Address spaces (RAM, ROM, I/O, etc.), their base addresses, sizes, and access permissions.
    • Endianness: Whether the architecture is little-endian or big-endian.
    • Instruction Length: Fixed-length or variable-length instructions, and their minimum/maximum sizes.
    • Calling Conventions: How arguments are passed, return values handled, and stack frames managed (if applicable).

    Without official documentation, this phase transitions into active reverse engineering of firmware images or even direct hardware analysis (e.g., JTAG, logic analysis) to infer these details.

    B. Firmware Extraction and Initial Analysis

    Obtaining the firmware image is paramount. For Android-embedded microcontrollers, this might involve extracting bootloaders, vendor partitions, or even side-channel attacks on dedicated chips. Once you have a binary blob, tools like binwalk can help identify file systems, compression, and potential instruction sequences.

    # Example: Extracting a boot partition from an Android device via adb (requires root) adb rootadb pull /dev/block/by-name/boot_a boot_a.img# Using binwalk for initial analysis to look for known architectures or data patternsbinwalk -Me boot_a.img

    Even without direct ISA knowledge, looking for repeating patterns, sequences of operations, or known exception/interrupt vectors can provide hints about instruction lengths and common operations.

    C. Key Architectural Elements for Sleigh

    For Ghidra Sleigh, the following must be clearly defined:

    • Register File: Every accessible register needs a name, size, and offset within Ghidra’s internal register space.
    • Memory Organization: At least one primary memory space (usually ‘ram’) and its addressability (byte, word).
    • Instruction Set: Each instruction’s binary pattern and its corresponding P-code semantics.
    • Context Registers: Registers whose values influence the interpretation of subsequent instructions (e.g., mode bits affecting instruction sets).

    II. Ghidra Sleigh: The Language of Disassembly and Decompilation

    A. What is Sleigh?

    Sleigh is Ghidra’s proprietary language for describing processor instruction sets. It allows you to specify how a processor’s machine code instructions are encoded and, crucially, what operation they perform at a low-level, processor-independent intermediate representation called P-code. Ghidra then uses these Sleigh definitions to disassemble binary code and lift it to P-code, which is the foundation for its powerful decompiler.

    B. The Core Sleigh Files

    A Ghidra processor module typically consists of several interconnected files:

    • .slaspec (Sleigh Specification): The heart of the processor module. It defines the processor’s registers, memory spaces, instruction tokens, patterns for each instruction, and their corresponding P-code semantics.
    • .pspec (Processor Specification): Defines global characteristics of the processor, such as endianness, default memory blocks, segment shifts, and default context settings.
    • .cspec (Compiler Specification): Describes how compilers target this processor, including calling conventions, stack management, and standard library functions (call-others). This is crucial for accurate decompilation.
    • .ldefs (Language Definitions): A simple XML file that ties all the above files together, defining the
  • Custom Frida Scripts: Tailoring OkHttp3 SSL Pinning Bypass for Unique Android Applications

    Introduction to SSL Pinning and its Challenges

    SSL Pinning is a security mechanism implemented by mobile applications to prevent man-in-the-middle (MitM) attacks. Instead of trusting any certificate signed by a trusted root Certificate Authority (CA), the application ‘pins’ specific certificates or public keys that it expects to see from the server. If the server presents a certificate that doesn’t match the pinned ones, the connection is terminated, even if the certificate is otherwise valid and signed by a trusted CA. This mechanism is crucial for securing sensitive data transmission in Android applications.

    OkHttp3 is a widely used HTTP client for Android and Java applications, known for its efficiency and robust feature set, including built-in support for SSL pinning via its CertificatePinner class. While standard proxy tools like Burp Suite or OWASP ZAP can easily intercept traffic from applications that don’t implement pinning, they fail against pinned applications because the application rejects the proxy’s self-signed (or custom CA-signed) certificate.

    The Limitations of Generic Frida Bypass Scripts

    For penetration testers, bypassing SSL pinning is often a prerequisite for analyzing application network traffic. Tools like Frida, a dynamic instrumentation toolkit, are invaluable for this purpose. Generic Frida scripts, such as those found in `frida-multiple-unpinning` or built into `objection` (Frida’s runtime mobile exploration toolkit), attempt to hook common SSL/TLS classes and methods across various frameworks (e.g., `TrustManager`, `HostnameVerifier`, `X509TrustManager`).

    While these generic scripts are effective for many applications, they often fall short when dealing with highly customized or obfuscated OkHttp3 implementations. Reasons for failure include:

    • **Specific OkHttp3 Usage:** Applications might instantiate CertificatePinner in non-standard ways, or configure it deeply within their code logic, making it difficult for generic hooks to catch all instances.
    • **Obfuscation:** Tools like ProGuard or DexGuard rename classes and methods, rendering static string-based hooks ineffective.
    • **Dynamic Class Loading:** Pinning logic might be loaded dynamically at runtime, after initial Frida attachment.
    • **Multiple Pinning Layers:** Some applications combine OkHttp3 pinning with other mechanisms (e.g., TrustKit, custom `X509TrustManager` implementations), requiring a multi-pronged bypass approach.

    When generic scripts fail, a tailored, custom Frida script becomes necessary, demanding a deeper understanding of the application’s internal workings and OkHttp3’s pinning mechanisms.

    Understanding OkHttp3’s SSL Pinning Mechanisms

    OkHttp3 primarily relies on the CertificatePinner class for SSL pinning. This class allows developers to declare specific hashes of certificates or public keys for given hostnames. During an SSL handshake, CertificatePinner verifies that one of the peer’s certificates matches a configured pin.

    CertificatePinner Deep Dive

    The core of OkHttp3’s pinning lies in the okhttp3.CertificatePinner class. When an `OkHttpClient` instance is built with a `CertificatePinner`, its `check` method is invoked during the TLS handshake for every connection. The primary method targeted for bypass is typically:

    public void check(String hostname, List<Certificate> peerCertificates)

    This method iterates through the provided `peerCertificates` and compares their public key hashes against the pins configured for the `hostname`. If no match is found, a `SSLPeerUnverifiedException` is thrown, terminating the connection.

    OkHostnameVerifier (Brief Mention)

    While `CertificatePinner` handles the strict pinning logic, `okhttp3.internal.tls.OkHostnameVerifier` is responsible for standard hostname verification, ensuring that the certificate’s subject alternative names (SANs) or common name (CN) match the requested hostname. While crucial for overall TLS security, bypassing `CertificatePinner` is usually sufficient for defeating SSL pinning, as `OkHostnameVerifier` typically doesn’t prevent proxies from working if the proxy’s certificate is otherwise valid for the hostname.

    Step-by-Step Custom Frida Script Development

    Developing a custom Frida script involves initial reconnaissance, crafting the hook, and handling advanced scenarios.

    Phase 1: Initial Reconnaissance

    Before writing any Frida code, you need to understand how the target application uses OkHttp3. This typically involves decompiling the APK.

    1. **Decompile the APK:** Use tools like Jadx or Ghidra to get a readable source code representation.
    2. **Search for OkHttp3 Usage:** Look for key classes and methods related to OkHttp3. Specifically search for:
      • `okhttp3.OkHttpClient.Builder`
      • `okhttp3.CertificatePinner`
      • `build()` methods on `OkHttpClient.Builder`
    # Example Jadx command to decompile an APKjadx -d output_dir com.example.app.apk# Inside the decompiled source, search for CertificatePinner usage:# grep -r

  • Troubleshooting Frida SSL Pinning: Fixing Common OkHttp3 Bypass Errors on Android

    Introduction to SSL Pinning and Frida’s Role

    SSL (Secure Sockets Layer) pinning is a security mechanism employed by applications to prevent man-in-the-middle (MitM) attacks. Instead of relying solely on the device’s trust store, applications with SSL pinning verify the server’s certificate or public key against a predefined, hardcoded set of trusted certificates or keys. If the certificate presented by the server during the TLS handshake does not match the pinned ones, the connection is aborted, even if the certificate is otherwise valid and issued by a trusted CA.

    For penetration testers and security researchers, SSL pinning presents a significant challenge, as it prevents intercepting application traffic with tools like Burp Suite or OWASP ZAP. Frida, a dynamic instrumentation toolkit, offers a powerful solution by allowing us to inject custom scripts into running processes and modify their behavior at runtime. This enables us to bypass SSL pinning by hooking into the application’s network security mechanisms and disabling or altering their validation logic. This article focuses on troubleshooting common issues encountered when attempting to bypass SSL pinning, specifically for Android applications using the popular OkHttp3 networking library.

    Understanding OkHttp3 SSL Pinning Implementation

    OkHttp3 is a widely used HTTP client for Android and Java applications. It provides a robust and flexible way to handle network requests, including built-in support for SSL pinning through its CertificatePinner class. Developers can configure an OkHttpClient instance with a CertificatePinner to enforce strict certificate validation rules. The pinning logic typically involves checking the SHA-256 hashes of certificates or public keys against a list of known, trusted hashes.

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

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

    Our goal with Frida is to either replace the CertificatePinner with a benign one, or more commonly, to hook into its internal verification methods and force them to return a ‘true’ or ‘allow’ value, effectively bypassing the pin check.

    Prerequisites for Frida Bypass

    Before diving into troubleshooting, ensure your setup is correct:

    • Rooted Android Device/Emulator: Frida requires root access to inject into system processes.
    • Frida Server: The appropriate Frida server binary running on your Android device (e.g., frida-server-16.1.4-android-arm64).
    • Frida Client: Installed on your host machine (e.g., pip install frida-tools).
    • Proxy Tool: Burp Suite, OWASP ZAP, or similar, configured as a proxy on your host machine.
    • Proxy CA Certificate: Installed as a system-trusted certificate on your Android device. This is crucial for your proxy to intercept HTTPS traffic.

    Installing Proxy CA Certificate on Android

    To install your proxy’s CA certificate (e.g., Burp Suite’s CA cert) as a system trusted certificate:

    1. Export the CA certificate from your proxy in DER format (e.g., cacert.der).
    2. Convert it to PEM format:openssl x509 -inform DER -in cacert.der -out cacert.pem
    3. Get the hash of the certificate:openssl x509 -inform PEM -subject_hash_old -in cacert.pem | head -1 (e.g., output: 9a5ba575)
    4. Rename the PEM file:mv cacert.pem 9a5ba575.0 (replace hash with your own)
    5. Push to the device’s system trust store:adb rootadb disable-verityadb remountadb push 9a5ba575.0 /system/etc/security/cacerts/adb reboot

    Common Frida SSL Pinning Bypass Scripts for OkHttp3

    A widely used Frida script for OkHttp3 bypass targets the CertificatePinner class directly. This script often aims to overwrite or nullify the pinning logic:

    Java.perform(function() { try { 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); // Simply call the original check without arguments, effectively bypassing the pin check this.check(hostname, peerCertificates); }; console.log('OkHttp3 CertificatePinner bypass applied!'); } catch (e) { console.log('Error applying OkHttp3 CertificatePinner bypass: ' + e.message); } try { // Additional bypass for TrustManager if default doesn't work var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager'); var TrustManagerImpl = Java.use('com.android.org.conscrypt.TrustManagerImpl'); TrustManagerImpl.checkTrustedRecursive.implementation = function(a, b, c, d) { console.log('Bypassing TrustManagerImpl.checkTrustedRecursive...'); return Java.array('java.security.cert.X509Certificate', []); }; console.log('TrustManagerImpl bypass applied!'); } catch (e) { console.log('Error applying TrustManagerImpl bypass: ' + e.message); }});

    To run this script:frida -U -f com.example.appname -l your_script.js --no-pause

    Troubleshooting Common Errors

    Error 1: SSLHandshakeException: java.security.cert.CertPathValidatorException

    This is the most common error, indicating that despite the Frida script, the application is still failing to validate your proxy’s SSL certificate.

    Causes:

    1. Proxy CA not system-trusted: The application might be using a default TrustManager that respects the system trust store, but your CA certificate wasn’t installed correctly or isn’t trusted by the app’s specific configuration.
    2. OkHttp3’s aggressive pinning: The script might not be comprehensive enough to handle all pinning scenarios or custom TrustManager implementations.
    3. Network Security Configuration (Android 7+): The app might explicitly disallow user-added CA certificates via its network_security_config.xml.

    Solutions:

    • Verify CA Installation: Double-check the steps for pushing the CA certificate to /system/etc/security/cacerts/. After reboot, confirm its presence and permissions.
    • Enhance Frida Script: The basic CertificatePinner.check bypass might not be enough. Add hooks for TrustManager methods. The script above includes a TrustManagerImpl bypass, which is often crucial.
    • Target TrustManagerImpl (if `CertificatePinner` fails): If the app uses a custom TrustManager or older Android versions, you might need to target javax.net.ssl.X509TrustManager directly.Java.use('javax.net.ssl.X509TrustManager').checkClientTrusted.implementation = function(chain, authType) {};Java.use('javax.net.ssl.X509TrustManager').checkServerTrusted.implementation = function(chain, authType) {};Java.use('javax.net.ssl.X509TrustManager').getAcceptedIssuers.implementation = function() { return []; };
    • Bypass Network Security Configuration (NSC): For Android 7+ apps, NSC can restrict trusted CAs. Frida can sometimes bypass this by hooking android.security.net.config.NetworkSecurityConfig.is CleartextTrafficPermitted or by targeting the classes that load the configuration. However, more robust solutions often involve repacking the APK with a modified NSC or dynamic hooking of certificate validation.

    Error 2: Script Not Attaching/Hooking (No output, no bypass)

    The Frida script runs, but no console messages appear, and traffic isn’t intercepted.

    Causes:

    1. Incorrect Package Name: The app’s package name (`com.example.appname`) is wrong.
    2. App Process Not Running: The app isn’t active or hasn’t started its network operations.
    3. Frida Server Issues: Frida server isn’t running or accessible.
    4. Timing Issues: The hook executes before the target class is loaded.

    Solutions:

    • Verify Package Name: Use adb shell pm list packages | grep 'appname' or frida-ps -Uai to get the exact package name.
    • Ensure App is Active: Launch the app and navigate to relevant sections to trigger network requests.
    • Check Frida Server:adb shell