mirror of
				https://github.com/HackTricks-wiki/hacktricks.git
				synced 2025-10-10 18:36:50 +00:00 
			
		
		
		
	flutter
This commit is contained in:
		
							parent
							
								
									4a6135f8a3
								
							
						
					
					
						commit
						25af2a51f5
					
				@ -316,6 +316,7 @@
 | 
			
		||||
  - [Drozer Tutorial](mobile-pentesting/android-app-pentesting/drozer-tutorial/README.md)
 | 
			
		||||
    - [Exploiting Content Providers](mobile-pentesting/android-app-pentesting/drozer-tutorial/exploiting-content-providers.md)
 | 
			
		||||
  - [Exploiting a debuggeable application](mobile-pentesting/android-app-pentesting/exploiting-a-debuggeable-applciation.md)
 | 
			
		||||
  - [Flutter](mobile-pentesting/android-app-pentesting/flutter.md)
 | 
			
		||||
  - [Frida Tutorial](mobile-pentesting/android-app-pentesting/frida-tutorial/README.md)
 | 
			
		||||
    - [Frida Tutorial 1](mobile-pentesting/android-app-pentesting/frida-tutorial/frida-tutorial-1.md)
 | 
			
		||||
    - [Frida Tutorial 2](mobile-pentesting/android-app-pentesting/frida-tutorial/frida-tutorial-2.md)
 | 
			
		||||
 | 
			
		||||
@ -419,6 +419,8 @@ To inspect HTTP traffic, it's necessary to **install the proxy tool's certificat
 | 
			
		||||
 | 
			
		||||
Applications targeting **API Level 24 and above** require modifications to the Network Security Config to accept the proxy's CA certificate. This step is critical for inspecting encrypted traffic. For instructions on modifying the Network Security Config, [**refer to this tutorial**](make-apk-accept-ca-certificate.md).
 | 
			
		||||
 | 
			
		||||
If **Flutter** is being used you need to to follow the instructions in [**this page**](flutter.md). This is becasue, just adding the certificate into the store won't work as Flutter has its own list of valid CAs.
 | 
			
		||||
 | 
			
		||||
#### Bypassing SSL Pinning
 | 
			
		||||
 | 
			
		||||
When SSL Pinning is implemented, bypassing it becomes necessary to inspect HTTPS traffic. Various methods are available for this purpose:
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										80
									
								
								src/mobile-pentesting/android-app-pentesting/flutter.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								src/mobile-pentesting/android-app-pentesting/flutter.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,80 @@
 | 
			
		||||
# Flutter
 | 
			
		||||
 | 
			
		||||
{{#include ../../banners/hacktricks-training.md}}
 | 
			
		||||
 | 
			
		||||
# Flutter
 | 
			
		||||
Flutter is **Google’s cross-platform UI toolkit** that lets developers write a single Dart code-base which the **Engine** (native C/C++) turns into platform-specific machine code for Android & iOS.  
 | 
			
		||||
The Engine bundles a **Dart VM**, **BoringSSL**, Skia, etc., and ships as the shared library **libflutter.so** (Android) or **Flutter.framework** (iOS). All actual networking (DNS, sockets, TLS) happens **inside this library**, *not* in the usual Java/Kotlin Swift/Obj-C layers. That siloed design is why the usual Java-level Frida hooks fail on Flutter apps.
 | 
			
		||||
 | 
			
		||||
## Intercepting HTTPS traffic in Flutter
 | 
			
		||||
 | 
			
		||||
Thsi is a summary of this [blog post](https://sensepost.com/blog/2025/intercepting-https-communication-in-flutter-going-full-hardcore-mode-with-frida/).
 | 
			
		||||
 | 
			
		||||
### Why HTTPS interception is tricky in Flutter  
 | 
			
		||||
* **SSL/TLS verification lives two layers down** in BoringSSL, so Java SSL‐pinning bypasses don’t touch it.  
 | 
			
		||||
* **BoringSSL uses its *own* CA store** inside libflutter.so; importing your Burp/ZAP CA into Android’s system store changes nothing.  
 | 
			
		||||
* Symbols in libflutter.so are **stripped & mangled**, hiding the certificate-verification function from dynamic tools.
 | 
			
		||||
 | 
			
		||||
### Fingerprint the exact Flutter stack  
 | 
			
		||||
Knowing the version lets you re-build or pattern-match the right binaries.
 | 
			
		||||
 | 
			
		||||
Step | Command / File | Outcome
 | 
			
		||||
----|----|----
 | 
			
		||||
Get snapshot hash | ```bash\npython3 get_snapshot_hash.py libapp.so\n``` | `adb4292f3ec25…`
 | 
			
		||||
Map hash → Engine | **enginehash** list in reFlutter | Flutter 3 · 7 · 12 + engine commit `1a65d409…`
 | 
			
		||||
Pull dependent commits | DEPS file in that engine commit | • `dart_revision` → Dart v2 · 19 · 6<br>• `dart_boringssl_rev` → BoringSSL `87f316d7…`
 | 
			
		||||
 | 
			
		||||
Find [get_snapshot_hash.py here](https://github.com/Impact-I/reFlutter/blob/main/scripts/get_snapshot_hash.py).
 | 
			
		||||
 | 
			
		||||
### Target: `ssl_crypto_x509_session_verify_cert_chain()`  
 | 
			
		||||
* Located in **`ssl_x509.cc`** inside BoringSSL.  
 | 
			
		||||
* **Returns `bool`** – a single `true` is enough to bypass the whole certificate chain check.  
 | 
			
		||||
* Same function exists on every CPU arch; only the opcodes differ.
 | 
			
		||||
 | 
			
		||||
### Option A – Binary patching with **reFlutter**  
 | 
			
		||||
1. **Clone** the exact Engine & Dart sources for the app’s Flutter version.
 | 
			
		||||
2. **Regex-patch** two hotspots:
 | 
			
		||||
   * In `ssl_x509.cc`, force `return 1;`  
 | 
			
		||||
   * (Optional) In `socket_android.cc`, hard-code a proxy (`"10.0.2.2:8080"`).
 | 
			
		||||
3. **Re-compile** libflutter.so, drop it back into the APK/IPA, sign, install.
 | 
			
		||||
4. **Pre-patched builds** for common versions are shipped in the reFlutter GitHub releases to save hours of build time.
 | 
			
		||||
 | 
			
		||||
### Option B – Live hooking with **Frida** (the “hard-core” path)  
 | 
			
		||||
Because the symbol is stripped, you pattern-scan the loaded module for its first bytes, then change the return value on the fly.
 | 
			
		||||
 | 
			
		||||
```javascript
 | 
			
		||||
// attach & locate libflutter.so
 | 
			
		||||
var flutter = Process.getModuleByName("libflutter.so");
 | 
			
		||||
 | 
			
		||||
// x86-64 pattern of the first 16 bytes of ssl_crypto_x509_session_verify_cert_chain
 | 
			
		||||
var sig = "55 41 57 41 56 41 55 41 54 53 48 83 EC 38 C6 02";
 | 
			
		||||
 | 
			
		||||
Memory.scan(flutter.base, flutter.size, sig, {
 | 
			
		||||
  onMatch: function (addr) {
 | 
			
		||||
    console.log("[+] found verifier at " + addr);
 | 
			
		||||
    Interceptor.attach(addr, {
 | 
			
		||||
      onLeave: function (retval) { retval.replace(0x1); }  // always 'true'
 | 
			
		||||
    });
 | 
			
		||||
  },
 | 
			
		||||
  onComplete: function () { console.log("scan done"); }
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Run it:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
frida -U -f com.example.app -l bypass.js
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
*Porting tips*  
 | 
			
		||||
* For **arm64-v8a** or **armv7**, grab the first ~32 bytes of the function from Ghidra, convert to a space-separated hex string, and replace `sig`.  
 | 
			
		||||
* Keep **one pattern per Flutter release**, store them in a cheat-sheet for fast reuse.
 | 
			
		||||
 | 
			
		||||
### Forcing traffic through your proxy  
 | 
			
		||||
Flutter itself **ignores device proxy settings**. Easiest options:  
 | 
			
		||||
* **Android Studio emulator:** Settings ▶ Proxy → manual.  
 | 
			
		||||
* **Physical device:** evil Wi-Fi AP + DNS spoofing, or Magisk module editing `/etc/hosts`.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## References
 | 
			
		||||
- [https://sensepost.com/blog/2025/intercepting-https-communication-in-flutter-going-full-hardcore-mode-with-frida/](https://sensepost.com/blog/2025/intercepting-https-communication-in-flutter-going-full-hardcore-mode-with-frida/)
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user