diff --git a/src/mobile-pentesting/android-app-pentesting/flutter.md b/src/mobile-pentesting/android-app-pentesting/flutter.md
index 1657f597b67..21bcbd3c3d4 100644
--- a/src/mobile-pentesting/android-app-pentesting/flutter.md
+++ b/src/mobile-pentesting/android-app-pentesting/flutter.md
@@ -15,15 +15,17 @@ This is a summary of this [blog post](https://sensepost.com/blog/2025/intercepti
* 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.
+When **reFlutter** cannot auto-patch the APK (unsupported engine hash, debug engine, missing `libapp.so`, newer release), avoid guessing offsets and derive the exact Flutter/BoringSSL pair first.
Step | Command / File | Outcome
----|----|----
-Get snapshot hash | `python3 get_snapshot_hash.py libapp.so` | `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
• `dart_boringssl_rev` → BoringSSL `87f316d7…`
+Check packaging | `unzip -l app.apk | grep -E "libapp.so|libflutter.so"` | Confirm the target ABI and that both Flutter libraries are really present
+Get snapshot hash | `python3 get_snapshot_hash.py libapp.so` | Engine snapshot hash (**use `libapp.so`, not `libflutter.so`**)
+Map hash → Engine | `curl -s https://raw.githubusercontent.com/Impact-I/reFlutter/refs/heads/main/enginehash.csv | grep ` | Flutter version + engine commit
+If hash is missing | `python3 gen_enginehash.py` in reFlutter `scripts/` | Fresh local hash table for newer/debug builds
+Pull dependent commits | Flutter release `DEPS` file | Exact `dart_boringssl_rev` / BoringSSL commit
-Find [get_snapshot_hash.py here](https://github.com/Impact-I/reFlutter/blob/main/scripts/get_snapshot_hash.py).
+Find [get_snapshot_hash.py here](https://github.com/Impact-I/reFlutter/blob/main/scripts/get_snapshot_hash.py). A bogus hash such as repeated `4` values usually means the script was run against `libflutter.so` instead of `libapp.so`.
### Target: `ssl_crypto_x509_session_verify_cert_chain()`
* Located in **`ssl_x509.cc`** inside BoringSSL.
@@ -116,32 +118,28 @@ Works on iOS via a Frida gadget or USB frida-server; chaining the socket redirec
Once the CA is trusted at the OS layer and Frida quashes Flutter's pinning logic (plus socket redirection if needed), Burp/mitmproxy regains full visibility for API fuzzing (BOLA, token tampering, etc.) without repacking the APK.
### Offset-based hook of BoringSSL verification (no signature scan)
-When pattern-based scripts fail across architectures (e.g., x86_64 vs ARM), directly hook the BoringSSL chain verifier by absolute address within libflutter.so. Workflow:
+When pattern-based scripts fail across architectures (e.g., x86_64 vs ARM), directly hook the BoringSSL chain verifier by absolute address within `libflutter.so`. A reliable workflow is:
-- Extract the right-ABI library from the APK: `unzip -j app.apk "lib/*/libflutter.so" -d libs/` and pick the one matching the device (e.g., `lib/x86_64/libflutter.so`).
-- Analyze in Ghidra/IDA and locate the verifier:
- - Source: BoringSSL ssl_x509.cc function `ssl_crypto_x509_session_verify_cert_chain` (3 args, returns bool).
- - In stripped builds, use **Search → For Strings → `ssl_client` → XREFs**, then open each referenced `FUN_...` and pick the one with 3 pointer-like args and a boolean return.
-- Compute the runtime offset: take the function address shown by Ghidra and subtract the image base (e.g., Ghidra often shows `0x00100000` for PIE Android ELFs). Example: `0x02184644 - 0x00100000 = 0x02084644`.
+- Extract the right-ABI library from the APK: `unzip -j app.apk "lib/*/libflutter.so" -d libs/` and pick the one matching the device.
+- Resolve the exact BoringSSL revision from the matching Flutter `DEPS`, fetch `ssl/ssl_x509.cc`, and identify `ssl_crypto_x509_session_verify_cert_chain`.
+- Use the line number of the `OPENSSL_PUT_ERROR(...)` inside that exact source revision as a **scalar search anchor** in Ghidra. The line number is compiled into the error path, so searching for that constant (for example `239`) is often faster than guessing patterns in a stripped binary.
+- If the scalar search is noisy, fall back to **Search → For Strings → `ssl_client` → XREFs** and keep only candidates that decompile into a 3-argument function matching `SSL_SESSION *`, `SSL_HANDSHAKE *`, `uint8_t *` semantics.
+- Compute the runtime offset: `function_address - image_base`. Example: `0x00840950 - 0x00100000 = 0x00740950`.
- Hook at runtime by base + offset and force success:
```javascript
-// frida -U -f com.target.app -l bypass.js --no-pause
-const base = Module.findBaseAddress('libflutter.so');
-// Example offset from analysis. Recompute per build/arch.
-const off = ptr('0x02084644');
-const addr = base.add(off);
-
-// ssl_crypto_x509_session_verify_cert_chain: 3 args, bool return
-Interceptor.replace(addr, new NativeCallback(function (a, b, c) {
- return 1; // true
-}, 'int', ['pointer', 'pointer', 'pointer']));
-
-console.log('[+] Hooked BoringSSL verify_cert_chain at', addr);
+const module = Process.findModuleByName('libflutter.so');
+const addr = module.base.add(ptr('0x00740950')); // recompute per build/arch
+Interceptor.attach(addr, {
+ onLeave(retval) {
+ retval.replace(0x1);
+ }
+});
```
Notes
- Signature scans can succeed on ARM but miss on x86_64 because the opcode layout changes; this offset method is architecture-agnostic as long as you recalc the RVA.
+- If `libflutter.so` is not loaded yet, install the hook after the Android linker resolves it (commonly by watching `linker64` symbols such as `do_dlopen` / `call_constructor` and calling your attach routine once `libflutter.so` appears).
- This bypass causes BoringSSL to accept any chain, enabling HTTPS MITM regardless of pins/CA trust inside Flutter.
- If you force-route traffic during debugging to confirm TLS blocking, e.g.:
@@ -149,7 +147,7 @@ Notes
iptables -t nat -A OUTPUT -p tcp -j DNAT --to-destination :
```
-…you will still need the hook above, since verification happens inside libflutter.so, not Android’s system trust store.
+…you will still need the hook above, since verification happens inside `libflutter.so`, not Android’s system trust store.
## Reversing the Dart payload (`libapp.so`)
@@ -228,6 +226,8 @@ Those artifacts can turn a “hard to read” AOT binary back into a much friend
## 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/)
+- [Bypassing Flutter TLS/SSL Verification When reFlutter Fails](https://petruknisme.medium.com/bypassing-flutter-tls-ssl-verification-when-reflutter-fails-a4c41ff758a3)
+- [Impact-I/reFlutter](https://github.com/Impact-I/reFlutter)
- [Flutter SSL Bypass: How to Intercept HTTPS Traffic When all other Frida Scripts Fail (vercel)](https://m4kr0.vercel.app/posts/flutter-ssl-bypass-how-to-intercept-https-traffic-when-all-other-frida-scripts-fail/)
- [Flutter SSL Bypass: How to Intercept HTTPS Traffic When all other Frida Scripts Fail (medium)](https://m4kr0x.medium.com/flutter-tls-bypass-how-to-intercept-https-traffic-when-all-other-frida-scripts-fail-bd3d04489088)
- [PoC Frida hook for Flutter SSL bypass](https://github.com/m4kr0x/flutter_ssl_bypass)