Flutter

Tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks

Flutter é a toolkit de UI multiplataforma do Google que permite aos desenvolvedores escreverem uma única base de código Dart que o Engine (C/C++ nativo) converte em código de máquina específico da plataforma para Android e iOS. O Engine inclui uma Dart VM, BoringSSL, Skia, etc., e é distribuído como a biblioteca compartilhada libflutter.so (Android) ou Flutter.framework (iOS). Toda a rede real (DNS, sockets, TLS) acontece dentro dessa biblioteca, não nas camadas Java/Kotlin ou Swift/Obj-C habituais. Esse design compartimentado é o motivo pelo qual os hooks de Frida ao nível Java normalmente falham em apps Flutter.

Intercepting HTTPS traffic in Flutter

Isto é um resumo deste blog post.

Why HTTPS interception is tricky in Flutter

  • A verificação SSL/TLS está duas camadas abaixo no BoringSSL, então bypasses de SSL-pinning em Java não o afetam.
  • O BoringSSL usa seu próprio repositório de CA dentro de libflutter.so; importar a CA do seu Burp/ZAP no repositório do sistema Android não altera nada.
  • Símbolos em libflutter.so são stripped & mangled, escondendo a função de verificação de certificados das ferramentas dinâmicas.

Identificar a stack Flutter exata

Saber a versão permite recompilar ou fazer pattern-match nos binários corretos.

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.

Alvo: ssl_crypto_x509_session_verify_cert_chain()

  • Localizada em ssl_x509.cc dentro do BoringSSL.
  • Retorna bool – um único true é suficiente para burlar toda a verificação da cadeia de certificados.
  • A mesma função existe em todas as arquiteturas de CPU; apenas os opcodes diferem.

Opção A – Patch binário com reFlutter

  1. Clone os códigos-fonte exatos do Engine e do Dart para a versão Flutter do app.
  2. Regex-patch dois pontos críticos:
  • Em ssl_x509.cc, forçar return 1;
  • (Opcional) Em socket_android.cc, codificar estaticamente um proxy ("10.0.2.2:8080").
  1. Recompilar libflutter.so, colocá-la novamente no APK/IPA, assinar, instalar.
  2. Builds pré-patchados para versões comuns são disponibilizados nos releases do reFlutter no GitHub para economizar horas de tempo de build.

Opção B – Hooking em tempo real com Frida (o caminho “hard-core”)

Como o símbolo está stripped, você pattern-scan o módulo carregado pelos seus primeiros bytes e então altera o valor de retorno em tempo de execução.

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

Preciso do conteúdo do arquivo src/mobile-pentesting/android-app-pentesting/flutter.md para traduzir. Por favor, cole aqui o texto (ou confirme como quer fornecer o arquivo). Vou retornar a tradução para português mantendo exatamente a mesma sintaxe Markdown/HTML e sem traduzir tags, links ou caminhos.

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

Dicas de portabilidade

  • Para arm64-v8a or armv7, pegue os primeiros ~32 bytes da função no Ghidra, converta para uma string hex separada por espaços, e substitua sig.
  • Mantenha um padrão por versão do Flutter, armazene-os em um cheat-sheet para reutilização rápida.

Forçando o tráfego através do seu proxy

Flutter itself ignora as configurações de proxy do dispositivo. Opções mais fáceis:

  • Android Studio emulator: Configurações ▶ Proxy → manual.
  • Physical device: AP Wi‑Fi malicioso + DNS spoofing, ou módulo Magisk editando /etc/hosts.

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

Quando você só precisa observar uma API Flutter pinada, combinar um AVD rootado/gravável, uma proxy CA confiável pelo sistema, e um script Frida drop-in costuma ser mais rápido do que engenharia reversa do libflutter.so:

  1. Install your proxy CA in the system store. Siga Install Burp Certificate para hash/renomear o certificado DER do Burp e colocá-lo em /system/etc/security/cacerts/ (requer /system gravável).

  2. Drop a matching frida-server binary and run it as root para que ele possa anexar-se ao processo Flutter:

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. Instale as ferramentas do lado do host e enumere o pacote alvo.
pip3 install frida-tools --break-system-packages
adb shell pm list packages -f | grep target
  1. Inicie o aplicativo Flutter com o Codeshare hook que neutraliza as verificações de pin do BoringSSL.
frida -U -f com.example.target --codeshare TheDauntless/disable-flutter-tls-v1 --no-pause

O script Codeshare sobrescreve o Flutter TLS verifier de modo que todo certificado (incluindo os gerados dinamicamente pelo Burp) é aceito, contornando verificações de public-key pin.

  1. Route traffic through your proxy. Configure o proxy Wi‑Fi do emulador pela GUI ou force via adb shell settings put global http_proxy 10.0.2.2:8080; se o roteamento direto falhar, recorra a adb reverse tcp:8080 tcp:8080 ou a uma VPN host-only.

  2. If the app ignores OS proxy settings, redirect sockets with a Frida shim. Ferramentas como frida4burp hook dart:io/BoringSSL socket creation para forçar sessões TCP de saída pelo seu proxy, mesmo com HttpClient.findProxyFromEnvironment hardcoded ou Wi‑Fi bypasses. Defina o host/porta do proxy no script e execute-o junto com o TLS bypass:

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

Funciona no iOS via um Frida gadget ou USB frida-server; encadear o socket redirect com o TLS bypass restaura tanto o roteamento quanto a aceitação de certificados para Burp/mitmproxy.

Uma vez que a CA é confiada no nível do OS e o Frida quashes Flutter’s pinning logic (mais socket redirection, se necessário), o Burp/mitmproxy recupera visibilidade total para API fuzzing (BOLA, token tampering, etc.) sem repacking the APK.

Offset-based hook of BoringSSL verification (no signature scan)

Quando scripts baseados em pattern falham entre arquiteturas (e.g., x86_64 vs ARM), faça o hook diretamente no verificador de chain do BoringSSL por endereço absoluto dentro de libflutter.so. Workflow:

  • 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.
  • Hook at runtime by base + offset and force success:
// 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);

Notas

  • Varreduras de assinatura podem ter sucesso em ARM, mas falhar em x86_64 porque o opcode layout muda; este método de offset é agnóstico à arquitetura desde que você recalcule o RVA.
  • Esse bypass faz com que o BoringSSL aceite qualquer cadeia, permitindo HTTPS MITM independentemente dos pins/confiança da CA dentro do Flutter.
  • Se você forçar o roteamento do tráfego durante a depuração para confirmar o bloqueio de TLS, por exemplo:
iptables -t nat -A OUTPUT -p tcp -j DNAT --to-destination <Burp_IP>:<Burp_Port>

…você ainda vai precisar do hook acima, pois a verificação ocorre dentro de libflutter.so, e não no Android’s system trust store.

Referências

Tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks