前言
某次金融App测试,进行抓包时发现只能抓到请求包,不进行抓包是可以正常使用的,推测使用了双向认证,第一次遇到,折腾了好久终于绕过去了,简单记录一下解决过程。
0X01抓包分析
单向校验:客户端校验服务端证书
双向校验:在单向校验的基础上增加了服务端对客户端的校验
使用BurpSuite进行抓包,可直接抓取请求包,但是没有返回包,猜测是服务端对客户端证书做了校验,burp的证书不合法所以无法接受返回包
0X02寻找证书
解压安装包,寻找是否有.p12/.fpx/.pem等文件
解压apk包,搜索发现存在多个p12证书
利用openssl简单验证下是否存在空密码、弱密码(123456、password等)等情况
openssl pkcs12 -info -in xxxx.p12
经过测试发现有三个证书使用了123456弱密码,依次导入BurpSuite中验证发现还是无法抓到返回包还剩一个xxx.com.p12文件密码未发现弱密码,文件名正好和抓包的host一致,校验的证书应该就是这个了,接下来只能通过逆向Hook去获取密码了
0X03寻找密码
定位证书加载入口
反编译apk,全局搜索证书文件名字,无果
搜一下.p12后缀,定位到相关代码
定位到关键代码
利用AI简单分析下initClientKeyFileMap() 方法用于构建一个 mClientKeyFileMap 哈希表,使用HashMap存储主机名和对应的 .p12 文件,用于 SSL/TLS 双向认证场景中客户端密钥与证书的匹配。
追踪证书加载流程
查找用例,继续分析
上述代码初始化了一个HTTP客户端,并构建一个包含主机名与对应 HTTP 客户端实例的映射表mHttpClientMap 。同时,该方法还利用 mClientKeyFileMap (由 initClientKeyFileMap 方法初始化)中的密钥文件信息,为特定主机配置 SSL/TLS 双向认证的客户端
关键代码:
b0.a createHttpClientBuilder = createHttpClientBuilder();
createHttpClientBuilder.Q0(b1.a.a(App.j().getAssets().open(value),EncryptNDK.a().getValue(value)), b1.a.b());
在 Q0 方法中, SSLSocketFactory 的初始化依赖证书和密码(见 b1.a.a(...) 调用)
调用链示例:
initClientKeyFileMap() → createHttpClientBuilder() → Q0(sslFactory,trustManager)
b1.a.a 方法
-
输入参数
CibApp.j().getAssets().open(value) :从 Android 应用的 assets 目录加载 .p12 证书文件输入流。
EncryptNDK.a().getValue(value) :通过 EncryptNDK.a().getValue(value) 获取密码,用于保护 .p12 文件。
-
输出:返回包含客户端证书和私钥的 KeyStore 对象,用于构建 SSL 上下文
继续查找用例
定位到关键代码,使用 EncryptNDK.a().getValue(str) 方法处理得到密码, str 为主机名拼接 .p12 ,刚好为本地找到的那个p12证书文件名
跟踪 EncryptNDK.a().getValue(str) 方法发现为native方法,so层不太会,先使用frida直接Hook试试
脚本:
Java.perform(function () {
let EncryptNDK = Java.use("com.xxxx.EncryptNDK");
EncryptNDK["getValue"].implementation = function (str) {
console.log(`EncryptNDK.getValue is called: str=${str}`);
let result = this["getValue"](str);
console.log(`EncryptNDK.getValue result=${result}`);
return result;
};
});
Hook结果为SM4算法的key和iv,不是我想要的密码
猜测使用SM4对str进行加密得到密码,手动加密进行验证,发现密码无效
到这一步有点僵住了,硬着头皮看了半天so,最后还是没搞明白
这个时候我想到了frida主动调用,不会写怎么办,交给AI
脚本:
Java.perform(() => {
// 严格匹配类路径
const EncryptNDK = Java.use("com.xxxx.EncryptNDK");
// 构造固定参数(用户明确传入的 str 值)
const str = "xxxx.p12";
// 调用单例方法 a() 获取实例
const instance = EncryptNDK.a();
1
2
3
4
5
6
7
8
9
10Hook结果:qwe123
0x04 抓包验证
导入证书
// 主动调用 getValue() 并输出结果
const encryptedPassword = instance.getValue(str);
console.log(`[*] P12 证书密码加密结果: ${encryptedPassword}`);
});
Hook结果:qwe123
0X04抓包验证
导入证书
成功抓包
总结
感兴趣的可以公众号回答回复"深情哥"进群,有公开课会在群里面通知,包括审计和src。edu邀请码获取也可以联系深情哥。
内部edu+src培训,包括src挖掘,edu挖掘,小程序逆向,js逆向,app渗透,导师是挖洞过30w的奥特曼,edu上千分的带头大哥!!!联系深情哥即可。
原文始发于微信公众号(实战安全研究):记一次双向认证绕过
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论