Skip to main content

flutter

Flutter app reversing and SSL pinning bypass — reFlutter Dart-AOT patching, BoringSSL bypass, libapp.so static analysis in Ghidra/radare2, Dart snapshot dump, and iOS Flutter.framework notes.

설치로 이동

소스 정보

저장소
BitterSecurity/Decepticon
최근 소스 활동
2026년 5월 30일 11:41
감지된 SKILL.md 언어
영어
스타
5,565
포크
1,053

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
flutter
description
Flutter app reversing and SSL pinning bypass — reFlutter Dart-AOT patching, BoringSSL bypass, libapp.so static analysis in Ghidra/radare2, Dart snapshot dump, and iOS Flutter.framework notes.
allowed-tools
Bash Read Write
metadata
{"subdomain":"mobile","when_to_use":"flutter dart aot libapp.so libflutter reflutter boringssl ssl pinning bypass snapshot dump cross-platform jadx-useless tunproxy uber-apk-signer","tags":"flutter, dart, reflutter, ssl-pinning, reverse-engineering, libapp, boringssl","mitre_attack":"T1635, T1521.003, T1406"}
# Flutter App Reversing Playbook > Flutter compiles Dart AOT to a native shared library (`libapp.so` / > `Flutter.framework`). `jadx` / `apktool` show only the thin Java/ObjC > shell and are useless for app logic. Use this playbook when static > Android or iOS skills yield nothing but an empty Java wrapper. ## Prerequisites - APK / IPA obtained (see `mobile/android/SKILL.md` Path 1 for APK pull). - Tools: `reflutter` (`pip install reflutter`), `uber-apk-signer` (download JAR from GitHub releases), `adb` + device or emulator, `jadx` (identification only), Burp Suite proxy. - For iOS: `reFlutter` also patches `Flutter.framework`; use `objection patchipa` or direct framework patching workflow. ## Step 1: Identify a Flutter App ```bash # Unzip APK, look for the telltale Flutter artifacts unzip -o base.apk -d /tmp/apk-out/ ls /tmp/apk-out/lib/arm64-v8a/ # Flutter app: libflutter.so libapp.so # Present: flutter_assets/ kernel_blob.bin (optional, debug) # jadx shows only the Java bootstrap: jadx -d /tmp/apk-java /tmp/base.apk # Expect: ~1 class, LoadLibrary("app"), nothing useful # Confirm with strings: strings /tmp/apk-out/lib/arm64-v8a/libflutter.so | grep -i "flutter" strings /tmp/apk-out/lib/arm64-v8a/libapp.so | grep -E "https?://" ``` Key tell: `libapp.so` size > 5 MB with no dex business logic; the `kernel_blob.bin` in `flutter_assets/` indicates a debug/JIT build (rare in production; can extract Dart directly with `dart_vm`). ## Step 2: reFlutter Workflow — Patch BoringSSL Pinning reFlutter patches the `ssl_crypto_x509_session_verify_cert_chain` function in `libflutter.so` to always return `true`, bypassing all BoringSSL-based certificate validation (covers both custom pinning and system trust). ### Flutter <= 3.23.x (hardcoded proxy IP) ```bash # Patch: supply your Burp listener IP reflutter base.apk # reFlutter prompts for IP — enter your Burp machine IP (e.g. 192.168.1.100) # Output: release.RE.apk (patched, unsigned) # Sign with uber-apk-signer java -jar uber-apk-signer.jar --allowResign -a release.RE.apk -o /tmp/ # Install on device adb install /tmp/release.RE-aligned-signed.apk # Configure Burp proxy listener on 0.0.0.0:8083 (reFlutter default port is 8083) # Launch app — HTTPS traffic appears in Burp ``` ### Flutter >= 3.24.0 (no hardcoded proxy IP — breaking change) Starting Flutter 3.24.0 (August 2024), the hardcoded proxy IP was removed from the patched BoringSSL stub. Traffic is no longer redirected to a fixed IP. Two options: **Option A: Device-level proxy** ```bash # Patch as above (no IP prompt in 3.24+) reflutter base.apk java -jar uber-apk-signer.jar --allowResign -a release.RE.apk -o /tmp/ # Install and configure device proxy manually: # Android Settings → Wi-Fi → [SSID] → Proxy → Manual # Host: <Burp-IP> Port: 8080 # Or use TunProxy for non-proxy-aware processes: adb install TunProxy.apk # Launch TunProxy, set server to <Burp-IP>:8080, toggle VPN # Then launch patched app ``` **Option B: Per-app proxy injection via Frida** ```bash # Run patched APK + Frida script to redirect all socket connections frida -U -f com.target.app -l proxy-redirect.js --no-pause # proxy-redirect.js: hooks connect() syscall, redirects to Burp IP ``` ### iOS Flutter.framework patching ```bash # Extract IPA unzip target.ipa -d /tmp/ipa-out/ # Locate Flutter.framework ls /tmp/ipa-out/Payload/TargetApp.app/Frameworks/Flutter.framework/Flutter # reFlutter iOS (experimental — check reFlutter README for current support) reflutter target.ipa # Sign with codesign + adhoc or with valid mobileprovision codesign --force --sign - /tmp/ipa-out/Payload/TargetApp.app/Frameworks/Flutter.framework/Flutter # Repack + install with ios-deploy or Sideloadly ``` ## Step 3: BoringSSL Bypass Mechanism reFlutter patches `ssl_crypto_x509_session_verify_cert_chain` to unconditionally return `1` (success). This function is the central chain verification entry point in BoringSSL (the TLS library embedded in `libflutter.so`) — no root CA, no pinning config, and no custom validator override this patch because the chain evaluation never runs. **Manual binary patch (if reFlutter fails on a specific version):** ```bash # Find the function offset in libflutter.so r2 -A /tmp/apk-out/lib/arm64-v8a/libflutter.so # In r2: afl~ssl_crypto_x509 # Or: /c ret in data section near ssl_crypto_x509_session_verify_cert_chain # Patch: overwrite function prologue with MOV W0, #1 / RET (AArch64) # MOV W0, 1 = 20 00 80 52 # RET = C0 03 5F D6 python3 -c " import struct with open('libflutter.so', 'r+b') as f: f.seek(<offset>) f.write(b'\x20\x00\x80\x52\xC0\x03\x5F\xD6') " ``` ## Step 4: Static Native RE — libapp.so in Ghidra / radare2 ```bash # Load libapp.so in radare2 for quick triage r2 -A /tmp/apk-out/lib/arm64-v8a/libapp.so # Strings — API endpoints, Firebase config, hardcoded keys r2 -qc 'iz~https' /tmp/apk-out/lib/arm64-v8a/libapp.so r2 -qc 'iz~firebase' /tmp/apk-out/lib/arm64-v8a/libapp.so r2 -qc 'iz~AIza' /tmp/apk-out/lib/arm64-v8a/libapp.so # Firebase API key prefix # Imports — identify Flutter plugins with native bridges r2 -qc 'ii' /tmp/apk-out/lib/arm64-v8a/libapp.so | head -40 ``` In Ghidra (MCP-connected via `ghidra` server): ``` # connect_instance first, then batch analyze # Load libapp.so → auto-analyze # Search defined strings for URL patterns, credential patterns # Dart AOT functions are not named — use string xrefs to find handlers ``` Dart AOT functions lack names (no symbol table in release builds) but the Dart snapshot contains type metadata that `Il2CppDumper`-style tools and `dart_vm` snapshot parsers can partially recover (see snapshot dump section below). ## Step 5: Dart Snapshot Dump (Hit-or-Miss) ```bash # Option 1: runtime adb pull from memory-mapped snapshot adb shell "run-as com.target.app cat /data/data/com.target.app/app_flutter/snapshot_blob.bin" \ > /tmp/snapshot_blob.bin 2>/dev/null # Option 2: use Dart VM snapshot reader tools # dart_snapshot_parser (community; partial support for Dart 2.x-3.x) pip install dart-snapshot-parser 2>/dev/null || true # Option 3: reFlutter's snapshot output # After running reFlutter and launching the patched app once, # reFlutter dumps snapshot_hash.txt which can seed symbol recovery tools ``` **Caveats**: Dart snapshot format changed in Dart 2.15, 3.0, and 3.4. Community parsers (snapshot_inspector, etc.) are often version-specific and may produce partial or no output on latest Flutter. Treat snapshot dump as a best-effort step; static string analysis is more reliable. ## Step 6: Flutter Plugin Identification Flutter plugins use platform channels with predictable naming: ```bash # Find channel names in libapp.so strings strings /tmp/apk-out/lib/arm64-v8a/libapp.so | grep -E "plugins\.|flutter\." | sort -u # Channel names like: # com.google.firebase.messaging → push tokens # plugins.flutter.io/path_provider → storage paths # flutter.baseflow.com/geolocator → GPS # plugins.flutter.io/local_auth → biometric # Cross-reference with flutter_assets/AssetManifest.json cat /tmp/apk-out/flutter_assets/AssetManifest.json | python3 -m json.tool | head ``` ## Evidence ```python kg_add_node( kind="finding", label="Flutter BoringSSL pinning bypassed", props={ "key": f"flutter-boringssl-bypass::{package_id}", "severity": "high", "cvss": 7.4, "package": package_id, "flutter_version": "<version-from-libflutter-strings>", "bypass_method": "reFlutter", "proxy_verified": True, }, ) ``` ## ZFP 1. Burp HTTP history showing decrypted HTTPS from patched Flutter app. 2. `strings libapp.so | grep https` output showing extracted endpoints. 3. Screenshot of reFlutter build output + uber-apk-signer signing confirming the patched APK was installed. ## OPSEC Notes - reFlutter requires re-signing the APK; app integrity checks (Google Play Integrity API, SafetyNet) will flag the modified signature. Use emulator or device without Play Store for testing. - The patched APK has a different certificate than the original; side- load via `adb install` or `adb install --bypass-low-target-sdk-block`. - For production devices, TunProxy routes traffic at the VPN layer without modifying the APK signature. - libflutter.so binary patching is version-specific; wrong offset crashes the app. Always test on a throwaway device/emulator first. ## Severity Table | Bug | Severity | |---|---| | No certificate validation (BoringSSL patched trivially) | High 7.4 | | Hardcoded API key / Firebase config in libapp.so strings | Critical 9.0 | | Sensitive data in Dart snapshot / kernel_blob | High 7.5 | | Flutter plugin channel unauthenticated method call | Medium-High | ## References - reFlutter: https://github.com/ptswarm/reFlutter - Flutter 3.24.0 proxy change: https://github.com/ptswarm/reFlutter/issues/107 - TunProxy (Android): https://github.com/raise-isayan/TunProxy - uber-apk-signer: https://github.com/patrickfav/uber-apk-signer - kayssel "Breaking Flutter" guide: https://blog.nviso.eu/2022/08/18/intercept-flutter-traffic-on-ios-and-android-http-https-edition/ - Cross-ref: `mobile/android/SKILL.md` (APK pull), `mobile/ios/dynamic/SKILL.md` (iOS BoringSSL path)
GitHub에서 보기