Intercepting HTTPS traffic from Android apps is essential for security research, bug bounties, privacy auditing, and QA testing. This guide walks you through the complete setup using mitmproxy and explains why SSL-unpinned APKs are the easiest path — especially on Android 14 and newer.
What You'll Need
- A computer running mitmproxy (Windows, macOS, or Linux)
- An Android device or emulator on the same network
- An SSL-unpinned APK of the app you want to inspect
- 5–10 minutes of setup time
Step 1: Install mitmproxy
mitmproxy is a free, open-source interactive HTTPS proxy. Install it on your computer:
# macOSbrew install mitmproxy
# Windows (via pip)pip install mitmproxy
# Linuxsudo apt install mitmproxy# orpip install mitmproxyStart mitmproxy with the web interface:
mitmweb --listen-port 8080This starts the proxy on port 8080 and opens a web UI at http://localhost:8081 where you can inspect intercepted traffic in real time.
Step 2: Configure Android Proxy Settings
On your Android device, configure the Wi-Fi network to use your computer as a proxy:
- Open Settings → Wi-Fi → long-press your network → Modify Network
- Set Proxy to "Manual"
- Enter your computer's local IP address as the proxy hostname
- Set the port to 8080
- Save the settings
To find your computer's local IP, run ifconfig (macOS/Linux) or ipconfig (Windows) and look for your network adapter's IPv4 address.
Step 3: Install the mitmproxy CA Certificate
With the proxy configured, open Chrome on your Android device and navigate to mitm.it. This page is served by mitmproxy and provides the CA certificate for your platform.
- Tap the Android button to download the certificate
- Go to Settings → Security → Install a certificate → CA certificate
- Select the downloaded certificate file
- Confirm the installation
This installs mitmproxy's CA certificate as a user-trusted certificate on your device.
Step 4: Install the SSL-Unpinned APK
This is the key step. Apps from Google Play have SSL pinning enabled — they only trust specific certificates hardcoded into the app binary, ignoring any certificates you install on the device. An SSL-unpinned APK has all certificate pinning code disabled at the binary level.
# Install the unpinned APK via adbadb install path/to/unpinned-app.apk
# If upgrading from a signed version, uninstall firstadb uninstall com.example.appadb install path/to/unpinned-app.apkStep 5: Start Inspecting Traffic
Open the installed app and use it normally. All HTTPS traffic flows through mitmproxy, and you'll see every request and response in the mitmweb interface:
- API endpoints, request parameters, and query strings
- Response bodies (JSON, protobuf, GraphQL)
- Headers, cookies, and authentication tokens
- WebSocket frames for real-time features
The Android 14 Problem
Android 14 introduced a critical change that broke existing bypass methods. The system now mounts /system/etc/security/cacerts as strictly read-only, preventing modifications even with root access. This means:
- Magisk modules like TrustUserCertificates can no longer move user certificates to the system store
- Manual certificate injection via
adb pushto the system CA directory is blocked - Root access alone is no longer sufficient to intercept traffic from apps with certificate pinning
On Android 14+, you're essentially limited to two approaches: dynamic native instrumentation (complex Frida setups) or pre-patched APKs. The patched APK approach is dramatically simpler since it requires zero root access or runtime manipulation.
Why Unpinned APKs Work (And Other Methods Don't)
Modern apps implement SSL pinning at multiple levels, and each level requires a different bypass technique:
- Network Security Config — can be bypassed by modifying the app's XML, but most apps with serious pinning don't rely on it.
- OkHttp CertificatePinner — requires hooking Java methods with Frida (needs root). Works for simple apps but fails when native code takes over.
- Native BoringSSL pinning — requires binary patching of .so libraries with tools like Ghidra or IDA Pro. Architecture-specific (arm64 vs x86_64).
- Custom protocols (Noise, MTProto, MSL) — deep reverse engineering of proprietary crypto. Even with TLS bypassed, traffic may still be encrypted.
A pre-patched APK addresses all of these at once. The conditional branch instructions in the native verification functions are patched to NOP or unconditional jumps, permanently disabling the checks before the app is even installed.
Common Mistakes
- Using outdated Frida scripts — Generic scripts like
universal-android-ssl-pinning-bypass.jshook Java-level classes. They fail silently against apps using native C++ pinning. The logcat showsSSL error code 1, net_error -202but no traffic appears in the proxy. - Architecture mismatch — Pushing an x86_64 compiled .so library to an ARM64 device (or vice versa) causes immediate crash with
UnsatisfiedLinkError. Emulators need x86_64 binaries; physical phones need arm64-v8a. - Not clearing app cache — Native libraries cache TLS sessions. After applying a hook or patch, force-stop the app and clear its data before relaunching.
- Expecting readable WhatsApp traffic — Even with TLS pinning bypassed, WhatsApp uses the Noise Protocol for E2EE. You'll see binary data, not readable messages.
Alternatives to mitmproxy
The same unpinned APK setup works with any HTTPS proxy:
- Charles Proxy — GUI-based, great for beginners ($50 license)
- Burp Suite — industry standard for penetration testing (free Community Edition)
- HTTP Toolkit — modern UI with built-in Android integration (free for basic use)
- Reqable — cross-platform with Protocol Buffer support
Troubleshooting
- App won't connect — make sure you're using the unpinned APK, not the Play Store version. Uninstall the original first.
- No traffic in proxy — verify proxy settings are correct and your device can reach your computer. Try opening
mitm.itin Chrome to confirm. - Certificate errors in browser — re-download and install the CA from
mitm.it. On Android 14+, the certificate must be installed as a user CA (not system). - Some requests missing — the app may use QUIC (HTTP/3 over UDP). Block UDP port 443 at the firewall level to force a TCP downgrade through your proxy.