其他
Flutter 逆向初探
本文为看雪论坛优秀文章
看雪论坛作者ID:Mr_Holiday[译]
一
Flutter简介
二
Flutter特征
其中,libapp.so库包含了开发过程中编写的所有编译过的Dart业务代码。libflutter.so库则存放了flutter的一些基础类库。
三
逆向Flutter解决HTTPS抓包问题
其中的HTTP和HTTPS是通过HTTPClient类的getUrl方式发送指定请求,Pinned Request是通过使用Dio包执行SSLPinning,发送HTTPS请求并验证证书。主要的实现逻辑如下所示:
首先像抓包Android APP一样拦截Flutter的请求包,但会发现三种请求都无法被拦截,因为Flutter APP默认情况下不使用系统的代理设置,所以可以使用Drony + BurpSuite,将手机上的目标APP的流量都重定向到Drony自身,再转发到BurpSuite上。发现只能拦截到HTTP请求,如下图所示。
发送HTTPS和HTTPS (Pinned) 请求失败,可以用logcat看到相关报错信息,这部分我们之后分析:
接下来就是通过逆向来绕过证书链校验,目前有两种方法可以解决这类场景:
1) 使用reFlutter开源逆向分析工具;
2) 使用Frida脚本hook libflutter.so中的函数。
首先PC端命令行安装reFlutter:pip3 install reflutter。装完输入命令:reflutter flutter_ssl_pinning_bypass_lab_android.apk。选择[1]流量监控和拦截,输入PC端的IP地址后,将获取到release.RE.apk,但此apk尚未签名,需要我们手动签名。
通过MT管理器或者uber-apk-signer jar签名,这里使用后者。输入命令:java -jar uber-apk-signer-1.2.1.jar --apk release.RE.apk。然后将重签名的apk安装到真机或者模拟器上。
设置Drony的wifi代理主机名端口和BurpSuite一致。
使用reFlutter框架拦截HTTPS流量比较简单易上手,但是存在一个很大的弊端,就是需要重打包app,导致它的局限性较大,因为现在的应用市场很多Flutter APP是做了防二次打包的,虽然可以直接替换私有目录的libflutter.so,但需要root,很多APP也会检测root。对于这些防护倒是可以使用frida框架绕过签名校验和root代码,但这样也增加了攻击成本。
因为BoringSSL是开源的,很容易就可以查到如下所示的393行代码段,其中的ret指的是SSL校验结果(验证通过是ssl_verify_ok),这段代码表示如果结果是ssl_verify_invalid,则报错提醒。
这个判断逻辑是处于ssl_verify_peer_cert函数中的,这个函数最后会return ret。那么如果直接hook这个函数修改ret的值为ssl_verify_ok可以绕过SSL验证吗?答案是否定的。因为最有可能采用的代码路径是,注册了一个自定义验证回调,使得368行返回true,而在第369行执行的回调返回ssl_verify_invalid。然后代码跳转到392行,ret变量赋值为ssl_verify_invalid,因此在ssl_verify_peer_cert函数return ret之前就会显示警报(393-394行)。
继续观察上下文环境寻找更好hook的函数,发现Handshake.cc的386行代码段是验证链的方法的实际部分。这行的session_verify_cert_chain函数实际是在ssl_x509.cc的第361行定义,此函数返回布尔值类型,如果hook这个函数,修改返回值为true,则386行的ret为ssl_verify_ok。
锁定hook的目标函数后,我们现在需要在libflutter.so中找到它。找到session_verify_cert_chain函数唯一性的特征方便后续IDA中定位该函数,如[ssl_client]或[ssl_server]。将libflutter.so导入IDA,使用Search -> Find Strings搜索关键词[ssl_client],发现有4处交叉引用。
第二处和第三处都是Function sub_3B1834中的,跳转到函数调用位置。
F5检查是不是目标函数,点击sub_7F4B14,查看代码逻辑与ssl_x509.cc中389行一致,确定是目标。
这时我们计算这个函数与其中一个导出函数的偏移量,并将它hook。可以复制函数的前10个以上的字节,之后利用frida编写的脚本检查该模式出现的频率。如果它只发生一次,说明找到了函数,可以hook它。首先在IDA中的Options->General->Number of opcode bytes 设置为4,定位到sub_3B1834起始位置,复制前10个以上的字节。
接下来就是编写frida的脚本,使用之前复制的字节定位,在运行时使用Interceptor将返回值更改为1 (true),这样就实现了绕过证书链检查拦截HTTPS和HTTPS(Pinned)的请求,代码如下所示。
其中注释说明的地址要add 0x01是因为在32位ARM上, 对于ARM函数, 此地址的最低有效位必须设置为0, 对于Thumb函数, 此地址必须设置为1。因为我们这里hook的libflutter.so是64位的,地址不需要再加1。
Flutter客户端界面显示请求成功。
并且Burpsuite也可以拦截所有请求。
如果将hook目标函数的前10多个字节找到规律部分使用通配符代替,上面的脚本会适用于大多数的Flutter APP,有兴趣的师傅们可以自己试试。
参考
https://l33t-en0ugh.gitbook.io/infosec/android-pentesting/bypass-ssl-pinning-for-flutter-apps-using-reflutter-framework
https://blog.nviso.eu/2020/05/20/intercepting-flutter-traffic-on-android-x64/
https://blog.nviso.eu/2022/08/18/intercept-flutter-traffic-on-ios-and-android-http-https-dio-pinning/
https://github.com/google/boringssl/blob/master/ssl/ssl_x509.cc
https://github.com/google/boringssl/blob/master/ssl/handshake.cc
https://github.com/google/boringssl/blob/master/include/openssl/err.h
看雪ID:Mr_Holiday
https://bbs.kanxue.com/user-home-971971.htm
# 往期推荐
1、Realworld CTF 2023 ChatUWU 详解
4、Kernel PWN-开启smap保护的babydrive
6、Relocate、PLT、GOT And Lazy Binding
球分享
球点赞
球在看
点击“阅读原文”,了解更多!