攔截 Flutter App 的 HTTPS 要求

修改 Flutter 的 Core library 來取消 Flutter 檢查憑證的功能,讓 Debugging Proxy 可以攔截與修改 HTTP 的 Request 與 Response

攔截 Flutter App 的 HTTPS 要求

最近在做一些 Flutter App 的 PT,需要攔截 App 到 Backend 的要求,取得 API List或修改 Request / Response。


在 Android 6.0 以前,要攔截 App 的 HTTP 要求,只需要於Wi-Fi 設定中,將 Proxy 指向 BurpSuite / Fiddler / Charles,安裝 Debugging Proxy 的 CA 憑證,就能成功抓包了。

但是在 Android 7.0 之後,增加了 App Network Security,就像 Certificate Pinning 一樣,如果沒有指定允許的 CA 在 network_security_config.xml 中,Android 就不會讓網路流量經過 Proxy。
有位大神寫了一個自動化工具放在 Github 上,解包 APK 後,塞入允許所有 CA 的 network_security_config.xml ,流量就能被 Proxy 接收了。

Flutter 的 App 就不一樣了,Flutter 用了自己的 Runtime Engine 去執行 dart bytecode ,所以在 apk 裡的 lib 資料夾中,每個 arch 都會各自平台的 libfultter.so 去執行 kernel_blob.bin(Non-Release Mode) / libapp.so(Release Mode)。
如果 apk 是 Debug Mode ,可以直接從 kernel_blob.bin 裡去還原程式碼,Release Mode 就沒辦法了。

Flutter 使用了 BoringSSL 套件去建立 TLS 連線,原始碼可以在 Flutter 官方的 repo 裡面找到。


所以為了攔截 Release App 的 HTTP Request,有幾件事情要完成:

  1. 攔截 App 的流量,導向到 Debugging Proxy
  2. Bypass Flutter 的 CA 憑證檢查
  3. Resquest / Response 改起來

1. 攔截 App 的流量,導向到 Debugging Proxy

有分為 Rooted 跟 Non-Rooted 的方法:

  • Rooted
    • iptables
      • 太麻煩了
    • ProxyDroid
      • 自動產生 iptables 指令套用,簡單
  • Non-Rooted
    這部分適合 Android 版本比較高的狀況,畢竟現在手機 Root 之後少了一堆功能。
    • VPN2SOCK
      • tun2sock + VPN 攔截流量
      • 自己寫的 PoC,還有滿滿的 Bug
  • Android 模擬器
    • LDPlayer
      • 有內建 Root / ADB
      • 有 Android 5.1 / 7.1 可選
      • 我是用這個啦,用 Android 5.1 版的,直接開 ProxyDroid就好

2. Bypass Flutter 的 CA 憑證檢查

根據這篇的錯誤訊息(不知道為什麼我的 App 沒出現 Exception),Flutter 的 TLS handshake failed 出現在 handshake.cc 的 352 行,原因是 CERTIFICATE_VERIFY_FAILED

查看 handshake.cc 的原始碼,發現 flutter 取得 session_verify_cert_chain 回傳的 boolean,來判斷檢查憑證是否合法。
那就讓 session_verify_cert_chain 永遠回傳 true 就好了。

找了一下,發現 session_verify_cert_chain 位在 ssl_x509.cc:362 ,在 ssl_x509.cc:391ssl_x509.cc:411 有 2 處 return false,把這兩個地方 patch 成 return true 就好了!!


libfultter.so @ armeabi-v7a

  • 就是 32 bits 的 ARM
  • 如果懶惰的話,只要 patch 這個 Arch 就好,大多數的手機(arm64, x86, x86_64)為了相容性都有放 ARM 32 bits 的 runtime,LDPlayer 跟我的 S10 也有,x86 的就當作練習吧。

先用 Ghidra 打開 lib/armeabi-v7a/libflutter.so
為了找到 session_verify_cert_chain,看了一下 Source,這個方法有 3 個 input,也有個 macro OPENSSL_PUT_ERROR 用來印出錯誤訊息,定義在 err.h:422,會讓錯誤訊息顯示出檔名跟行數,所以就在 Ghidra 中尋找 ssl_x509.cc 這個字串,找 xrefs 的 function 並且是 3 個 input 的就是了。

Ghidra -> Search -> Program Text,搜尋 ssl_x509.cc
有 7 個 xrefs

FUN_012c68b4 中,看到有3個 input,一個 FUN_0127d008 (OPENSSL_PUT_ERROR) ,OPENSSL_PUT_ERROR,Line Number 0x186 = 390 也是在 session_verify_cert_chain 裡面。

點一下第 14 行的 return 0 就會定位到 function 的結尾。

                             LAB_012c696c
        012c696c 00  24           mov        r4,#0x0         // Line #18 return 0;
        012c696e 0d  e0           b          LAB_012c698c
                             LAB_012c6970

        012c6970 4f  f4  c3  70   mov.w      r0,#0x186
        012c6974 00  21           mov        r1,#0x0
        012c6976 00  90           str        r0,[sp,#0x0 ]
        012c6978 10  20           mov        r0,#0x10
        012c697a 15  4b           ldr        r3,[DAT_012c69d0 ]                               = FEE39C79h
        012c697c 0b  22           mov        r2,#0xb
        012c697e 00  24           mov        r4,#0x0        // Line #62 uVar4 = 0;
        012c6980 7b  44           add        r3=>s_...
        012c6982 b6  f7  41  fb    bl         FUN_0127d008  // OPENSSL_PUT_ERROR 

                             LAB_012c6986
        012c6986 03  a8           add        r0,sp,#0xc
        012c6988 d8  f7  ce  fc    bl         FUN_0129f328

                             LAB_012c698c                   // return
        012c698c 20  46           mov        r0,r4
        012c698e 23  b0           add        sp,#0x8c
        012c6990 bd  e8  f0  8f   pop.w      { r4, r5, r6, r7, r8, r9, r10 , r11 , pc }

只要將 L18 的 return 0 跟 L62 的 uVar4 = 0 換成 1 就可以了。

Online ARM Assambler 取得 movs r4, 1 的 OpCode 0124用 Hex Editor 改,或是直接在 Ghidra 中,在movs r4, 0x0 右鍵選 Patch Instruction,把 0x0 改成 0x1,存檔。

libflutter.so 塞回 apk 裡面,重新用 apksigner 簽章,丟進手機安裝。


Referance

https://orangewirelabs.wordpress.com/2019/06/04/bypassing-root-ca-checks-in-flutter-based-apps-on-android/

Subscribe to 54PL

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe