记一次安卓测试多限制绕过
2023-6-25 08:2:41 Author: Web安全工具库(查看原文) 阅读量:25 收藏

app使用某加固企业版,几乎所有方法的实现都在so层中,hook目标方法时存在Didn't find class错误,上一篇文章中已经讲过了root检测绕过和抓包相关的知识点在这篇文章中相关内容不在陈述。

加密算法寻找

脱壳后对代码进行分析,定位到获取密钥的方法,先直接大致看了看代码中aes sm4 rsa等方法的数量,发现实在是太多了,仅aes算法相关的类就有十几个了,一个一个hook挺费时间的,而且实际处理的方法还有可能在so层中,于是这里根据请求包字段{"data":"密文"}的特征"data"去搜索

虽然有加密相关的,但是后续测试发现完全没关系。破坏请求包加密内容再次搜索

也没找到,直接搜索AES,SM4,DES等加密的相关关键词找到一些代码片段,后面hook发现加密竟然都在so层中处理,我们找到相关代码CB

hook后确认了数据加密的方式AES

通过查看m20方法的引用也定位到了获取密钥的方法AESKeyget

classloader问题解决

编写脚本hook时发现这些方法全都报错,显示找不到该类

这是由于加壳app运行起来的时候,会加载classloader,再去运行里面的代码,这时候里面的代码会因为classloader发生了变化,所以脱壳后看到的函数名并不代表运行起来之后的函数名,因为它运行起来之后有一个加密和解密的过程,如果运行后函数名发生了变化,这个时候再去hook就会产生Didn't find class的错误,此时需要拿到加载应用本身dex的classloader,然后通过这个classloader去hook被加固的类。

使用jadx打开apk,找到加固的classloader

重新编写利用脚本,通过加固的classloader去加载要hook的方法

function classloader() {
Java.perform(function(){
var StubApp = Java.use("com.xxxxxxxxx");
StubApp.attachBaseContext.implementation = function (context) {
var result = this.attachBaseContext(context);
var classLoader = context.getClassLoader();
Java.classFactory.loader = classLoader;
classloadermain1();
return result;
};
});
}

function classloadermain1() {
Java.perform(function(){
let HttpParams = Java.classFactory.use("com.xxxx");
HttpParams["AESKeyget"].implementation = function () {
console.log('------------------------------------------------------------');
let ret = this.AESKeyget();
console.log('AESKey ret value is ' + ret);
console.log('------------------------------------------------------------');
return ret;
};
});
}

成功hook到目标方法,但是发现该方法获取的密钥竟然是加密后的(通过某种加密后再以base64形式输出的密文),且每次重启app密钥会发生变化

加密算法定位

在分析代码时,注意到了另一个类中某方法同样是获取该密钥的,且类名包含SharedPreference字段,这说明大概率也会存储在安卓本地,SharedPreferences是安卓的一种存储方式,该存储方式通常用来存储应用的配置信息,保存方式基于XML文件存储的key-value键值对数据,一般作为数据存储的一种补充。SharedPreferences对象本身只能获取数据而不支持存储和修改,存储修改是通过SharedPreferences.edit()获取的内部接口Editor对象实现。SharedPreferences本身是一个接口,程序无法直接创建SharedPreferences实例,只能通过Context提供的getSharedPreferences(String name, intmode)方法来获取实例。存储路径为:/data/data/<package name>/shared_prefs目录下。回到代码,其中有定义BASE_AES_KEY = "aes_key";

那么我们直接在手机/data/data/应用包名/shared_prefs目录下搜索aes_key不就可以了

成功获取该字段值,发现该字段是base64编码的,解码后还是那个加密后的密钥,那没事了只能转向其他方向。通过常规方式如右键查找引用,发现都没有被引用。继续分析CB的引用(当然也可以直接搜索关键词一个一个试),最终找到对明文进行加密和对密文解密的方法,该方法核心处理在so层中,且传入的key正是之前hook的那个AESKeyget方法的值,该方法将明文数据和加密后的密钥传入so层中,先对密钥解密然后对数据进行加密

主动调用实现数据加解密

这里我们没办法通过密钥对数据进行解密,但是我们可以主动调用该方法,首先通过hook将密钥固定

let Sl = Java.classFactory.use("com.xxxx.xxxx");
Sl["AESKeyget"].implementation = function () {
let ret = this.AESKeyget();
return "固定的密钥";
};

然后在burp中复制我们要解密的数据

通过通过主动调用解密方法去解密

function AEScall(aeskey) {
Java.perform(function(){
let AESDD = Java.use("com.xxxxx.xxxxxxx");
let decrypt = AESDD.AES_decrypt('密文',aeskey);
console.log('AES decrypt value is ' + decrypt);
});
}

修改请求包数据后,通过主动调用加密算法再加密回去

function AEScall(aeskey) {
Java.perform(function(){
let AESDD = Java.use("com.xxxxx.xxxxxxx");
let decrypt = AESDD.AES_decrypt('密文',aeskey);
let encrypt = AESDD.AES_encrypt('明文',aeskey);
console.log('AES decrypt value is ' + decrypt);
console.log('AES encrypt value is' + encrypt);
});
}

获取密文后在数据包中执行即可,返回包内容同理直接主动调用即可解密

签名校验绕过

实际上该app登录的接口使用的加密方式和登录后其他模块的加密方式并不相同,该app登录模块数据包中存在hash签名校验

经过对代码的分析找到加密方法并非本文开头遇到的AES,而是使用的SM4加密算法,通过hook对应的方法找到密钥和iv,对数据进行解密

修改完数据重新加密回去

在数据包中不能直接执行,因为还有hash的校验,直接右键加解密方法查找用例,看到只有一处使用了该方法,在这里也找到了hash对应的方法其实就是先对data进行md5加密,然后再base64输出,重新计算完签名执行,返回包数据解密后竟然显示数据重放,原来请求头中还有一个参数TIME_DESIGN: xxxxxxxxxx,通过搜索关键词TIME_DESIGN找到获取该签名的代码

这里思路是直接主动调用获取的一个新的,从代码可以知道该方法非静态方法,可以通过Java.choose 从内存中找到实例化好的类进行调用

function heihei() {
var result = null;
Java.perform(function () {
Java.choose("com.xxxxxxxxxx",{ //要hook的类
onMatch:function(instance){
result=instance.getDesign(); //要hook的方法
},
onComplete:function(){
console.log("result: " + result);
}
});
})
}

但是写好代码后发现一运行直接崩溃

重写利用代码,通过实例化 .$new() 对象的方式再次尝试主动调用成功

function heihei() {
var result = null;
Java.perform(function () {
var targetclass = Java.use("com.xxxxxx");
var newobj = targetclass.$new();
result = newobj.getUniqueNumber();
console.log("TIME_DESIGN is : " + result);
return ret;
})
}

直接hook调用即可获取一个

重新添加上TIME_DESIGN后再次执行成功,对返回包数据解密,未提示加解密错误和重放攻击

本文作者:白Einzz, 转载请注明来自FreeBuf.COM

1、关注公众号逆向有你回复:送书666,获取每日送书抽奖码
2、每日抽奖送书规则,个人微信:ivu123ivu

· 今 日 送 书 ·

本书是Python全栈开发系列的第3册,共分为7章,将重点讲解数据分析的相关知识点,即数据搜集、数据清洗、数据分析和数据可视化,并搭配近400个示例代码,理论知识与实战开发并重,可以帮助读者快速、深入地理解和应用相关技术。

本书可以作为广大计算机软件技术人员的参考用书,也可以作为大中专,以及高等院校计算机科学与技术、自动化、软件工程、网络工程、人工智能和信息管理与信息系统等专业的教学参考用书。


文章来源: http://mp.weixin.qq.com/s?__biz=MzI4MDQ5MjY1Mg==&mid=2247509598&idx=1&sn=430a0d7b9d9e8288a2a94f7fe2d1d928&chksm=ebb5495ddcc2c04bd8ec5f26c586a212470c768eb4db6cd1c68545fe2d54e6ae29ca6377517b#rd
如有侵权请联系:admin#unsafe.sh