/bypassing-mobile-pinning
Diagnose and defeat TLS interception failures in mobile apps — certificate pinning, Android Network Security Config, user-CA distrust, native BoringSSL pinning, and mutual TLS — using objection, Frida, SSL Kill Switch, and APK patching. Use when Burp, mitmproxy, or Charles shows
$ npx -y skills add trilwu/secskills --skill bypassing-mobile-pinning --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/bypassing-mobile-pinning
Context preview
The summary Claude sees to decide when to auto-load this skill.
Diagnose and defeat TLS interception failures in mobile apps — certificate pinning, Android Network Security Config, user-CA distrust, native BoringSSL pinning, and mutual TLS — using objection, Frida, SSL Kill Switch, and APK patching. Use when Burp, mitmproxy, or Charles shows
SKILL.md
bypassing-mobile-pinning.SKILL.mdname: bypassing-mobile-pinning
description: Diagnose and defeat TLS interception failures in mobile apps — certificate pinning, Android Network Security Config, user-CA distrust, native BoringSSL pinning, and mutual TLS — using objection, Frida, SSL Kill Switch, and APK patching. Use when Burp, mitmproxy, or Charles shows a TLS handshake error, an empty proxy, or "network error" from a mobile app, or when a target is known to pin certificates.
verified: 2026-07-27
Bypassing Mobile Pinning
Most time lost here is spent bypassing pinning that was never there. An empty proxy has at least six causes, and the fix for each is different — one of them is not pinning at all but Android's default distrust of user CAs, and another is that the app has its own TLS stack and never saw your proxy. Diagnose before you reach for a bypass script.
Use only against apps you are authorized to test.
When to Use
- Burp/mitmproxy/Charles shows a TLS handshake failure from a mobile app
- The proxy shows nothing while the app clearly reaches the network
- The app reports "network error", "connection insecure", or similar with the
proxy configured
- You know the target pins, and need the right hook for its stack
When NOT to Use
- **Flutter targets** — use `reversing-flutter-apps`; Flutter's failure looks
identical but the cause and fix are specific to its engine
- **The wider mobile assessment** — use `testing-mobile-applications`
- **The backend once traffic is visible** — use `testing-apis`
- **Reversing a native `.so` in depth** — use `analyzing-binaries`
Diagnose First
Run this before installing anything. The symptom tells you the cause.
| Symptom | Likely cause | Fix | | --- | --- | --- | | Proxy sees the CONNECT, then TLS alert `unknown_ca` / `bad_certificate` | Your CA is not trusted | Install as **system** CA, or patch Network Security Config | | Proxy sees the CONNECT, then alert only for *some* domains | Real pinning, scoped to those hosts | Hook the pinning check | | Proxy sees **nothing at all**, app works | App ignores the system proxy | Force traffic at the network layer (Flutter, gRPC, some SDKs) | | Proxy sees the handshake, server rejects the client | Mutual TLS — the app presents a client certificate | Extract the client cert; this is not pinning | | Works on emulator, fails on device (or vice versa) | Root/emulator detection, not TLS | Different problem — see the detection section | | Fails only after login | Pinning applied to the API host only | Hook, then re-check |
# What does the app actually declare?
apktool d target.apk -o out
cat out/res/xml/network_security_config.xml 2>/dev/null
rg -n 'networkSecurityConfig|usesCleartextTraffic' out/AndroidManifest.xml
# Which TLS stack is in play?
rg -l 'okhttp3|CertificatePinner|TrustKit|AFNetworking|Alamofire' out/ 2>/dev/null | head
unzip -l target.apk | rg 'libssl|libcrypto|libconscrypt|libflutter|libil2cpp|libmonodroid'
**Check Network Security Config before assuming pinning.** Since Android 7, apps do not trust user-installed CAs by default. An app with no pinning code at all will still fail interception. Two fixes:
# A. Install your CA as a SYSTEM certificate (no app modification — preferred)
# Emulator:
emulator -avd <name> -writable-system
adb root && adb remount
openssl x509 -inform PEM -subject_hash_old -in burp.pem | head -1 # → e.g. 9a5ba580
cp burp.pem 9a5ba580.0 && adb push 9a5ba580.0 /system/etc/security/cacerts/
adb shell chmod 644 /system/etc/security/cacerts/9a5ba580.0
# Rooted device: use a Magisk module so the change survives reboot
# B. Patch the config and repack (when you cannot get a system CA)
# Add to network_security_config.xml:
# <base-config><trust-anchors>
# <certificates src="system"/><certificates src="user"/>
# </trust-anchors></base-config>
apktool b out -o patched.apk && apksigner sign --ks debug.keystore patched.apk
Option A is better: it modifies nothing in the app, so integrity checks, signature checks, and Play Integrity are unaffected. Reach for B only when you cannot get a system CA onto the device.
Android: Hook by Stack
Identify the stack, then hook it. A generic script that misses will look like "the bypass failed" when it simply targeted the wrong layer.
# Start here — covers the common Java-layer stacks in one shot
objection -g com.target.app explore
# then: android sslpinning disable
# Or a maintained universal script
frida -U -f com.target.app -l android-ssl-bypass.js --no-pause
| Stack | Marker | Hook point | | --- | --- | --- | | OkHttp 3/4 | `okhttp3.CertificatePinner` | `CertificatePinner.check()` — return normally | | HttpURLConnection | `javax.net.ssl.*` | Custom `X509TrustManager.checkServerTrusted()` | | Conscrypt | `libconscrypt_jni.so` | `Platform.checkServerTrusted`, or the native layer | | Apache HttpClient | `org.apache.http` | `SSLSocketFactory` verifier | | WebView | `onReceivedSslError` | Force `handler.proceed()` | | **Native / BoringSSL** | `libssl.so`, custom `.so` | `SSL_CTX_set_custom_verify`, `ssl_verify_peer_cert` | | Xamarin | `libmonodroid.so`, `libxamarin*` | Mono-level `ServicePointManager` / `HttpClientHandler` | | Unity | `libil2cpp.so` | `UnityWebRequest` cert handler, or BestHTTP's verifier | | gRPC | `libgrpc.so` | Channel credentials; often also ignores the proxy |
**Native pinning is where the universal scripts stop working.** When `objection` reports success but traffic still fails, the check is in a `.so`. BoringSSL's verification entry points are not exported, so scripts locate them by pattern scanning — and the pattern is version-specific. Confirm what you are dealing with:
# Is a native TLS stack even present?
unzip -j target.apk 'lib/arm64-v8a/*' -d libs && rg -l 'SSL_|BoringSSL|s2n' libs/
# Trace the native calls to see which library terminates TLS
frida-trace -U -f com.target.app -i 'SSL_*' -i '*verify*'
For Unity, Xamarin, and Flutter bui
Read more
name: bypassing-mobile-pinning description: Diagnose and defeat TLS interception failures in mobile apps — certificate pinning, Android Network Security Config, user-CA distrust, native BoringSSL pinning, and mutual TLS — using objection, Frida, SSL Kill Switch, and APK patching. Use when Burp, mitmproxy, or Charles shows a TLS handshake error, an empty proxy, or "network error" from a mobile app, or when a target is known to pin certificates. verified: 2026-07-27
Bypassing Mobile Pinning
Most time lost here is spent bypassing pinning that was never there. An empty proxy has at least six causes, and the fix for each is different — one of them is not pinning at all but Android's default distrust of user CAs, and another is that the app has its own TLS stack and never saw your proxy. Diagnose before you reach for a bypass script.
Use only against apps you are authorized to test.
When to Use
- Burp/mitmproxy/Charles shows a TLS handshake failure from a mobile app
- The proxy shows nothing while the app clearly reaches the network
- The app reports "network error", "connection insecure", or similar with the
proxy configured
- You know the target pins, and need the right hook for its stack
When NOT to Use
- **Flutter targets** — use `reversing-flutter-apps`; Flutter's failure looks
identical but the cause and fix are specific to its engine
- **The wider mobile assessment** — use `testing-mobile-applications`
- **The backend once traffic is visible** — use `testing-apis`
- **Reversing a native `.so` in depth** — use `analyzing-binaries`
Diagnose First
Run this before installing anything. The symptom tells you the cause.
| Symptom | Likely cause | Fix | | --- | --- | --- | | Proxy sees the CONNECT, then TLS alert `unknown_ca` / `bad_certificate` | Your CA is not trusted | Install as **system** CA, or patch Network Security Config | | Proxy sees the CONNECT, then alert only for *some* domains | Real pinning, scoped to those hosts | Hook the pinning check | | Proxy sees **nothing at all**, app works | App ignores the system proxy | Force traffic at the network layer (Flutter, gRPC, some SDKs) | | Proxy sees the handshake, server rejects the client | Mutual TLS — the app presents a client certificate | Extract the client cert; this is not pinning | | Works on emulator, fails on device (or vice versa) | Root/emulator detection, not TLS | Different problem — see the detection section | | Fails only after login | Pinning applied to the API host only | Hook, then re-check |
# What does the app actually declare? apktool d target.apk -o out cat out/res/xml/network_security_config.xml 2>/dev/null rg -n 'networkSecurityConfig|usesCleartextTraffic' out/AndroidManifest.xml # Which TLS stack is in play? rg -l 'okhttp3|CertificatePinner|TrustKit|AFNetworking|Alamofire' out/ 2>/dev/null | head unzip -l target.apk | rg 'libssl|libcrypto|libconscrypt|libflutter|libil2cpp|libmonodroid'
**Check Network Security Config before assuming pinning.** Since Android 7, apps do not trust user-installed CAs by default. An app with no pinning code at all will still fail interception. Two fixes:
# A. Install your CA as a SYSTEM certificate (no app modification — preferred) # Emulator: emulator -avd <name> -writable-system adb root && adb remount openssl x509 -inform PEM -subject_hash_old -in burp.pem | head -1 # → e.g. 9a5ba580 cp burp.pem 9a5ba580.0 && adb push 9a5ba580.0 /system/etc/security/cacerts/ adb shell chmod 644 /system/etc/security/cacerts/9a5ba580.0 # Rooted device: use a Magisk module so the change survives reboot # B. Patch the config and repack (when you cannot get a system CA) # Add to network_security_config.xml: # <base-config><trust-anchors> # <certificates src="system"/><certificates src="user"/> # </trust-anchors></base-config> apktool b out -o patched.apk && apksigner sign --ks debug.keystore patched.apk
Option A is better: it modifies nothing in the app, so integrity checks, signature checks, and Play Integrity are unaffected. Reach for B only when you cannot get a system CA onto the device.
Android: Hook by Stack
Identify the stack, then hook it. A generic script that misses will look like "the bypass failed" when it simply targeted the wrong layer.
# Start here — covers the common Java-layer stacks in one shot objection -g com.target.app explore # then: android sslpinning disable # Or a maintained universal script frida -U -f com.target.app -l android-ssl-bypass.js --no-pause
| Stack | Marker | Hook point | | --- | --- | --- | | OkHttp 3/4 | `okhttp3.CertificatePinner` | `CertificatePinner.check()` — return normally | | HttpURLConnection | `javax.net.ssl.*` | Custom `X509TrustManager.checkServerTrusted()` | | Conscrypt | `libconscrypt_jni.so` | `Platform.checkServerTrusted`, or the native layer | | Apache HttpClient | `org.apache.http` | `SSLSocketFactory` verifier | | WebView | `onReceivedSslError` | Force `handler.proceed()` | | **Native / BoringSSL** | `libssl.so`, custom `.so` | `SSL_CTX_set_custom_verify`, `ssl_verify_peer_cert` | | Xamarin | `libmonodroid.so`, `libxamarin*` | Mono-level `ServicePointManager` / `HttpClientHandler` | | Unity | `libil2cpp.so` | `UnityWebRequest` cert handler, or BestHTTP's verifier | | gRPC | `libgrpc.so` | Channel credentials; often also ignores the proxy |
**Native pinning is where the universal scripts stop working.** When `objection` reports success but traffic still fails, the check is in a `.so`. BoringSSL's verification entry points are not exported, so scripts locate them by pattern scanning — and the pattern is version-specific. Confirm what you are dealing with:
# Is a native TLS stack even present? unzip -j target.apk 'lib/arm64-v8a/*' -d libs && rg -l 'SSL_|BoringSSL|s2n' libs/ # Trace the native calls to see which library terminates TLS frida-trace -U -f com.target.app -i 'SSL_*' -i '*verify*'
For Unity, Xamarin, and Flutter bui
**Security skills for Claude Code that encode the judgment professionals bring, not another wrapper around a scanner.** Offense, defense, and the reverse-engineering and code-audit core both sides share. Every skill is fact-checked against primary sources.

