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 ํ›„ํ‚น์€ Flutter ์•ฑ์—์„œ ๋™์ž‘ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

Flutter์—์„œ HTTPS ํŠธ๋ž˜ํ”ฝ ๊ฐ€๋กœ์ฑ„๊ธฐ

์ด๊ฒƒ์€ ์ด blog post์˜ ์š”์•ฝ์ž…๋‹ˆ๋‹ค.

Flutter์—์„œ HTTPS ๊ฐ€๋กœ์ฑ„๊ธฐ๊ฐ€ ๊นŒ๋‹ค๋กœ์šด ์ด์œ 

  • 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.

์ •ํ™•ํ•œ Flutter ์Šคํƒ ์‹๋ณ„

๋ฒ„์ „์„ ์•Œ๋ฉด ์˜ฌ๋ฐ”๋ฅธ ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ์žฌ๋นŒ๋“œํ•˜๊ฑฐ๋‚˜ ํŒจํ„ด ๋งค์นญํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

StepCommand / FileOutcome
์Šค๋ƒ…์ƒท ํ•ด์‹œ ๊ฐ€์ ธ์˜ค๊ธฐpython3 get_snapshot_hash.py libapp.soadb4292f3ec25โ€ฆ
ํ•ด์‹œ โ†’ Engine ๋งคํ•‘reFlutter์˜ enginehash ๋ฆฌ์ŠคํŠธFlutter 3 ยท 7 ยท 12 + engine commit 1a65d409โ€ฆ
์ข…์† ์ปค๋ฐ‹ ๊ฐ€์ ธ์˜ค๊ธฐํ•ด๋‹น engine ์ปค๋ฐ‹์˜ DEPS ํŒŒ์ผโ€ข dart_revision โ†’ Dart v2 ยท 19 ยท 6
โ€ข dart_boringssl_rev โ†’ BoringSSL 87f316d7โ€ฆ

Find get_snapshot_hash.py here.

๋Œ€์ƒ: 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:
  • ssl_x509.cc์—์„œ return 1;์„ ๊ฐ•์ œํ•ฉ๋‹ˆ๋‹ค.
  • (Optional) socket_android.cc์—์„œ ํ”„๋ก์‹œ๋ฅผ ํ•˜๋“œ์ฝ”๋”ฉ("10.0.2.2:8080").
  1. libflutter.so๋ฅผ Re-compileํ•˜๊ณ , APK/IPA๋กœ ๋‹ค์‹œ ๋„ฃ์–ด ์„œ๋ช… ํ›„ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.
  2. Pre-patched builds๋Š” ๋นŒ๋“œ ์‹œ๊ฐ„์„ ์ ˆ์•ฝํ•˜๊ธฐ ์œ„ํ•ด reFlutter GitHub ๋ฆด๋ฆฌ์Šค์— ์ผ๋ฐ˜ ๋ฒ„์ „์šฉ์œผ๋กœ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค.

Option B โ€“ Live hooking with Frida (the โ€œhard-coreโ€ path)

์‹ฌ๋ณผ์ด stripped๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ๋กœ๋“œ๋œ ๋ชจ๋“ˆ์—์„œ ํ•ด๋‹น ํ•จ์ˆ˜์˜ ์ฒซ ๋ฐ”์ดํŠธ๋ฅผ pattern-scanํ•œ ๋‹ค์Œ ๋ฐ˜ํ™˜ ๊ฐ’์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.

// 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 ํŒŒ์ผ์˜ ๋‚ด์šฉ์„ ๋ถ™์—ฌ๋„ฃ์–ด ์ฃผ์„ธ์š”(๋˜๋Š” ํŒŒ์ผ ์ „์ฒด๋ฅผ ์ œ๊ณต). ์ œ๊ณตํ•˜์‹  ๋‚ด์šฉ์„ ์ง€์นจ์— ๋”ฐ๋ผ ํ•œ๊ตญ์–ด๋กœ ๋ฒˆ์—ญํ•ด ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

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

ํฌํŒ… ํŒ

  • For arm64-v8a or armv7, Ghidra์—์„œ ํ•จ์ˆ˜์˜ ์ฒ˜์Œ ์•ฝ 32๋ฐ”์ดํŠธ๋ฅผ ๊ฐ€์ ธ์™€ ๊ณต๋ฐฑ์œผ๋กœ ๊ตฌ๋ถ„๋œ 16์ง„์ˆ˜ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ํ•œ ๋’ค sig๋ฅผ ๊ต์ฒดํ•˜์„ธ์š”.
  • ๊ฐ Flutter release๋‹น ํ•˜๋‚˜์˜ ํŒจํ„ด์„ ์œ ์ง€ํ•˜๊ณ , ๋น ๋ฅธ ์žฌ์‚ฌ์šฉ์„ ์œ„ํ•ด ์น˜ํŠธ์‹œํŠธ์— ์ €์žฅํ•˜์„ธ์š”.

ํ”„๋ก์‹œ๋ฅผ ํ†ตํ•œ ํŠธ๋ž˜ํ”ฝ ๊ฐ•์ œ ์ „๋‹ฌ

Flutter ์ž์ฒด๋Š” ๊ธฐ๊ธฐ ํ”„๋ก์‹œ ์„ค์ •์„ ๋ฌด์‹œํ•ฉ๋‹ˆ๋‹ค. ๊ฐ€์žฅ ์‰ฌ์šด ์˜ต์…˜:

  • Android Studio emulator: Settings โ–ถ Proxy โ†’ manual.
  • Physical device: evil Wi-Fi AP + DNS spoofing, ๋˜๋Š” Magisk ๋ชจ๋“ˆ๋กœ /etc/hosts๋ฅผ ํŽธ์ง‘.

๋น ๋ฅธ Flutter TLS bypass ์›Œํฌํ”Œ๋กœ์šฐ (Frida Codeshare + system CA)

ํ•€๋œ Flutter API๋งŒ ๊ด€์ฐฐํ•ด์•ผ ํ•  ๋•Œ๋Š”, rooted/writable AVD, system-trusted proxy CA, ๊ทธ๋ฆฌ๊ณ  drop-in Frida ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์กฐํ•ฉํ•˜๋Š” ๊ฒƒ์ด 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. ํ˜ธ์ŠคํŠธ ์ธก ๋„๊ตฌ๋ฅผ ์„ค์น˜ํ•˜๊ณ  ๋Œ€์ƒ ํŒจํ‚ค์ง€๋ฅผ ์—ด๊ฑฐํ•ฉ๋‹ˆ๋‹ค.
pip3 install frida-tools --break-system-packages
adb shell pm list packages -f | grep target
  1. Codeshare hook์œผ๋กœ Flutter ์•ฑ์„ ์‹คํ–‰ํ•ด BoringSSL pin checks๋ฅผ ๋ฌด๋ ฅํ™”ํ•ฉ๋‹ˆ๋‹ค.
frida -U -f com.example.target --codeshare TheDauntless/disable-flutter-tls-v1 --no-pause

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 ๋˜๋Š” host-only VPN์œผ๋กœ ํด๋ฐฑํ•˜์„ธ์š”.

OS ๋ ˆ์ด์–ด์—์„œ CA๋ฅผ ์‹ ๋ขฐํ•˜๋„๋ก ์„ค์ •ํ•˜๊ณ  Frida๊ฐ€ Flutter์˜ pinning ๋กœ์ง์„ ๋ฌด๋ ฅํ™”ํ•˜๋ฉด, Burp/mitmproxy๋Š” APK๋ฅผ ์žฌํŒจํ‚ค์ง•ํ•˜์ง€ ์•Š๊ณ ๋„ API fuzzing (BOLA, token tampering ๋“ฑ)์— ๋Œ€ํ•ด ์™„์ „ํ•œ ๊ฐ€์‹œ์„ฑ์„ ํšŒ๋ณตํ•ฉ๋‹ˆ๋‹ค.

BoringSSL ๊ฒ€์ฆ์˜ ์˜คํ”„์…‹ ๊ธฐ๋ฐ˜ ํ›… (์‹œ๊ทธ๋‹ˆ์ฒ˜ ์Šค์บ” ์—†์Œ)

ํŒจํ„ด ๊ธฐ๋ฐ˜ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์•„ํ‚คํ…์ฒ˜ ๊ฐ„(e.g., x86_64 vs ARM)์—์„œ ์‹คํŒจํ•  ๊ฒฝ์šฐ, libflutter.so ๋‚ด๋ถ€์˜ ์ ˆ๋Œ€ ์ฃผ์†Œ๋กœ 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 args, returns bool).
  • ์‹ฌ๋ณผ์ด ์ œ๊ฑฐ๋œ ๋นŒ๋“œ์—์„œ๋Š” Search โ†’ For Strings โ†’ ssl_client โ†’ XREFs๋ฅผ ์‚ฌ์šฉํ•œ ๋‹ค์Œ, ์ฐธ์กฐ๋œ ๊ฐ FUN_...์„ ์—ด์–ด ํฌ์ธํ„ฐ ๊ฐ™์€ ์ธ์ž 3๊ฐœ์™€ boolean ๋ฐ˜ํ™˜์„ ๊ฐ€์ง„ ํ•จ์ˆ˜๋ฅผ ์„ ํƒํ•˜์„ธ์š”.
  • ๋Ÿฐํƒ€์ž„ ์˜คํ”„์…‹์„ ๊ณ„์‚ฐํ•˜์„ธ์š”: Ghidra์— ํ‘œ์‹œ๋œ ํ•จ์ˆ˜ ์ฃผ์†Œ์—์„œ ์ด๋ฏธ์ง€ ๋ฒ ์ด์Šค๋ฅผ ๋นผ์„ธ์š” (์˜ˆ: Ghidra๋Š” PIE Android ELF์— ๋Œ€ํ•ด ์ข…์ข… 0x00100000์„ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค). ์˜ˆ: 0x02184644 - 0x00100000 = 0x02084644.
  • base + offset์œผ๋กœ ๋Ÿฐํƒ€์ž„์—์„œ ํ›…์„ ๊ฑธ๊ณ  ์„ฑ๊ณต์„ ๊ฐ•์ œํ•˜์„ธ์š”:
// 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);

Notes

  • Signature scans์€ ARM์—์„œ๋Š” ์„ฑ๊ณตํ•  ์ˆ˜ ์žˆ์ง€๋งŒ opcode layout์ด ๋ณ€๊ฒฝ๋˜์–ด x86_64์—์„œ๋Š” ๋†“์น  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค; ์ด offset method๋Š” RVA๋ฅผ ์žฌ๊ณ„์‚ฐํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ์•„ํ‚คํ…์ฒ˜์— ๊ตฌ์• ๋ฐ›์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • This bypass๋Š” BoringSSL์ด any chain์„ ์ˆ˜๋ฝํ•˜๊ฒŒ ๋งŒ๋“ค์–ด Flutter ๋‚ด๋ถ€์˜ pins/CA trust์™€ ๊ด€๊ณ„์—†์ด HTTPS MITM์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.
  • TLS ์ฐจ๋‹จ์„ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด debugging ์ค‘์— ํŠธ๋ž˜ํ”ฝ์„ force-routeํ•˜๋Š” ๊ฒฝ์šฐ, ์˜ˆ:
iptables -t nat -A OUTPUT -p tcp -j DNAT --to-destination <Burp_IP>:<Burp_Port>

โ€ฆ์—ฌ์ „ํžˆ ์œ„์˜ hook์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๊ฒ€์ฆ์€ Android์˜ ์‹œ์Šคํ…œ ์‹ ๋ขฐ ์ €์žฅ์†Œ๊ฐ€ ์•„๋‹ˆ๋ผ libflutter.so ๋‚ด๋ถ€์—์„œ ์ด๋ฃจ์–ด์ง€๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์ฐธ๊ณ ์ž๋ฃŒ

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 ์ง€์›ํ•˜๊ธฐ