小杂谈
搞了差不多两年移动安全发现多动手才能学到新东西,故此推出app逆向实战系列用于记录破解各个app的安全防护,以此勉励。
一些括号内词句读者自动脑补表情包,狗头保命[doge]
正文
frida反调试
将某8样本app塞进jadx查壳,数据多的亚比,恭喜没加壳,前进第一步!(加壳了就没有后面doge)
打开app查看http数据包(美滋滋),看到以下数据,所有请求都统一封装了并且还有加密、签名。(日瑞玛,退钱!)
要弄掉以下防护需要几个步骤:(柯南思考)
1、sign应该是签名,需要弄清和哪些参数有关
2、请求没有body和param参数,应该是加密存在hbody字段内
3、有key参数,再加上返回包返回的result猜测是AES加密
4、uuid用来防重放攻击
将数据包发到repeater看看到底是什么妖魔鬼怪。我敲,难道是传说中的防重放攻击?原地退役想法+1
找一个没有防重放攻击的,一般是静态资源类的,修改任意参数,哦豁中奖(摇头)。签名+防重放攻击,web方面的测试没戏了。
掏出我的大杀器–frida!(杰哥摘眼镜,真拿你没办法),frida直接退出,但是程序还在运行(怀疑人生)
首先出现以上问题我们得用排除法来定位原因,学习嘛总是在试错与排错中进行
1、设备原因,电脑、手机,重启大法没能解决,拿别的设备运行也是一样,排除。
2、app有hook检测,检测到直接终止进程
幸好本人平时喜欢游走在各个安全论坛,记起来一位大佬曾写过的frida反调试文章(其实是某位大哥提醒的),文章链接如下:
https://bbs.kanxue.com/thread-277034.htm、https://www.jianshu.com/p/74b7803701b6
通过结合两位大佬的代码集成hook代码成功绕过检测,so文件的检测原理后面再分析,留个坑。
绕过hook检测后就可以为所欲为了(逐渐变态)
签名
对apk反编译,定位签名方法,因为请求体里面sign是在body请求体里面,客户端生成方式一般就是使用map进行封装,所以jadx的搜索关键词:“sign”
此时有多个符合条件的类,可以通过排除法一步步缩小范围。第三方sdk可以直接忽略,一般是和包名类似的类,如果不确定可以对该类进行hook,通过多次测试后发现如下类调用了签名的方法,跟进去
从类名来看应该就是http请求的签名加密类,定义了一个sign的常量
使用map添加SIGN、this.sign键值对,sign的值来源于zdo4i1uyyhqsmvpopdtoaa方法,查看具体的签名方法继续跟进
签名方法是调用so层的接口,参数是字符串数组,使用frida直接hook看入参和返回值
function signs(){
let JNIHelper = Java.use("com.wuba.bangjob.secret.JNIHelper");
JNIHelper["zdo4i1uyyhqsmvpopdtoaa"].implementation = function (context, strArr) {
console.log('zdo4i1uyyhqsmvpopdtoaa is called' + 'strArr: ' + strArr);
let ret = this.zdo4i1uyyhqsmvpopdtoaa(context, strArr);
console.log('zdo4i1uyyhqsmvpopdtoaa ret value is ' + ret + '\n');
return ret;
};
}
入参为body的参数,也就是说签名只鉴别这几个参数,其实从前面定位的sign方法也能知道入参有哪些
对入参进行分析:
- uuid,防重放攻击
- hbody,加密后的body参数
- cmd,http请求接口
- key,可能是AES加密后的密钥
- index,暂不明确
签名接口的调用有两种方式,其一,被动调用:通过程序正常流程调用到方法,修改入参获取sign。其二,主动调用:通过frida-rpc方式对sign方法直接调用
本文只展示第一种,rpc没法调用绕过hook检测的代码(后续研究),index参数添加单引号,调用方法生成sign绕过签名校验
function asign(){ //因为rpc没法正常调用绕过hook的代码,故只能通过修改入参来获取sign,生成对应的字符串数组就好
let ZCMBusinessMgr = Java.use("com.wuba.bangjob.secret.ZCMBusinessMgr");
ZCMBusinessMgr["zdo4i1uyyhqsmvpopdtoaa"].implementation = function (strArr) {
console.log('zdo4i1uyyhqsmvpopdtoaa is called' + '\n' + 'strArr: ');
const dataString = "2425f470209f44b09b78bfae18e99694,jZvBbkCB3VYRSHFtw6yCVA==,recswitch.getsw,BSG8C+iTDpbExiNqb5uRI2bYg2kdXXFtzN8sYh5+XblZzDwloEWzvNd9NkEEwsByRgdwEn7M6GzD0TESbazzGg==,1'";
const dataArray = dataString.split(',');
console.log("call strArr" + dataArray)
let ret = this.zdo4i1uyyhqsmvpopdtoaa(dataArray);
console.log('zdo4i1uyyhqsmvpopdtoaa ret value is ' + ret +'\n');
return ret;
};
}
将sign进行替换,结果返回参数校验失败说明已经绕过签名限制,猜测后端判定index是个int类型所以传入’返回错误提示。
解密
签名部分搞定了接下来就是请求体和返回包中的数据解密,还是在 EncrptyInterceptorModel类中
hbody
function hbody(){
let JNIHelper = Java.use("com.wuba.bangjob.secret.JNIHelper");
JNIHelper["unz5pzfix1jfy3hqyuyvqq"].implementation = function (context, str, str2) {
console.log('请求参数为: ' + str2); //str为uuid
let ret = this.unz5pzfix1jfy3hqyuyvqq(context, str, str2);
console.log('unz5pzfix1jfy3hqyuyvqq ret value is ' + ret);
return ret;
};
}
result
通过多次调试定位到nuvbtdxcz2tnbyzxugrfxg方法就是对返回包的result参数进行解密的
因为返回的是ASCII码,所以需要进行多次转换才能输出可见字符,hook代码如下:
function result(){
let EncrptyInterceptorModel = Java.use("com.wuba.client.framework.rx.retrofit.secure.EncrptyInterceptorModel");
EncrptyInterceptorModel["nuvbtdxcz2tnbyzxugrfxg"].overload('java.lang.String').implementation = function (str) {
//console.log('返回的加密result: ' + str);
let ret = this.nuvbtdxcz2tnbyzxugrfxg(str);
//console.log('nuvbtdxcz2tnbyzxugrfxg ret value is ' + ret);
var decodedStr = decodeURIComponent(escape(bytesToString(ret)));
var urlstr = decodeURI(decodedStr);
var unistr = unescape(urlstr.replace(/\\u/g, "%u"));
console.log("解密后的数据为: "+ unistr );
return ret;
};
}
防重放攻击
防重放攻击解决方案看:https://blog.csdn.net/weixin_42806958/article/details/118971184
该app使用的是第二种方式,
1、客户端每次发起请求,都需要携带uuid(要求唯一即可)
2、服务端接收到请求后,先校验用户携带的uuid是否存在,不存在:将uuid存储到数据库或缓存服务器中,存在:既认为是重播攻击
总结:实现简单有效,但随着请求量越多,存储的数据会越来越大。
没有特别的生成方式,直接导入uuid库生成一个uuid
原本已经提示繁忙
修改uuid后也是提示的繁忙(丢撵了),而且是部分接口才有这种情况,经过多次测试数据包隔一段时间可以再次请求,如果是uuid决定的重放,使用上述方式就可以绕过了,但是目前不成功。关键点应该是后端要么就是风控,不好判断。关于key参数的值来源于so文件里面,输出的是加密后的key,可能是动态key也可能是带有IV的加密模式。
总结
虽然最后一步暂不明确是否是uuid决定重放,也不知道是否是防重放手段,但是从该app上学到了防重放的一些防护手段。且从整个流程上来看,想对web层进行测试只需hook入参就好,后续的参数加密、签名等机制会自动运行,并不需要逐一绕过。frida玩得好,国家饭吃得饱,打完收工。