Flutter

Tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE) 学习和实践 Azure 黑客技术:HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks

Flutter 是 Google 的跨平台 UI 工具包,允许开发者编写单一的 Dart 代码库,由 Engine(本地 C/C++)将其转换为针对 Android 和 iOS 的平台特定机器码。
Engine 包含 Dart VM, BoringSSL, Skia 等,并以共享库 libflutter.so(Android)或 Flutter.framework(iOS)的形式发布。所有实际的网络(DNS、sockets、TLS)都发生在 该库内部而非 常见的 Java/Kotlin 或 Swift/Obj‑C 层。正是这种隔离设计导致常用的 Java 级别 Frida hooks 在 Flutter 应用上失效。

Intercepting HTTPS traffic in Flutter

这是这篇 blog post 的摘要。

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.

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…

查找 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. 克隆应用所用 Flutter 版本的精确 Engine 与 Dart 源码。
  2. 对两个热点进行 Regex-patch
  • In ssl_x509.cc, force return 1;
  • (Optional) In socket_android.cc, hard-code a proxy ("10.0.2.2:8080").
  1. 重新编译 libflutter.so,将其替换回 APK/IPA,签名并安装。
  2. reFlutter 的 GitHub releases 中提供了常见版本的 pre-patched builds,可节省数小时的构建时间。

### Option B – Live hooking with Frida (the “hard-core” path) 由于符号被剥离,你需要对已加载模块进行模式扫描以查找其起始字节,然后在运行时修改返回值。

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

我没有收到 src/mobile-pentesting/android-app-pentesting/flutter.md 的内容。请粘贴该文件的 Markdown 文本(或上传),我会按要求保留所有 markdown/html 标签、链接、路径和代码不变,只把其余的英语翻译为中文。

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

移植提示

  • 对于 arm64-v8aarmv7,从 Ghidra 获取函数前约 32 字节,将其转换为以空格分隔的 hex 字符串,然后替换 sig
  • 为每个 Flutter 发布保留 一个模式,将它们存储在速查表中以便快速重用。

强制将流量通过你的代理

Flutter 本身 忽略设备代理设置。最简单的选项:

  • Android Studio emulator: 设置 ▶ 代理 → 手动。
  • Physical device: 恶意 Wi-Fi AP + DNS spoofing,或使用 Magisk module 编辑 /etc/hosts

快速 Flutter TLS 绕过工作流程 (Frida Codeshare + system CA)

如果你只需要观察被 pin 的 Flutter API,将已 root/可写的 AVD、受系统信任的代理 CA 和一个可直接使用的 Frida 脚本结合起来,通常比逆向工程 libflutter.so 更快:

  1. 将你的代理 CA 安装到系统证书存储。 按照 Install Burp Certificate 对 Burp 的 DER 证书进行哈希/重命名,并推送到 /system/etc/security/cacerts/(需要可写的 /system)。

  2. 放置匹配的 frida-server 二进制并以 root 运行,以便它可以附加到 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. 安装主机端工具并枚举目标包。
pip3 install frida-tools --break-system-packages
adb shell pm list packages -f | grep target
  1. 以带有 Codeshare 钩子的方式启动 Flutter 应用,该钩子会禁用 BoringSSL 的 pin 校验。
frida -U -f com.example.target --codeshare TheDauntless/disable-flutter-tls-v1 --no-pause

The Codeshare 脚本覆盖了 Flutter 的 TLS 验证器,因此接受所有证书(包括 Burp 动态生成的证书),从而绕过 public-key pin 比较。

  1. 通过你的代理路由流量。 通过模拟器的 Wi‑Fi 代理 GUI 配置,或用 adb shell settings put global http_proxy 10.0.2.2:8080 强制设置;如果直接路由失败,则退回使用 adb reverse tcp:8080 tcp:8080 或仅主机 VPN。

一旦在操作系统层信任 CA 并且 Frida 使 Flutter 的 pinning 逻辑失效,Burp/mitmproxy 就能恢复对 API 的完全可视性,用于 API fuzzing(BOLA、token tampering 等),且无需重打包 APK。

基于偏移的 BoringSSL 验证 hook(无需签名扫描)

当基于模式的脚本在不同架构(例如 x86_64 与 ARM)间失效时,可直接在 libflutter.so 内通过绝对地址 hook BoringSSL 的链式验证器。工作流程:

  • 从 APK 中提取对应 ABI 的库:unzip -j app.apk "lib/*/libflutter.so" -d libs/,并选择与设备匹配的那个(例如 lib/x86_64/libflutter.so)。
  • 在 Ghidra/IDA 中分析并定位验证器:
  • 来源:BoringSSL 的 ssl_x509.cc 中函数 ssl_crypto_x509_session_verify_cert_chain(3 个参数,返回 bool)。
  • 在被剥除符号(stripped)的构建中,使用 Search → For Strings → ssl_client → XREFs,然后打开每个被引用的 FUN_...,选择具有 3 个指针式参数并返回 boolean 的那个。
  • 计算运行时偏移:取 Ghidra 显示的函数地址并减去镜像基址(例如,Ghidra 对 PIE Android ELF 通常显示为 0x00100000)。示例:0x02184644 - 0x00100000 = 0x02084644
  • 在运行时按 base + offset 进行 hook 并强制成功:
// 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);
  • 签名扫描在 ARM 上可能成功但在 x86_64 上失效,因为 opcode 布局发生变化;只要你重新计算 RVA,这种偏移方法与架构无关。
  • 此绕过使 BoringSSL 接受任何链,从而在 Flutter 内部无视 pins/CA trust 启用 HTTPS MITM。
  • 如果你在调试时强制路由流量以确认 TLS 阻断,例如:
iptables -t nat -A OUTPUT -p tcp -j DNAT --to-destination <Burp_IP>:<Burp_Port>

…你仍然需要上面的 hook,因为验证发生在 libflutter.so 内,而不是 Android 的系统信任存储。

参考资料

Tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE) 学习和实践 Azure 黑客技术:HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks