Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。Flutter应用程序是用Dart编写的,这是一种由Google在7年多前创建的语言。
通常情况下我们会通过添加Burp作为拦截代理,来拦截移动应用程序与其后端之间的通信流量(以用于安全评估等)。虽然Flutter应用代理起来可能会有些困难,但这绝对是可能的。
TL;DR
Flutter使用Dart编写,因此它不会使用系统CA存储
Dart使用编译到应用程序中的CA列表
Dart在Android上不支持代理,因此请使用带有iptables的ProxyDroid
Hook x509.cc中的session_verify_cert_chain函数以禁用链验证(chain validation)
你可以直接使用本文底部的脚本,或者按照下面的步骤获取正确的字节或偏移量。
测试设置
为了执行我的测试,我安装了flutter插件并创建了一个flutter应用程序,该应用程序附带了一个默认的交互式按钮,用于递增计数器。我将其修改为通过HttpClient类获取URL:
该应用程序可以使用flutter build aot进行编译,并通过adb install推送到设备。
每次按此按钮时,都会向
发送一个调用,如果成功,则会将其打印到设备日志中。
在我的设备上,我通过Magisk-Frida-Server安装了Frida,我的Burp证书通过MagiskTrustUserCerts模块添加到系统CA存储中。但不幸的是,Burp上并没有看到有任何流量通过,即使应用程序日志显示请求成功。
通过 ProxyDroid/iptables 向代理发送流量
HttpClient有一个findProxy方法,其文档写的非常清楚:默认情况下,所有流量都直接发送到目标服务器,而不考虑任何代理设置:
设置用于解析代理服务器的功能,该代理服务器用于打开指定URL的HTTP连接。如果未设置此功能,则将始终使用直接连接。
– findProxy文档
应用程序可以将此属性设置为HttpClient.findProxyFromEnvironment,它会搜索特定的环境变量,例如http_proxy和https_proxy。即使应用程序是用这个实现编译的,但在Android上它也将毫无用处,因为所有应用程序都是初始zygote进程的子进程,因此没有这些环境变量。
也可以定义一个返回首选代理的自定义findProxy实现。对我的测试应用程序进行的快速修改确实表明,此配置将所有HTTP数据发送到了我的代理服务器:
当然,我们无法在黑盒评估期间修改应用程序,因此需要另一种方法。幸运的是,我们总是有iptables fallback来将所有流量从设备路由到我们的代理。在已root的设备上,ProxyDroid可以很好地处理这个问题,我们可以看到所有HTTP流量都流经了Burp。
拦截 HTTPS 流量
这是个更加棘手的问题。如果我将URL更改为HTTPS,会导致Burp SSL握手失败。这很奇怪,因为我的设备被设置为将我的Burp证书包含为受信任的根证书。
经过一些研究,最终我在一个GitHub issue中找到了有关Windows上问题的解释,但它同样也适用于Android: Dart使用Mozilla的NSS库生成并编译自己的Keystore。
这意味着我们不能通过将代理CA添加到系统CA存储来绕过SSL验证。为了解决这个问题,我们必须深入研究libflutter.so,并找出我们需要修补或hook的,以验证我们的证书。Dart使用Google的BoringSSL来处理与SSL相关的所有内容,幸运的是Dart和BoringSSL都是开源的。
当向Burp发送HTTPS流量时,Flutter应用程序实际上会抛出一个错误,我们可以将其作为起点: