Flutter SSL/TLS Bypass
Flutter apps run networking code inside libflutter.so (Android) or Flutter.framework (iOS), which bundles BoringSSL. This means:
- Java/Kotlin SSL pinning bypasses don't work - verification happens in native code
- System CA store is ignored - BoringSSL uses its own CA store
- Symbols are stripped - you can't hook by function name
This skill covers three approaches to bypass Flutter TLS verification:
Quick Decision Tree
| Situation | Recommended Approach |
|---|---|
| Rooted device, need fast bypass | Frida Codeshare + system CA |
| Can't use Codeshare, have Ghidra | Offset-based hooking |
| Can rebuild the app | Binary patching with reFlutter |
| Multiple Flutter versions | Pattern-based Frida hooking |
Method 1: Frida Codeshare (Fastest)
When you have a rooted device or writable AVD, this is the quickest path:
Step 1: Install Proxy CA in System Store
# Get Burp CA certificate (DER format)
# Hash it and push to system store
adb push burp.der /system/etc/security/cacerts/
adb shell "chmod 644 /system/etc/security/cacerts/burp.der"
See install-burp-certificate.md for full instructions on hashing and renaming.
Step 2: Deploy Frida Server
adb push frida-server-17.0.5-android-x86_64 /data/local/tmp/frida-server
adb shell "su -c 'chmod 755 /data/local/tmp/frida-server && /data/local/tmp/frida-server &'"
Step 3: Spawn App with TLS Bypass
frida -U -f com.example.target --codeshare TheDauntless/disable-flutter-tls-v1 --no-pause
This Codeshare script overrides the Flutter TLS verifier to accept all certificates.
Step 4: Route Traffic Through Proxy
# Emulator
adb shell settings put global http_proxy 10.0.2.2:8080
# Or use reverse tunnel
adb reverse tcp:8080 tcp:8080
Step 5: Force Socket Redirection (if needed)
If the app ignores proxy settings:
frida -U -f com.example.target --no-pause \
--codeshare TheDauntless/disable-flutter-tls-v1 \
-l scripts/frida4burp.js
Method 2: Offset-Based Hooking (Architecture-Agnostic)
When pattern scanning fails across architectures, hook by absolute offset:
Step 1: Extract libflutter.so
unzip -j app.apk "lib/*/libflutter.so" -d libs/
# Pick the one matching your device architecture
# e.g., libs/lib/x86_64/libflutter.so or libs/lib/arm64-v8a/libflutter.so
Step 2: Analyze in Ghidra
- Open
libflutter.soin Ghidra - Search → For Strings →
ssl_client - Go to XREFs and examine each
FUN_...reference - Find the function with:
- 3 pointer-like arguments
- Boolean return type
- Located near BoringSSL code
This is ssl_crypto_x509_session_verify_cert_chain() from ssl_x509.cc.
Step 3: Calculate Runtime Offset
# Example: Ghidra shows 0x02184644, image base is 0x00100000
# Offset = 0x02184644 - 0x00100000 = 0x02084644
Step 4: Hook at Runtime
Use scripts/offset-hook.js:
# Edit the script with your calculated offset
# Then run:
frida -U -f com.target.app -l scripts/offset-hook.js --no-pause
Method 3: Pattern-Based Hooking
For known Flutter versions, use opcode pattern matching:
Step 1: Get the Pattern
For x86-64, the first 16 bytes of ssl_crypto_x509_session_verify_cert_chain:
55 41 57 41 56 41 55 41 54 53 48 83 EC 38 C6 02
For ARM64/ARMv7, extract from Ghidra:
- Open
libflutter.soin Ghidra - Navigate to the verifier function
- Copy first ~32 bytes as space-separated hex
Step 2: Run Pattern Hook
Use scripts/pattern-hook.js:
# Edit with your architecture's pattern
frida -U -f com.example.app -l scripts/pattern-hook.js
Method 4: Binary Patching with reFlutter
For permanent bypass without runtime hooking:
Step 1: Identify Flutter Version
# Extract libapp.so from APK
unzip app.apk lib/x86_64/libapp.so
# Get snapshot hash
python3 scripts/get_snapshot_hash.py libapp.so
# Output: adb4292f3ec25...
Step 2: Map to Engine Version
- Search the hash in reFlutter enginehash list
- Note the Flutter version and engine commit
- Pull DEPS file to find BoringSSL revision
Step 3: Patch and Rebuild
git clone https://github.com/Impact-I/reFlutter
cd reFlutter
# Follow build instructions for your Flutter version
# Patch ssl_x509.cc to force return 1
# Rebuild libflutter.so
Step 4: Replace in APK
# Extract APK
apktool d app.apk -o app-decompiled
# Replace libflutter.so in appropriate arch folder
# Rebuild and sign
apktool b app-decompiled -o app-patched.apk
apksigner sign --ks mykey.jks app-patched.apk
Forcing Proxy Traffic
Flutter ignores device proxy settings. Options:
| Platform | Method |
|---|---|
| Android Emulator | Settings → Proxy → Manual |
| Physical Device | Evil Wi-Fi AP + DNS spoofing |
| Rooted Device | Magisk module editing /etc/hosts |
| Any | Frida socket redirection (see Method 1, Step 5) |
Verification
After applying any bypass:
- Start Burp/mitmproxy on port 8080
- Launch the app with Frida attached
- Watch for traffic in your proxy
- Test certificate acceptance - Burp's dynamically generated certs should be accepted
If you see SSL handshake failed or no traffic:
- Verify CA is in system store (not user store)
- Check Frida is attached and hook is active
- Confirm proxy routing is working
- Try offset-based method if pattern scanning fails
Troubleshooting
| Problem | Solution |
|---|---|
| Hook not triggering | Verify libflutter.so is loaded: frida-ps -Ua |
| Wrong architecture | Match Frida server and libflutter.so arch (x86_64 vs arm64) |
| Pattern not found | Use offset-based method instead |
| Traffic not routing | Use frida4burp.js for socket redirection |
| App crashes on hook | Ensure return type matches (bool = int in C) |