PluginBench
Skill
Fail
Audit score 45

mobile-ssl-pinning-bypass

yaklang/hack-skills

Bypass SSL/TLS certificate pinning on Android and iOS mobile apps for traffic interception testing.

What is mobile-ssl-pinning-bypass?

Mobile SSL pinning bypass playbook for intercepting HTTPS traffic from Android and iOS applications that implement certificate pinning, public key pinning, or SPKI hash pinning. Use when testing mobile apps with pinning defenses across native, React Native, Flutter, and Xamarin frameworks.

  • Bypass certificate pinning, public key pinning, and SPKI hash pinning on Android via Frida hooks, Objection, or network security config modification
  • Disable pinning on iOS using Frida, Objection, or runtime patching techniques
  • Identify and classify pinning types (certificate, public key, SPKI hash, CA, multi-pin) and their resilience levels
  • Patch APKs by decompiling, modifying network security config, and re-signing for Android testing
  • Hook framework-specific implementations (OkHttp, Retrofit, Volley, Conscrypt, AFNetworking) to bypass pinning checks
  • Install proxy CAs as system certificates via Magisk or manual system partition modification

How to install mobile-ssl-pinning-bypass

npx skills add https://github.com/yaklang/hack-skills --skill mobile-ssl-pinning-bypass
Prerequisites
  • Frida and Frida gadget (for runtime hooking) or Objection installed
  • Android Debug Bridge (adb) for Android device/emulator access
  • Apktool, jadx, and apksigner for APK decompilation and repackaging on Android
  • Burp Suite or similar HTTPS proxy for traffic interception
  • Rooted Android device or emulator (for Xposed/LSPosed modules or system CA installation)
  • iOS jailbreak or runtime patching capability for iOS targets
Claude Code
Cursor
Windsurf
Cline

How to use mobile-ssl-pinning-bypass

  1. 1.Identify the target app's pinning type by analyzing network traffic or decompiling the APK/IPA
  2. 2.Choose a bypass method: Frida hooks (universal, runtime), Objection (one-command), network security config (APK modification), or Xposed modules (Android)
  3. 3.For Frida: inject the universal SSL bypass script into the running app process using Frida CLI or spawn mode
  4. 4.For Objection: run 'android sslpinning disable' command in the interactive session after attaching to the app
  5. 5.For APK modification: decompile with apktool, add or modify network_security_config.xml to trust user-installed CAs, repackage and re-sign
  6. 6.For iOS: use Frida with iOS-specific hooks or Objection's iOS SSL bypass commands on a jailbroken device
  7. 7.Configure your HTTPS proxy (Burp, mitmproxy) to use a custom CA certificate and route traffic through it
  8. 8.Verify pinning is disabled by confirming HTTPS traffic appears in the proxy without certificate errors

Use cases

Good for
  • Intercepting HTTPS traffic from a banking app using OkHttp certificate pinning to test API security
  • Bypassing public key pinning on a social media app to analyze encrypted data flows with Burp Suite
  • Testing an enterprise app with CA pinning by modifying the network security config and re-signing the APK
  • Disabling multi-pin backup certificate validation on a payment app using Frida hooks during penetration testing
  • Analyzing Flutter or React Native app traffic by hooking underlying SSL/TLS validation layers
Who it's for
  • Mobile security penetration testers
  • Android and iOS app security researchers
  • API security testers analyzing mobile app backends
  • Red team operators conducting mobile app assessments
  • Security engineers validating pinning implementations

mobile-ssl-pinning-bypass FAQ

What's the difference between certificate pinning and public key pinning?

Certificate pinning validates the exact leaf certificate presented by the server (breaks when the cert rotates). Public key pinning validates the Subject Public Key Info (SPKI) extracted from the certificate (survives certificate renewal if the key stays the same). SPKI hash pinning stores a SHA-256 hash of the SPKI for comparison.

Do I need root/jailbreak to bypass pinning?

Not always. Frida and Objection can bypass pinning on non-rooted Android via USB debugging if the app is debuggable. APK modification works on any device. However, rooted devices enable Xposed/LSPosed modules and system CA installation for broader coverage. iOS requires jailbreak for most runtime patching techniques.

Why does the network security config method only work on Android?

Android's network security config is an Android-specific XML file that controls TLS validation at the app level. iOS uses different certificate validation mechanisms (CFNetwork, URLSession) and does not have an equivalent config file. iOS requires Frida hooks, Objection, or code patching instead.

What if the app uses multi-pin (backup pins) or CA pinning?

Multi-pin and CA pinning are more resilient because they allow fallback certificates or any cert from a trusted CA. Frida hooks that bypass the entire TrustManager or CertificatePinner check work regardless of pinning complexity. For CA pinning, installing your proxy CA as a system certificate (via Magisk or system partition) may allow traffic to pass without code modification.

How do I know if my bypass worked?

Configure your HTTPS proxy to intercept traffic and check if the app's requests appear in the proxy without SSL/certificate errors. If pinning is still active, the app will reject the proxy's certificate and fail to connect. Frida console output will also log '[Bypass]' messages when hooks are triggered.

Full instructions (SKILL.md)

Source of truth, from yaklang/hack-skills.


name: mobile-ssl-pinning-bypass description: >- Mobile SSL pinning bypass playbook. Use when intercepting HTTPS traffic from mobile applications that implement certificate pinning, public key pinning, or SPKI hash pinning on Android and iOS, including React Native, Flutter, and Xamarin frameworks.

SKILL: Mobile SSL Pinning Bypass — Expert Attack Playbook

AI LOAD INSTRUCTION: Expert SSL pinning bypass techniques for mobile platforms. Covers Android and iOS bypass methods (Frida, Objection, Xposed, SSL Kill Switch), framework-specific bypasses (Flutter, React Native, Xamarin), and troubleshooting non-standard pinning implementations. Base models miss framework-specific hook points and multi-layer pinning configurations.

0. RELATED ROUTING

Before going deep, consider loading:

  • android-pentesting-tricks for broader Android testing beyond SSL bypass
  • ios-pentesting-tricks for broader iOS testing beyond SSL bypass
  • api-sec once traffic is intercepted for API-level testing

1. SSL PINNING TYPES

Pinning TypeWhat Is PinnedResilienceCommon In
Certificate pinningExact leaf certificate (DER/PEM)Low (breaks on cert rotation)Legacy apps
Public key pinningSubject Public Key InfoMedium (survives cert renewal if key unchanged)Modern apps
SPKI hash pinningSHA-256 of SPKIMedium (same as public key)OkHttp, AFNetworking
CA pinningIntermediate or root CA certHigh (any cert from that CA works)Enterprise apps
Multi-pin (backup pins)Primary + backup pinsHigh (fallback pins)HPKP-aware apps

How Pinning Works

TLS Handshake
│
├── Server presents certificate chain
│
├── Standard validation (system trust store)
│   └── Passes? continue : connection fails
│
└── Pin validation (app-level check)
    ├── Extract server cert/pubkey/SPKI hash
    ├── Compare against embedded pins
    └── Match found? → allow : → reject connection

2. ANDROID BYPASS METHODS

2.1 Frida Universal SSL Bypass

// Hooks TrustManager, OkHttp, Volley, Retrofit, Conscrypt
Java.perform(function() {

    // ── TrustManagerImpl (Android system) ──
    try {
        var TMI = Java.use('com.android.org.conscrypt.TrustManagerImpl');
        TMI.verifyChain.implementation = function() {
            console.log('[Bypass] TrustManagerImpl.verifyChain');
            return arguments[0]; // return untouched chain
        };
    } catch(e) {}

    // ── X509TrustManager (custom implementations) ──
    var TrustManager = Java.registerClass({
        name: 'com.bypass.TrustManager',
        implements: [Java.use('javax.net.ssl.X509TrustManager')],
        methods: {
            checkClientTrusted: function() {},
            checkServerTrusted: function() {},
            getAcceptedIssuers: function() { return []; }
        }
    });

    var SSLContext = Java.use('javax.net.ssl.SSLContext');
    SSLContext.init.overload('[Ljavax.net.ssl.KeyManager;',
        '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom')
        .implementation = function(km, tm, sr) {
        console.log('[Bypass] SSLContext.init');
        this.init(km, [TrustManager.$new()], sr);
    };

    // ── OkHttp3 CertificatePinner ──
    try {
        var CP = Java.use('okhttp3.CertificatePinner');
        CP.check.overload('java.lang.String', 'java.util.List').implementation = function() {
            console.log('[Bypass] OkHttp3 CertificatePinner.check: ' + arguments[0]);
        };
        // check$okhttp variant (OkHttp 4.x)
        try { CP['check$okhttp'].implementation = function() {}; } catch(e) {}
    } catch(e) {}

    // ── Retrofit / OkHttp interceptor ──
    try {
        var OkHttpClient = Java.use('okhttp3.OkHttpClient$Builder');
        OkHttpClient.certificatePinner.implementation = function(pinner) {
            console.log('[Bypass] OkHttpClient.Builder.certificatePinner');
            return this; // return builder without pinner
        };
    } catch(e) {}

    // ── Volley (HurlStack) ──
    try {
        var HurlStack = Java.use('com.android.volley.toolbox.HurlStack');
        HurlStack.createConnection.implementation = function(url) {
            console.log('[Bypass] Volley HurlStack: ' + url);
            var conn = this.createConnection(url);
            // Remove hostname verifier
            conn.setHostnameVerifier(Java.use(
                'javax.net.ssl.HttpsURLConnection').getDefaultHostnameVerifier());
            return conn;
        };
    } catch(e) {}

    // ── Conscrypt / BoringSSL (modern Android) ──
    try {
        var Conscrypt = Java.use('org.conscrypt.ConscryptFileDescriptorSocket');
        Conscrypt.verifyCertificateChain.implementation = function() {
            console.log('[Bypass] Conscrypt verifyCertificateChain');
        };
    } catch(e) {}

    // ── Apache HttpClient (legacy) ──
    try {
        var AbstractVerifier = Java.use('org.apache.http.conn.ssl.AbstractVerifier');
        AbstractVerifier.verify.overload('java.lang.String', '[Ljava.lang.String;',
            '[Ljava.lang.String;', 'boolean').implementation = function() {
            console.log('[Bypass] Apache AbstractVerifier');
        };
    } catch(e) {}

    // ── HostnameVerifier ──
    try {
        var HV = Java.use('javax.net.ssl.HttpsURLConnection');
        HV.setDefaultHostnameVerifier.implementation = function(v) {
            console.log('[Bypass] Ignoring custom HostnameVerifier');
        };
    } catch(e) {}

    console.log('[+] Android universal SSL bypass loaded');
});

2.2 Objection (One Command)

objection -g com.target.app explore --startup-command "android sslpinning disable"

2.3 Network Security Config (Debug Override)

<!-- AndroidManifest.xml: android:networkSecurityConfig="@xml/network_security_config" -->

<!-- res/xml/network_security_config.xml -->
<network-security-config>
  <base-config>
    <trust-anchors>
      <certificates src="system" />
      <certificates src="user" />     <!-- Trust user-installed CAs -->
    </trust-anchors>
  </base-config>
</network-security-config>

Workflow: decompile APK → add/modify config → repackage → re-sign → install.

apktool d target.apk -o target_dir
# Edit res/xml/network_security_config.xml
# Add reference in AndroidManifest.xml if missing
apktool b target_dir -o target_patched.apk
zipalign -v 4 target_patched.apk target_aligned.apk
apksigner sign --ks my-key.keystore target_aligned.apk
adb install target_aligned.apk

2.4 Xposed / LSPosed Modules

ModuleMethodScopeRoot Required
JustTrustMeHooks TrustManager + OkHttpPer-appYes (Xposed)
SSLUnpinningHooks certificate validationPer-appYes (LSPosed)
TrustMeAlreadyGlobal TrustManager bypassSystem-wideYes (LSPosed)

2.5 Magisk + System CA Installation

# Install proxy CA as system cert (Android 7+ requires this for system-level trust)
# Method 1: MagiskTrustUserCerts module
# Moves user CAs to /system/etc/security/cacerts/ via Magisk overlay

# Method 2: Manual (requires root)
adb push burp_ca.pem /sdcard/
adb shell
su
mount -o remount,rw /system
cp /sdcard/burp_ca.pem /system/etc/security/cacerts/9a5ba575.0  # hash-named
chmod 644 /system/etc/security/cacerts/9a5ba575.0
mount -o remount,ro /system

# Get correct hash filename:
openssl x509 -inform PEM -subject_hash_old -in burp_ca.pem | head -1
# Output: 9a5ba575 → filename is 9a5ba575.0

2.6 Manual Decompile → Patch → Repackage

# Step 1: Decompile
jadx -d decompiled/ target.apk

# Step 2: Find pinning code
grep -r "CertificatePinner\|X509TrustManager\|checkServerTrusted\|ssl" decompiled/

# Step 3: Identify pinning implementation and patch
# Use smali editing for precise control:
apktool d target.apk
# Edit smali files to NOP out pinning checks
# Look for invoke-virtual {checkServerTrusted} and replace with return-void

# Step 4: Repackage and sign
apktool b target_dir -o patched.apk
apksigner sign --ks debug.keystore patched.apk

3. iOS BYPASS METHODS

3.1 Frida (SecTrust Hooks)

// Hook core iOS SSL validation functions
var SecTrustEvaluateWithError = Module.findExportByName('Security', 'SecTrustEvaluateWithError');
Interceptor.attach(SecTrustEvaluateWithError, {
    onLeave: function(retval) {
        retval.replace(ptr(1));
    }
});

var SecTrustEvaluate = Module.findExportByName('Security', 'SecTrustEvaluate');
Interceptor.attach(SecTrustEvaluate, {
    onLeave: function(retval) {
        retval.replace(ptr(0));
    }
});

// Hook SSLHandshake (lower-level)
var SSLHandshake = Module.findExportByName('Security', 'SSLHandshake');
if (SSLHandshake) {
    Interceptor.attach(SSLHandshake, {
        onLeave: function(retval) {
            if (retval.toInt32() === -9807) { // errSSLXCertChainInvalid
                retval.replace(ptr(0));
            }
        }
    });
}

// Hook NSURLSession delegate method
try {
    var cls = ObjC.classes.NSURLSession;
    // Hook URLSession:didReceiveChallenge:completionHandler: on delegates
    ObjC.enumerateLoadedClasses({
        onMatch: function(name) {
            try {
                var methods = ObjC.classes[name].$ownMethods;
                for (var i = 0; i < methods.length; i++) {
                    if (methods[i].indexOf('didReceiveChallenge') !== -1 &&
                        methods[i].indexOf('completionHandler') !== -1) {
                        console.log('[SSL] Found delegate: ' + name + ' ' + methods[i]);
                    }
                }
            } catch(e) {}
        },
        onComplete: function() {}
    });
} catch(e) {}

3.2 Objection (One Command)

objection -g com.target.app explore --startup-command "ios sslpinning disable"

3.3 SSL Kill Switch 2 (Jailbreak Tweak)

# Install via Cydia/Sileo
# Package: com.nablac0d3.sslkillswitch2
# Disables SSL pinning system-wide or per-app via Settings toggle

# Hooks:
# - SecTrustEvaluate
# - SSLHandshake
# - SSLSetSessionOption
# - tls_helper_create_peer_trust

3.4 Library-Specific Hooks

LibraryiOS Hook PointFrida Approach
AFNetworkingAFSecurityPolicy.evaluateServerTrust:forDomain:Return YES
AlamofireServerTrustManager.evaluate(_:forHost:)Skip evaluation
TrustKitTSKPinningValidator verifyPublicKeyPin:Return success
NSURLSessionURLSession:didReceiveChallenge:completionHandler:Call completionHandler with .useCredential

3.5 Manual Binary Patch

# Find pinning function in binary
strings decrypted_binary | grep -i "pin\|cert\|trust"
# Disassemble and find the validation function
# Replace comparison/branch instruction with NOP or unconditional pass

# LLDB runtime modification
lldb -n TargetApp
(lldb) breakpoint set -n "SecTrustEvaluateWithError"
(lldb) breakpoint command add 1
> thread return 1
> continue
> DONE

4. FRAMEWORK-SPECIFIC BYPASSES

4.1 Flutter

Flutter uses Dart's dart:io library with BoringSSL underneath. Standard Frida hooks on Java/ObjC layers don't work.

// Flutter SSL bypass — must hook BoringSSL directly
// Find ssl_crypto_x509_session_verify_cert_chain in libflutter.so
var libflutter = Process.findModuleByName('libflutter.so');  // Android
// var libflutter = Process.findModuleByName('Flutter');       // iOS

// Hook ssl_verify_peer_cert (BoringSSL function)
// Signature varies by Flutter version — use pattern scanning
var pattern = 'FF C3 ..';  // Example pattern, varies
var matches = Memory.scan(libflutter.base, libflutter.size, pattern, {
    onMatch: function(address, size) {
        console.log('[Flutter] Potential verify function at: ' + address);
        Interceptor.attach(address, {
            onLeave: function(retval) {
                retval.replace(ptr(0));  // SSL_VERIFY_OK
            }
        });
    },
    onComplete: function() {}
});

// Alternative: use reflutter tool for automated patching
// reflutter target.apk
// This patches BoringSSL in the Flutter engine directly

reflutter tool (recommended for Flutter apps):

pip install reflutter
reflutter target.apk
# Outputs patched APK that redirects traffic to your proxy
# Also disables SSL verification in the BoringSSL engine

4.2 React Native

React Native uses platform networking: OkHttp on Android, NSURLSession on iOS.

PlatformNetworking StackBypass Method
AndroidOkHttp3Standard OkHttp CertificatePinner hook
iOSNSURLSessionStandard SecTrust hooks
Android (Hermes)Same OkHttpSame hooks, but Hermes JIT may need additional handling
// React Native Android — same as OkHttp bypass
Java.perform(function() {
    try {
        var CP = Java.use('okhttp3.CertificatePinner');
        CP.check.overload('java.lang.String', 'java.util.List').implementation = function() {};
    } catch(e) { console.log('OkHttp3 not found, trying okhttp2...'); }

    try {
        var CP2 = Java.use('com.squareup.okhttp.CertificatePinner');
        CP2.check.overload('java.lang.String', 'java.util.List').implementation = function() {};
    } catch(e) {}
});

4.3 Xamarin

// Xamarin pinning typically via:
// ServicePointManager.ServerCertificateValidationCallback
// or custom HttpClientHandler
// Frida bypass for Xamarin (Mono runtime)
// Hook Mono method: System.Net.ServicePointManager.set_ServerCertificateValidationCallback
var mono_method = Module.findExportByName('libmonosgen-2.0.so',
    'mono_runtime_invoke');
// More practical: hook the managed callback at CIL level
// Use Frida's Mono bridge or objection's built-in Xamarin support

// Objection has built-in Xamarin bypass:
// objection -g com.target.app explore
// > android sslpinning disable   (covers Xamarin on Android)

5. CERTIFICATE TRANSPARENCY & HPKP

TechnologyStatusImpact on Testing
Certificate Transparency (CT)Active, enforced by browsersMobile apps rarely enforce CT; not a bypass obstacle
HPKP (HTTP Public Key Pinning)Deprecated (2018)Legacy apps may still check; remove header from proxy response
Expect-CT headerDeprecated (2024)Minimal impact on mobile testing
CT in mobile appsRareOnly Google apps enforce via custom CT checks

6. TROUBLESHOOTING

6.1 Common Failures

SymptomCauseFix
Bypass script loaded but traffic still failsMultiple pinning layersHook ALL layers: TrustManager + OkHttp + custom checks
"Client certificate required"Mutual TLS (mTLS)Extract client cert from app bundle/keychain, import into proxy
Connection works but no HTTP trafficNon-HTTP protocol (MQTT, gRPC, WebSocket)Use Wireshark or protocol-specific proxy
App crashes after bypassAnti-tampering detects hooksBypass integrity checks first, then SSL
Proxy CA not trustedAndroid 7+ user CA restrictionsInstall CA as system cert (Magisk module)
Flutter app ignores hooksBoringSSL not hooked at native layerUse reflutter or native BoringSSL hooks
Certificate chain validation timeoutOCSP stapling mismatchDisable OCSP checks or mock OCSP responder

6.2 Diagnostic Steps

# Verify proxy CA is installed correctly
# Android:
adb shell "ls /system/etc/security/cacerts/ | grep $(openssl x509 -subject_hash_old -in ca.pem | head -1)"

# iOS: Settings → General → About → Certificate Trust Settings

# Check if target app is actually using SSL (vs. plain HTTP)
# Wireshark filter: tcp.port == 443 and ip.addr == <device_ip>

# Check if Frida is hooking the right process
frida-ps -U | grep target

# Verbose Frida output for debugging hooks
frida -U -f com.target.app -l bypass.js --debug

7. SSL PINNING BYPASS DECISION TREE

Need to intercept mobile app HTTPS traffic
│
├── Platform?
│   ├── Android ↓
│   │   ├── Rooted device available?
│   │   │   ├── Yes → Frida universal bypass (§2.1) [FIRST TRY]
│   │   │   │   ├── Works? → done
│   │   │   │   └── Fails? → add Conscrypt + Volley hooks
│   │   │   ├── Still fails? → LSPosed + TrustMeAlready (§2.4)
│   │   │   └── Still fails? → install CA as system cert (§2.5)
│   │   └── No root?
│   │       ├── Debug build? → Network Security Config (§2.3)
│   │       └── Release build? → decompile + patch + repackage (§2.6)
│   │
│   └── iOS ↓
│       ├── Jailbroken device available?
│       │   ├── Yes → Objection ios sslpinning disable (§3.2) [FIRST TRY]
│       │   │   ├── Works? → done
│       │   │   └── Fails? → Frida SecTrust hooks (§3.1)
│       │   ├── Still fails? → SSL Kill Switch 2 (§3.3)
│       │   └── Still fails? → library-specific hooks (§3.4)
│       └── No jailbreak?
│           ├── Re-sign with Frida gadget → run Frida hooks
│           └── Binary patch → sideload (§3.5)
│
├── Framework-specific app?
│   ├── Flutter → reflutter tool or BoringSSL native hooks (§4.1)
│   ├── React Native → standard platform hooks (§4.2)
│   └── Xamarin → Objection or Mono runtime hooks (§4.3)
│
├── Bypass works but issues remain?
│   ├── Client cert required? → extract + import to proxy (§6.1)
│   ├── Non-HTTP protocol? → protocol-specific tooling (§6.1)
│   └── App crashes? → fix anti-tampering first (§6.1)
│
└── All methods fail?
    ├── Analyze traffic at network level (Wireshark/tcpdump)
    ├── Check for custom proprietary protocol
    └── Consider iptables + transparent proxy approach

8. PROXY SETUP QUICK REFERENCE

Proxy ToolBest ForSSL Bypass Integration
Burp SuiteFull HTTP analysisImport CA to device
mitmproxyScripted interceptionmitmproxy --set confdir=~/.mitmproxy
Charles ProxymacOS-native, easy setupBuilt-in CA installation
ProxymanmacOS/iOS nativeDirect iOS device support
HTTP ToolkitQuick Android setupAutomated CA + Frida bypass
# Android proxy setup
adb shell settings put global http_proxy <host_ip>:8080

# Remove proxy
adb shell settings put global http_proxy :0

# iOS proxy: Settings → Wi-Fi → Configure Proxy → Manual