服务端证书校验
客户端校验
1 2 - 在客户端中预设证书信息 - 客户端向服务端发送请求,将服务端返回的证书信息(公钥)和客户端预设证书信息进行校验
服务端校验
1 2 - 在客户端预设证书(p12/bks) - 客户端向服务端发送请求时,携带证书信息,在服务端会校验客户端携带过来证书的合法性
1.服务端校验逻辑 服务端证书的校验逻辑:
逆向时 ,需要实现:
获取 bks 或 p12证书 文件
获取证书相关密码
将证书导入到charles,可以实现抓包(bks格式需要转换p12格式)
用requests发送请求时,携带证书去发送请求
2.一波逆向案例 2.1 泡泡聊天
版本:v1.7.4
下载:https://www.wandoujia.com/apps/8280413
2.1.1 抓包
2.1.2 Hook密码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Java .perform (function ( ) { var KeyStore = Java .use ("java.security.KeyStore" ); KeyStore .load .overload ('java.io.InputStream' , '[C' ).implementation = function (v1, v2 ) { var pwd = Java .use ("java.lang.String" ).$new(v2); console .log ('\n------------' ) console .log ("类型:" + this .getType ()); console .log ("密码:" + pwd); console .log (JSON .stringify (v1)); var res = this .load (v1, v2); return res; }; });
2.1.3 Hook证书文件 在开发时,是将证书文件加载到 InputStream
对象中,后续发送请求是会携带。。。
而我们想要获取证书可有两种方式:
2.1.3.1.定位代码
所以,证书的位置 在apk的assets目录下 client.bks
且密码阿是 111111
2.1.3.2.导出证书 注意:在手机上一定要先给当前app开启可以操作硬盘的权限,否则无法导出证书文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 Java .perform (function ( ) { var KeyStore = Java .use ("java.security.KeyStore" ); var String = Java .use ("java.lang.String" ); KeyStore .load .overload ('java.io.InputStream' , '[C' ).implementation = function (inputStream, v2 ) { var pwd = String .$new(v2); console .log ('\n------------' ) console .log ("密码:" + pwd, this .getType ()); if (this .getType () === "BKS" ) { var myArray = new Array (1024 ); for (var i = 0 ; i < myArray.length ; i++) { myArray[i] = 0x0 ; } var buffer = Java .array ('byte' , myArray); var file = Java .use ("java.io.File" ).$new("/sdcard/Download/paopao-" + new Date ().getTime () + ".bks" ); var out = Java .use ("java.io.FileOutputStream" ).$new(file); var r; while ((r = inputStream.read (buffer)) > 0 ) { out.write (buffer, 0 , r); } console .log ("save success!" ) out.close () } var res = this .load (inputStream, v2); return res; }; });
2.1.4 转换bks到p12 charles不支持导入bks格式的证书,如果逆向过程中得到了bks格式证书,需要使用 portecle
将bks证书转化弄成p12格式,然后再处理。
提示:此工具依赖jdk,请务必先在自己电脑上安装jdk。
打开portecle,并导入bks证书。
提示:也可以使用 https://keystore-explorer.org/downloads.html 来做证书的转换(我的mac不太好用)。
2.1.5 charles导入证书 将p12证书导入charles,然后再转抓包就可以了。
再次抓包,成功了。
2.1.6 Python请求 如果是服务端证书校验,需要携带证书才能访问。
2.1.6.1.requests_pkcs12 1 pip install requests-pkcs12
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 from requests_pkcs12 import get, postres = post( url='https://8.218.94.100:48837/userservices/v2/user/login' , json={ "device_type" : "app" , "username" : "008615131255555" , "password" : "a2c62dffccea2f1d638aa3945be8770c" , "device_id" : "1d40d48517776215104989bd5949fb91" , "device_name" : "Xiaomi M2007J17C" , "device_model" : "M2007J17C" }, headers={ "bundle_id" : "com.paopaotalk.im" , "version" : "1.7.4" , "timestamp" : "1600" , "sign" : "94703ac3c05a8a5380010cc90890c72b" , "app_id" : "qiyunxin" , "Accept-Language" : "zh-CN" , "package" : "com.paopaotalk.im" , }, pkcs12_filename='Client1.p12' , pkcs12_password='111111' , verify=False ) print (res.text)
2.6.1.2.requests 默认requests不支持直接使用p12格式的证书,所以需要将p12转换成pem才可以。
1 >>>openssl pkcs12 -in paopao-client.p12 -out demo.pem -nodes -passin "pass:111111"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 from requests import postres = post( url='https://8.218.94.100:48837/userservices/v2/user/login' , json={ "device_type" : "app" , "username" : "008615131255555" , "password" : "a2c62dffccea2f1d638aa3945be8770c" , "device_id" : "1d40d48517776215104989bd5949fb91" , "device_name" : "Xiaomi M2007J17C" , "device_model" : "M2007J17C" }, headers={ "bundle_id" : "com.paopaotalk.im" , "version" : "1.7.4" , "timestamp" : "1600" , "sign" : "94703ac3c05a8a5380010cc90890c72b" , "app_id" : "qiyunxin" , "Accept-Language" : "zh-CN" , "package" : "com.paopaotalk.im" , }, cert='demo.pem' , verify=False ) print (res.text)
2.2 美之图 版本:v3.5.6 (apk文件见课件)
注意 :请不要晚上逆向这个app,有点伤身体(无不良引导,实在是案例少)。
2.2.1 校验逻辑 服务端证书的校验逻辑:
逆向时 ,需要实现:
获取 bks 或 p12证书 文件
获取证书相关密码
将证书导入到charles,可以实现抓包(bks格式需要转换p12格式)
用requests发送请求时,携带证书去发送请求
2.2.2 Hook密码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Java .perform (function ( ) { var KeyStore = Java .use ("java.security.KeyStore" ); KeyStore .load .overload ('java.io.InputStream' , '[C' ).implementation = function (v1, v2 ) { var pwd = Java .use ("java.lang.String" ).$new(v2); console .log ('\n------------' ) console .log ("类型:" + this .getType ()); console .log ("密码:" + pwd); console .log (JSON .stringify (v1)); var res = this .load (v1, v2); return res; }; });
2.2.3 Hook证书文件 在开发时,是将证书文件加载到 InputStream
对象中,后续发送请求是会携带。。。
而我们想要获取证书可有两种方式:
2.2.3.1.定位代码
Hook KeyStore.load
输出调用栈,寻找证书的位置。
1 2 3 4 5 6 7 8 9 java.lang.Throwable at java.security.KeyStore.load(Native Method) at com.deepe.c.j.c.b.b(Unknown Source:32) at com.deepe.c.j.c.b.a(Unknown Source:39) at com.deepe.c.j.e.d.getSslSocketFactory(Unknown Source:20) at com.deepe.c.j.d.g.a(Unknown Source:42) at com.deepe.c.j.d.g.a(Unknown Source:120) at com.deepe.c.j.d.a.a(Unknown Source:33) at com.deepe.c.j.h.run(Unknown Source:69)
app是有壳,需要先试用 frida-dexdump
进行脱壳,然后反编译。
1 frida-dexdump -U -f com.mmzztt.app
定位代码位置
再结合Hook脚本,就能确定其实是读取本地的 config
文件,其实就是p12证书。注意 :因为有壳,所以Hook相关代码时需要延迟下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 Java .perform (function ( ) { var KeyStore = Java .use ("java.security.KeyStore" ); var String = Java .use ("java.lang.String" ); KeyStore .getInstance .overload ('java.lang.String' ).implementation = function (name ) { return this .getInstance (name); } KeyStore .load .overload ('java.io.InputStream' , '[C' ).implementation = function (v1, v2 ) { var pwd = String .$new(v2); console .log ("密码:" + pwd); var res = this .load (v1, v2); return res; }; setTimeout (function ( ){ var b = Java .use ("com.deepe.c.j.c.b" ); b.b .implementation = function (str,str2 ) { console .log (str,str2); return this .b (str,str2); } var UZUtility = Java .use ("com.uzmap.pkg.uzkit.UZUtility" ); UZUtility .guessInputStream .implementation = function (str ) { console .log (str); return this .guessInputStream (str); } var f = Java .use ("com.deepe.c.i.f" ); f.a .overload ('java.lang.String' , 'java.io.InputStream' ).implementation = function (str,stream ) { console .log (str,stream); return this .a (str,stream); } },1000 ); });
2.2.3.2.导出证书 注意:在手机上一定要先给当前app开启可以操作硬盘的权限,否则无法导出证书文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 Java .perform (function ( ) { var KeyStore = Java .use ("java.security.KeyStore" ); var String = Java .use ("java.lang.String" ); KeyStore .load .overload ('java.io.InputStream' , '[C' ).implementation = function (inputStream, v2 ) { var pwd = String .$new(v2); console .log ('\n------------' ) console .log ("密码:" + pwd, this .getType ()); if (this .getType () === "PKCS12" ) { var myArray = new Array (1024 ); for (var i = 0 ; i < myArray.length ; i++) { myArray[i] = 0x0 ; } var buffer = Java .array ('byte' , myArray); var file = Java .use ("java.io.File" ).$new("/sdcard/Download/meizhitu-" + new Date ().getTime () + ".bks" ); var out = Java .use ("java.io.FileOutputStream" ).$new(file); var r; while ((r = inputStream.read (buffer)) > 0 ) { out.write (buffer, 0 , r); } console .log ("save success!" ) out.close () } var res = this .load (inputStream, v2); return res; }; });
2.2.4 charles导入证书 同上
2.2.5 python请求 同上
3 笔记 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 抓包-服务端证书校验 今日概要: - 客户端校验补充 - 服务端证书校验 1.客户端校验补充 1.1 客户端证书校验逻辑 安卓开发者,在安卓app中集成代码,校验:集成证书 vs 真正请求证书是否一致。【手机端校验】 1.2 三处客户端证书校验 - 公钥校验,安卓开发:证书->Base64编码->字符串,手机APP发送请求:字符串+真正请求的字符。 案例: - Net01,无证书校验 - Net02,客户端证书校验 - 证书校验,安卓开发:PEM证书,手机APP发送请求:读取证书+真正请求获取证书 案例: - Net03,证书校验(baidu_com.pem) - Host校验, 案例:Net04 扩展:关于【公钥校验】【证书校验】基于配置文件的形式进行开发。 1.3 关于混淆的问题 非通用的实现:frida的Hook脚本 + xposed 代码混淆: - 系统代码,永远不会被混淆 - 第三方包+自定义包,会被混淆。 解决思路:以系统包Hook+调用栈分析 => 绕过 2.服务端证书校验 2.1 校验逻辑 - 安卓开发工程需要将:证书文件(bks/p12)+ 密码 + 代码 => 集成手机APP中;每次发送网络请求:证书+密码,携带到后端API - 后端开发工程师:项目开发和部署要开发对应的校验过程。 2.2 想要绕过服务端证书校验 核心:找到集成在APP中 【xx.bks或xx.p12】+【密码】 方法1:学会编写Java代码去集成【xx.bks或xx.p12】+【密码】,反编译APK,关键字找对应的代码。 方法2:编写Hook脚本 KeyStore.load 函数(KeyStore属于系统包 -> 不会被混淆) java.security.KeyStore.load 函数 通用方案,适用于所有的APP中只要有服务端证书校验。 2.3 观看Hook脚本 - 获取密码 - 导出证书文件 2.4 案例:泡泡聊天 1.现象 - 手机直接访问 ok - 手机+Charles代理 error 2.基于通用的Hook脚本测试 密码:111111 证书:paopao-1711028290196.bks 3.集成Charles中,实现正常抓包 - 将手机中的bks证书获取到电脑 - 将bks格式转换为p12格式【工具】 - p12集成charles 4.基于Python发送请求 - 基于requests-pkcs12模块 - 基于requests模块+PEM证书(pkcs12->pem) - 基于openssl >>>openssl pkcs12 -in paopao-client.p12 -out paopao.pem -nodes -passin "pass:111111" - 发送请求 2.5 案例:美之图
__END__