Flutter

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks

Flutter est l’outil d’interface multiplateforme de Google qui permet aux développeurs d’écrire une unique base de code Dart que l’Engine (C/C++ natif) transforme en code machine spécifique à la plateforme pour Android & iOS. L’Engine inclut une Dart VM, BoringSSL, Skia, etc., et est livré en tant que bibliothèque partagée libflutter.so (Android) ou Flutter.framework (iOS). Tout le réseau réel (DNS, sockets, TLS) se passe dans cette bibliothèque, et non dans les couches Java/Kotlin Swift/Obj-C habituelles. Ce design cloisonné explique pourquoi les hooks Frida au niveau Java habituels échouent sur les apps Flutter.

Interception du trafic HTTPS dans Flutter

Ceci est un résumé de ce blog post.

Pourquoi l’interception HTTPS est délicate dans Flutter

  • La vérification SSL/TLS se situe deux couches plus bas dans BoringSSL, donc les contournements de SSL‑pinning au niveau Java ne l’atteignent pas.
  • BoringSSL utilise son propre magasin de CA à l’intérieur de libflutter.so ; importer votre Burp/ZAP CA dans le store système d’Android ne change rien.
  • Les symboles dans libflutter.so sont stripped & mangled, ce qui masque la fonction de vérification des certificats pour les outils dynamiques.

Fingerprint the exact Flutter stack

Connaître la version permet de recompiler ou de pattern-matcher les bons binaires.

StepCommand / FileOutcome
Get snapshot hashpython3 get_snapshot_hash.py libapp.soadb4292f3ec25…
Map hash → Engineenginehash list in reFlutterFlutter 3 · 7 · 12 + engine commit 1a65d409…
Pull dependent commitsDEPS file in that engine commitdart_revision → Dart v2 · 19 · 6
dart_boringssl_rev → BoringSSL 87f316d7…

Find get_snapshot_hash.py here.

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").
  1. Re-compile libflutter.so, drop it back into the APK/IPA, sign, install.
  2. 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.

// 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"); }
});

Je n’ai pas reçu le contenu du fichier. Veuillez coller ici le contenu de src/mobile-pentesting/android-app-pentesting/flutter.md et je le traduirai en français en respectant les règles indiquées.

frida -U -f com.example.app -l bypass.js

Conseils de portage

  • Pour arm64-v8a or armv7, récupérez les ~32 premiers octets de la fonction dans Ghidra, convertissez-les en une chaîne hex espacée, et remplacez sig.
  • Gardez one pattern per Flutter release, stockez-les dans une cheat-sheet pour réutilisation rapide.

Forcer le trafic via votre proxy

Flutter lui-même ignore les paramètres proxy de l’appareil. Options les plus simples :

  • Android Studio emulator: Settings ▶ Proxy → manual.
  • Physical device: evil Wi-Fi AP + DNS spoofing, or Magisk module editing /etc/hosts.

Quick Flutter TLS bypass workflow (Frida Codeshare + system CA)

Quand vous avez seulement besoin d’observer une API Flutter pinée, combiner un AVD rooté/accesssible en écriture, une system-trusted proxy CA, et un script Frida plug-and-play est souvent plus rapide que reverse-engineering libflutter.so:

  1. Install your proxy CA in the system store. Follow Install Burp Certificate to hash/rename Burp’s DER certificate and push it into /system/etc/security/cacerts/ (writable /system required).

  2. Drop a matching frida-server binary and run it as root so it can attach to the Flutter process:

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 &'"
  1. Installer les outils côté hôte et énumérer le package cible.
pip3 install frida-tools --break-system-packages
adb shell pm list packages -f | grep target
  1. Lancer l’application Flutter avec le hook Codeshare qui neutralise les BoringSSL pin checks.
frida -U -f com.example.target --codeshare TheDauntless/disable-flutter-tls-v1 --no-pause

Le script Codeshare surcharge le vérificateur TLS de Flutter de sorte que tous les certificats (y compris ceux générés dynamiquement par Burp) sont acceptés, contournant les public-key pin comparisons.

  1. Routez le trafic via votre proxy. Configurez la GUI proxy Wi‑Fi de l’émulateur ou imposez-la via adb shell settings put global http_proxy 10.0.2.2:8080 ; si le routage direct échoue, utilisez adb reverse tcp:8080 tcp:8080 ou un host-only VPN.

  2. Si l’app ignore les paramètres proxy de l’OS, redirigez les sockets avec un shim Frida. Des outils comme frida4burp hook la création de sockets dart:io/BoringSSL pour forcer les sessions TCP sortantes vers votre proxy, même en présence d’un HttpClient.findProxyFromEnvironment codé en dur ou de contournements Wi‑Fi. Définissez l’hôte/port du proxy dans le script et exécutez-le en parallèle du TLS bypass :

frida -U -f com.example.target --no-pause \
--codeshare TheDauntless/disable-flutter-tls-v1 \
-l frida4burp.js

Fonctionne sur iOS via un Frida gadget ou USB frida-server ; enchaîner la redirection de socket avec le contournement TLS restaure à la fois le routage et l’acceptation des certificats pour Burp/mitmproxy.

Une fois que la CA est approuvée au niveau OS et que Frida annule la logique de pinning de Flutter (plus socket redirection si nécessaire), Burp/mitmproxy regagne une visibilité complète pour API fuzzing (BOLA, token tampering, etc.) sans repacker l’APK.

Hook basé sur l’offset de la vérification BoringSSL (no signature scan)

Lorsque les scripts basés sur des patterns échouent entre architectures (par ex., x86_64 vs ARM), hookez directement le vérificateur de chaîne BoringSSL par adresse absolue dans libflutter.so. Workflow:

  • Extraire la bonne librairie ABI depuis l’APK: unzip -j app.apk "lib/*/libflutter.so" -d libs/ et choisir celle correspondant à l’appareil (p.ex., lib/x86_64/libflutter.so).
  • Analyser dans Ghidra/IDA et localiser le vérificateur :
  • Source: BoringSSL ssl_x509.cc fonction ssl_crypto_x509_session_verify_cert_chain (3 args, returns bool).
  • Dans les builds stripped, utilisez Search → For Strings → ssl_client → XREFs, puis ouvrez chaque FUN_... référencé et choisissez celui avec 3 args de type pointeur et un retour boolean.
  • Calculer l’offset runtime : prenez l’adresse de la fonction affichée par Ghidra et soustrayez la image base (p.ex., Ghidra affiche souvent 0x00100000 pour PIE Android ELFs). Exemple: 0x02184644 - 0x00100000 = 0x02084644.
  • Hooker au runtime en utilisant base + offset et forcer le succès :
// 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);

Remarques

  • Les Signature scans peuvent réussir sur ARM mais échouer sur x86_64 parce que la disposition des opcodes change ; cette méthode d’offset est agnostique vis‑à‑vis de l’architecture tant que le RVA est recalculé.
  • Ce contournement fait que BoringSSL accepte n’importe quelle chaîne, permettant un HTTPS MITM indépendamment des pins/CA trust à l’intérieur de Flutter.
  • Si vous forcez le routage du trafic pendant le débogage pour confirmer le blocage TLS, par ex. :
iptables -t nat -A OUTPUT -p tcp -j DNAT --to-destination <Burp_IP>:<Burp_Port>

…vous aurez toujours besoin du hook ci‑dessus, puisque la vérification a lieu dans libflutter.so, et non dans le magasin de confiance système d’Android.

Références

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks