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推送到设备。
每次按此按钮时,都会向http://www.nviso.eu发送一个调用,如果成功,则会将其打印到设备日志中。
在我的设备上,我通过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应用程序实际上会抛出一个错误,我们可以将其作为起点:
我们需要做的第一件事是在BoringSSL库中找到这个错误。该错误实际上已向我们显示了触发错误的位置:handshake.cc:352。Handshake.cc确实是BoringSSL库的一部分,并且包含了执行证书验证的逻辑。第352行的代码如下所示,这很可能就是我们看到的错误。行数并不完全匹配,但这很可能是版本差异的结果。
这是ssl_verify_peer_cert函数的一部分,该函数返回ssl_verify_result_t枚举,它在第2290行的ssl.h中被定义:
如果我们可以将ssl_verify_peer_cert的返回值更改为ssl_verify_ok (=0),那么我们就可以继续了。然而,在这个方法中有很多事情正在发生,Frida只能更改函数的返回值。如果我们更改这个值,它仍会因为上面的ssl_send_alert()函数调用而失败(相信我,我试过)。
让我们找一个更好的hook的方法。handshake.cc的代码段正上方是以下代码,这是验证链的方法的实际部分:
session_verify_cert_chain函数在第362行的ssl_x509.cc中被定义。此函数还返回原始数据类型(布尔值),并且是一个更好的hook选项。如果此函数中的检查失败,则它仅通过OPENSSL_PUT_ERROR报告问题,但它没有像ssl_verify_peer_cert函数那样的问题。OPENSSL_PUT_ERROR是err.h中第418行被定义的宏,其中包含源文件名。这与用于Flutter应用程序的错误的宏相同。
既然我们知道要hook哪个函数了,现在我们要做的就是在libflutter.so中找到它。在session_verify_cert_chain函数中多次调用OPENSSL_PUT_ERROR宏,这样可以使用Ghidra轻松的找到正确的方法。因此,将库导入Ghidra,使用Search -> Find Strings并搜索x509.cc。
这里只有4个XREF,因此很容易找到一个看起来像session_verify_cert_chain的函数:
其中一个函数取2个整数,1个“undefined未定义”,并且包含一个对OPENSSL_PUT_ERROR(FUN_00316500)的单独调用。在我的libflutter.so版本中为FUN_0034b330。现在你要做的是从一个导出函数计算该函数的偏移量并将其hook。我通常会采用一种懒惰的方法,复制函数的前10个字节,并检查该模式出现的频率。如果它只出现一次,我就知道我找到了这个函数,并且我可以hook它。这很有用,因为我经常可以为库的不同版本使用相同的脚本。使用基于偏移的方法,这更加困难。这很有用,因为我可以经常对不同版本的库使用相同的脚本。对于基于偏移量的方法,更加困难。
所以,现在我们让Frida在libflutter.so库中搜索这个模式:
在我的Flutter应用程序上运行此脚本的结果如下:
现在,我们只需使用Interceptor将返回值更改为1 (true):
设置proxydroid并使用此脚本启动应用程序后,现在我们可以看到HTTP流量了:
我已经在一些Flutter应用程序上对此进行了测试,这种方法适用于所有应用程序。由于BoringSSL库较为稳定,因此这种方法可能会在未来很长一段时间内都有效。
禁用 SSL Pinning(SecurityContext)
最后,让我们看看如何绕过SSL Pinning。一种方法是定义一个包含特定证书的新SecurityContext。
对于我的应用程序,我添加了以下代码让它只接受我的Burp证书。SecurityContext构造函数接受一个参数withTrustedRoots,默认为false。
应用程序现在将自动接受我们的Burp代理作为任意网站的证书。如果我们现在将其切换到nviso.eu证书,我们将无法拦截连接(请求和响应)。
幸运的是,上面列出的Frida脚本已经绕过了这种root-ca-pinning实现,因为底层逻辑仍然依赖于BoringSSL库的相同方法。
禁用 SSL Pinning(ssl_pinning_plugin)
Flutter开发人员执行ssl pinning的方法之一是通过 ssl_pinning_plugin flutter插件。此插件实际上是发送一个HTTPS连接并验证证书,之后开发人员将信任该通信并执行non-pinned HTTPS请求:
该插件是Java实现的,我们可以使用Frida轻松的hook:
1
2
3
4
5
6
7
8
9
10
11
|
function disablePinning()
{
var SslPinningPlugin = Java.use("com.macif.plugin.sslpinningplugin.SslPinningPlugin");
SslPinningPlugin.checkConnexion.implementation = function()
{
console.log("Disabled SslPinningPlugin");
return true;
}
}
Java.perform(disablePinning)
|
结论
这是一个非常有趣的过程,因为Dart和BoringSSL都是开源的,所以进行的非常顺利。由于字符串的数量并不多,因此即使没有任何符号,也能很容易的找到禁用ssl验证逻辑的正确位置。我扫描函数序言(function prologue)的方法可能并不总是有效,但由于BoringSSL非常稳定,因此在未来的一段时间内它应该都会有效。
猜你还喜欢
- 07-08八年专业安全团队承接渗透入侵维护服务
- 08-06SQLMAP的注入命令以及使用方法
- 08-03白帽故事汇:网络安全战士从来不是「男生」的专利
- 07-27编辑器漏洞手册
- 07-12web安全之如何全面发现系统后台
- 02-22常见Web源码泄露总结
- 07-25网站后台登陆万能密码
- 07-23破解emlog收费模板“Begin”
- 01-12批量检测SQL注入
- 01-22Apache Solr远程代码执行漏洞(CVE-2017-12629)从利用到入侵检测
- 随机文章
-
- SRC漏洞挖掘实用技巧
- Javascript原型链攻击与防御
- 我如何通过Uber API接口劫持任意Uber注册账户
- Linux Sudo被曝漏洞,可导致用户以root权限运行命令
- Jenkins脏牛漏洞FRP内网提权
- ScoutSuite:一款针对云集群环境的安全审计工具
- 从宽字节注入认识PDO的原理和正确使用
- NDAY漏洞CVE-2017-11882新变异样本分析
- B2R2:一套针对二进制代码分析的实用算法、函数以及工具集
- 探究if条件语句引发的两个Web漏洞
- Apache Solr最新RCE漏洞分析
- 知名Web域名注册商披露数据泄露事件
- PHP-fpm 远程代码执行漏洞(CVE-2019-11043)分析
- 浅谈安全攻防场景下的安全检测
- Think CMF X任意内容包含漏洞分析复现
- 最后一个登录框引起的血案
- 十种注入技巧 | 通用性进程注入技巧研究
- 使用Burp拦截Flutter App与其后端的通信
- 存储型XSS的攻防:不想做开发的黑客不是好黑客
- Suricata + Lua实现本地情报对接
- 热门文章
-
- 八年专业安全团队承接渗透入侵维护服务
- Emlog黑客站模板“Milw0rm”发布
- Stuxnet纪录片-零日 Zero.Days (2016)【中文字幕】
- SQLMAP的注入命令以及使用方法
- 白帽故事汇:网络安全战士从来不是「男生」的专利
- 编辑器漏洞手册
- web安全之如何全面发现系统后台
- 常见Web源码泄露总结
- 渗透测试培训(第五期)
- 深入理解JAVA反序列化漏洞
- cmseasy前台无需登录直接获取敏感数据的SQL注入(有POC证明)
- 网站后台登陆万能密码
- 黑麒麟2016渗透培训系列教程
- 破解emlog收费模板“Begin”
- 那些强悍的PHP一句话后门
- Android平台渗透测试套件zANTI v2.5发布(含详细说明)
- 渗透工具BackTrack与KaliLinux全套视频教程
- Python列为黑客应该学的四种编程语言之一 初学者该怎么学
- CVE-2017-11882漏洞复现和利用
- 恶意程序报告在线查询工具
文章存档
- 2021年3月(4)
- 2020年12月(4)
- 2020年11月(5)
- 2020年10月(8)
- 2020年9月(8)
- 2020年8月(20)
- 2020年7月(47)
- 2020年6月(70)
- 2020年5月(41)
- 2020年4月(21)
- 2020年3月(120)
- 2020年2月(26)
- 2019年12月(12)
- 2019年11月(13)
- 2019年10月(17)
- 2019年9月(15)
- 2019年8月(13)
- 2019年7月(15)
- 2019年6月(15)
- 2019年5月(19)
- 2019年4月(23)
- 2019年3月(19)
- 2019年2月(11)
- 2019年1月(29)
- 2018年12月(24)
- 2018年11月(56)
- 2018年10月(79)
- 2018年9月(20)
- 2018年8月(17)
- 2018年7月(16)
- 2018年6月(7)
- 2018年5月(10)
- 2018年3月(6)
- 2018年2月(2)
- 2018年1月(11)
- 2017年11月(18)
- 2017年10月(6)
- 2017年9月(8)
- 2017年8月(7)
- 2017年7月(7)
- 2017年6月(15)
- 2017年5月(30)
- 2017年4月(7)
- 2017年3月(1)
- 2017年2月(4)
- 2017年1月(1)
- 2016年12月(3)
- 2016年11月(7)
- 2016年10月(6)
- 2016年9月(6)
- 2016年8月(102)
- 2016年7月(24)
- 2013年7月(1)
- 文章标签
-