Frida解决证书绑定问题
客户端校验服务端--SSL Pinning
案例链接
1 | 链接: https: //pan .baidu.com /s/1xKT-zK1TEdTTV7cgLMW6RQ ? pwd =3597 提取码: 3597
|
1)在确定代理成功,能正常抓取https流量后,打开APP,这里以dida.apk
为例。打开案例APP并测试通过手机号注册发送验证码流程,在单机发送验证码
按钮后会提示发送失败,请重试
2)观察数据包发现提示Client closed the connection before a request was made. Possibly the SSL certificate was rejected.
判断出是客户端主动停止与服务器端的连接,APP使用了证书绑定技术
3)解决方案是使证书绑定的函数失效,需要对不同网络框架中证书绑定的相关代码有一定了解。例如,App使用okhttp3网络框架进行证书绑定(见代码清单10-1),那么最终使用的证书绑定类总是CertificatePinner类,此时Hook的目标就是CertificatePinner类中的函数;App使用TrustManager完成证书的绑定,那么Hook的目标就是TrustManager类中的证书绑定的函数。
OkHttp3证书绑定代码
由于网络框架有很多,没对每个网络框架进行函数确定工作量会很大。幸运的是前人已经完成了很多种网络框架的证书绑定函数的收集工作并将这些函数的Hook集成到Objection或其他工具中。这里介绍两种工具,已经涵盖了绝大多数的网络框架
- Objection
- github上一个项目(DroidSSLUnpinning)
https://github.com/WooyunDota/DroidSSLUnpinning/blob/master/ObjectionUnpinningPlus/hooks.js
objection 例子:
1)考虑到App可能在启动时就已经完成了证书绑定,在adbshell中先使用kill命令将App完全关闭再使用Objection进行注入,并使用如下命令完成证书解绑。
以下命令需要在Objection注入时执行,因此还需要使用Objection的-s/--start-command参数达到命令在应用启动之前就执行的效果。
2)在objection完成Hook后,同样继续测试发送验证码接口,发现仍然提示发送失败,请重试
。可能是objection工具并未集成足够多的网络框架证书绑定函数,可尝试使用上面推荐的 DroidSSLUnpinning
项目,该项目集成了更多的网络框架证书绑定相关函数。
手工Hook
经过DroidSSLUnpinning
和objection等将近20个网络框架证书绑定函数的Hook都失效的情况下,差不多可以得到结论:APP被混淆了。应对这种情况,大致有两个方法:
- 使用objection对所有HTTP字符串相关类进行Hook
- 考虑到App在验证证书时一定会打开证书文件判断是否是App自身信任的框架,因此一定会使用File类的构造函数打开证书文件获得文件句柄,在测试时可以使用Objection Hook上所有File类的构造函数,即File.$init函数。(推荐)
1)使用Hook File类句柄方法,需要注意的是,在终端中“”是特殊符号,因此在输入时需要对字符进行转义。可以参考的Objection注入命令:
1 | objection -g cn.ticktick.task explore -s "android hooking watch class_method java.io.File.\$init --dump-args --dump-backtrace --dump-return"
|
然后点击发送验证码
在打印日志中以/system/etc/security/cacerts这个系统存放证书的路径为关键词在终端进行搜索,最终会发现有一个栈信息中存在着非常明显的CertificatePinner.java 文 件 名 信 息 , 因 此 可 以 判 定 对 应 的 函 数xp.f.a()就是对应的完成证书绑定的函数。
2)由于objection不支持对函数逻辑进行修改,故使用Frida脚本进行Hook
function killCertificatePinner(){
Java.perform(function(){
console.log("Beginning killCertificatePinner !...")
Java.use("xp.f").a.implementation = function(str, list){
console.log("called xp.f.a ~")
// 将函数返回值置空
return ;
}
})
}
function main(){
killCertificatePinner();
}
setImmediate(main)
3)在使用Frida脚本Hook后,再次对相关流程进行测试的抓包结果如下所示
Frida脚本绕过服务器端校验客户端证书
本案例使用便利蜂APP(bianlifeng.apk),也可以网上下载
1)使用浏览器访问百度测试,确定Charles可以正常抓取手机端流量,然后设置代理,打开便利蜂APP,点击登录或注册,输入手机号,获取验证码,发现获取失败,查看数据包返回 400 No required SSL certificate was sent
,可以得知该APP使用了服务端校验证书的方式
2)从安全客上下载Frida脚本,以spawn运行脚本,即可打印出证书名称和密码
实用FRIDA进阶:内存漫游、hook anywhere、抓包
脚本代码
function hook_KeyStore_load() {
Java.perform(function () {
var StringClass = Java.use("java.lang.String");
var KeyStore = Java.use("java.security.KeyStore");
KeyStore.load.overload('java.security.KeyStore$LoadStoreParameter').implementation = function (arg0) {
// printStack("KeyStore.load1");
// 输出调用栈
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
console.log("KeyStore.load1:", arg0);
this.load(arg0);
};
KeyStore.load.overload('java.io.InputStream', '[C').implementation = function (arg0, arg1) {
// printStack("KeyStore.load2");
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
console.log("KeyStore.load2:", arg0, arg1 ? StringClass.$new(arg1) : null);
this.load(arg0, arg1);
};
console.log("hook_KeyStore_load...");
});
}
hook_KeyStore_load()
1 | frida -U -f com.bianlifeng.customer.android -l . /agent/Chap10/hookKeyStore .js --no-pause
|
运行结果:
获取证书名称,密码及其他信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | KeyStore.load1: null
java.lang.Throwable
at java.security.KeyStore.load(Native Method)
at com.wormpex.sdk.errors.CrashHandler.getKeyStore(Native Method)
at com.wormpex.sdk.network.a.c(ClientAuthenticationSocketFactory.java:2)
at com.wormpex.sdk.network.a.d(ClientAuthenticationSocketFactory.java:3)
at com.wormpex.sdk.network.a.b(ClientAuthenticationSocketFactory.java:1)
at com.wormpex.h.f$b$a.call(SdkInitHelper.java:2)
at com.wormpex.h.f$b$a.call(SdkInitHelper.java:1)
at com.wormpex.sdk.network.LazySSLSocketFactory.ensureCreateDelegate(LazySSLSocketFactory.java:8)
at com.wormpex.sdk.network.LazySSLSocketFactory.preCreateDelegate(LazySSLSocketFactory.java:1)
at com.wormpex.h.f$b$b$a.run(SdkInitHelper.java:1)
at com.wormpex.j.c.a$a.queueIdle(QUtil.java:3)
at android.os.MessageQueue.next(MessageQueue.java:404)
at android.os.Looper.loop(Looper.java:193)
at android.os.HandlerThread.run(HandlerThread.java:67)
KeyStore.load2: java.io.ByteArrayInputStream@bced593 blibee
|
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课