Flutter apps are notoriously difficult to intercept—they ignore system proxies and use BoringSSL with custom certificate pinning. This guide walks through setting up a VPN on Kali to route iOS traffic, disabling TLS verification with Frida, and capturing packets in Burp. It’s applicable to Android with minor adjustments.

Background

Recently I dove into mobile pentesting for the first time with a Flutter-based app on iOS. Flutter’s architecture makes traffic interception painful because:

  1. No system proxy respect - Flutter uses its own HTTP stack, ignoring iOS/Android proxy settings
  2. BoringSSL - Custom TLS implementation instead of the OS’s certificate store
  3. Certificate pinning - Often baked into the Dart/Flutter code or via libraries like Dio

This guide is based on NVISO’s excellent 2020 blog with their 2022 update for newer Flutter/Dio pinning. The process is similar for Android—swap iOS-specific steps with rooted Android equivalents like ProxyDroid.

Note: This requires a jailbroken iOS device (or rooted Android). Always test in a lab environment. Jailbreaking voids warranties and carries security risks.

Prerequisites

Component iOS Android
Host Kali Linux Kali Linux
Device Jailbroken iOS Rooted Android
Frida Frida server via Cydia Frida server in /data/local/tmp/
Proxy OpenVPN app ProxyDroid
Tools Burp Suite Burp Suite

If running Kali in VMware/VirtualBox, switch from NAT to Bridged networking. This assigns the VM its own IP on the host’s network, making it directly accessible from your mobile device without port forwarding headaches.

Setting Up OpenVPN on Kali

To route the device’s traffic through Kali (acting as a MITM proxy), we need OpenVPN. This creates a tunnel for traffic redirection.

Download and tweak the installer script to fix Kali compatibility:

1
2
3
4
wget https://git.io/vpn -O openvpn-install.sh
sed -i "$(($(grep -ni "debian is too old" openvpn-install.sh | cut -d : -f 1)+1))d" ./openvpn-install.sh
chmod +x openvpn-install.sh
sudo ./openvpn-install.sh

During setup, answer the prompts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
This server is behind NAT. What is the public IPv4 address or hostname?
Public IPv4 address / hostname [120.140.41.143]: <your Kali IP>

Which protocol should OpenVPN use?
1) UDP (recommended)
2) TCP
Protocol [1]: 1

What port should OpenVPN listen on?
Port [1194]: 1194

Select a DNS server for the clients:
1) Default system resolvers
2) Google
3) 1.1.1.1
4) OpenDNS
5) Quad9
6) Gcore
7) AdGuard
8) Specify custom resolvers
DNS server [1]: 3

Enter a name for the first client:
Name [client]: ios-pentest

This generates a .ovpn file in /root/ (e.g., ios-pentest.ovpn).

Start the OpenVPN service:

1
sudo service openvpn start

Serve the .ovpn file via HTTP for download:

1
2
cd /root/
sudo python3 -m http.server 8000

On iOS, navigate to http://<kali-ip>:8000/ios-pentest.ovpn, download the file, import it into the OpenVPN app, and connect.

Configuring IPTables for Traffic Redirection

Redirect HTTP/HTTPS traffic (ports 80/443) to Burp’s listener (default 8080). The interface depends on your setup:

For OpenVPN (tun0)

1
2
3
4
5
6
# Redirect traffic to Burp
sudo iptables -t nat -A PREROUTING -i tun0 -p tcp --dport 80 -j REDIRECT --to-port 8080
sudo iptables -t nat -A PREROUTING -i tun0 -p tcp --dport 443 -j REDIRECT --to-port 8080

# NAT for outbound traffic
sudo iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE

For WiFi Hotspot (wlan0)

If you’re using a WiFi hotspot instead of VPN:

1
2
3
sudo iptables -t nat -A PREROUTING -i wlan0 -p tcp --dport 80 -j REDIRECT --to-port 8080
sudo iptables -t nat -A PREROUTING -i wlan0 -p tcp --dport 443 -j REDIRECT --to-port 8080
sudo iptables -t nat -A POSTROUTING -s 192.168.10.0/24 -o eth0 -j MASQUERADE

Enable IP forwarding:

1
sudo sysctl -w net.ipv4.ip_forward=1

In Burp, go to Proxy > Options and enable Invisible proxying on port 8080. This is crucial—invisible proxying handles non-proxy-aware traffic by using the Host header to determine the destination.

Installing Frida

On Jailbroken iOS

  1. Open Cydia and search for “Frida”
  2. Install the Frida server package
  3. Run frida-server on the device (via SSH or terminal app)

On Rooted Android

1
2
3
4
5
6
7
8
# On host, download frida-server for your architecture
wget https://github.com/frida/frida/releases/download/16.0.0/frida-server-16.0.0-android-arm64.xz
unxz frida-server-16.0.0-android-arm64.xz

# Push to device
adb push frida-server-16.0.0-android-arm64 /data/local/tmp/frida-server
adb shell chmod +x /data/local/tmp/frida-server
adb shell su -c /data/local/tmp/frida-server &

On Host (Kali)

1
pip install frida-tools

Verify connectivity:

1
frida-ps -U  # List processes on USB device

Disabling TLS Verification with Frida

Flutter uses BoringSSL internally, so we hook into its verification functions to bypass pinning. NVISO maintains an excellent script for this: disable-flutter-tls.js.

Download the script:

1
wget https://raw.githubusercontent.com/NVISOsecurity/disable-flutter-tls-verification/main/disable-flutter-tls.js

The script works by scanning for patterns in libFlutter.so (Android) or Flutter.framework/Flutter (iOS) and replacing ssl_verify_peer_cert to always return success:

1
2
3
4
5
6
function hook_ssl_verify_peer_cert(address) {
Interceptor.replace(address, new NativeCallback((pathPtr, flags) => {
console.log("[+] Certificate validation disabled");
return 0; // ssl_verify_ok
}, 'int', ['pointer', 'int']));
}

Run the script against your target app:

1
frida -Uf com.example.flutterapp -l disable-flutter-tls.js --no-pause

Replace com.example.flutterapp with your app’s bundle ID. Use frida-ps -Ua to find installed apps.

The -U flag targets a USB device, -f spawns the app, and --no-pause prevents Frida from pausing at startup.

Troubleshooting

If the script fails with pattern mismatch errors:

  1. Check Flutter version - The 2022 update targets ssl_verify_peer_cert instead of the old session_verify_cert_chain
  2. Architecture issues - ARM64 vs ARM32 patterns differ
  3. Report issues - Check NVISO’s GitHub for updates

For apps using Dio (a popular Flutter HTTP library with its own pinning), the script handles this by bypassing the certificate chain check.

Intercepting Traffic

With everything configured:

  1. Connect the iOS device to OpenVPN (or WiFi hotspot)
  2. Verify VPN connection in iOS settings
  3. Launch the target app with Frida attached:
1
frida -Uf com.example.flutterapp -l disable-flutter-tls.js --no-pause
  1. Trigger network requests in the app
  2. Watch traffic appear in Burp

You should now see all HTTP/HTTPS traffic from the Flutter app, decrypted and modifiable.

Android Notes

For Android, the process is nearly identical:

iOS Android
OpenVPN app ProxyDroid
Cydia/Frida Magisk + frida-server
Jailbreak Root

ProxyDroid routes all traffic through your proxy, similar to the VPN approach. The same Frida script works cross-platform.

Beyond Basics

Decrypting Binaries

For production apps, you may need to decrypt the binary first:

Debugging

If Dio pinning errors appear in logs even after Frida injection, the app may be using a custom implementation. Check for:

  • BadCertificateCallback handlers
  • Custom HttpClient configurations
  • Native code certificate checks

Documentation

In real pentests, document everything:

  • Device state (jailbreak/root method)
  • Tools and versions used
  • Traffic captures and findings
  • Legal/ethical considerations

Flutter evolves fast—NVISO’s repos are updated regularly to match new Flutter releases.

Summary

Step Tool Purpose
1 OpenVPN Route device traffic through Kali
2 IPTables Redirect 80/443 to Burp
3 Frida Disable TLS verification at runtime
4 Burp Intercept and modify traffic

This setup successfully caught encrypted API calls in my Flutter app, revealing endpoints, authentication flows, and data structures that would otherwise be invisible.

That’s Flutter traffic intercepted. On to more mobile apps!