记一次安卓测试多限制绕过

admin 2023年6月25日09:02:18评论53 views字数 3884阅读12分56秒阅读模式

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的方法

  1. function classloader() {
  2. Java.perform(function(){
  3. var StubApp = Java.use("com.xxxxxxxxx");
  4. StubApp.attachBaseContext.implementation = function (context) {
  5. var result = this.attachBaseContext(context);
  6. var classLoader = context.getClassLoader();
  7. Java.classFactory.loader = classLoader;
  8. classloadermain1();
  9. return result;
  10. };
  11. });
  12. }

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将密钥固定

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

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

记一次安卓测试多限制绕过

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

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

记一次安卓测试多限制绕过

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

  1. function AEScall(aeskey) {
  2. Java.perform(function(){
  3. let AESDD = Java.use("com.xxxxx.xxxxxxx");
  4. let decrypt = AESDD.AES_decrypt('密文',aeskey);
  5. let encrypt = AESDD.AES_encrypt('明文',aeskey);
  6. console.log('AES decrypt value is ' + decrypt);
  7. console.log('AES encrypt value is' + encrypt);
  8. });
  9. }

记一次安卓测试多限制绕过

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

记一次安卓测试多限制绕过

签名校验绕过

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

记一次安卓测试多限制绕过

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

记一次安卓测试多限制绕过

修改完数据重新加密回去

记一次安卓测试多限制绕过

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

记一次安卓测试多限制绕过

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

  1. function heihei() {
  2. var result = null;
  3. Java.perform(function () {
  4. Java.choose("com.xxxxxxxxxx",{ //要hook的类
  5. onMatch:function(instance){
  6. result=instance.getDesign(); //要hook的方法
  7. },
  8. onComplete:function(){
  9. console.log("result: " + result);
  10. }
  11. });
  12. })
  13. }

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

记一次安卓测试多限制绕过

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

  1. function heihei() {
  2. var result = null;
  3. Java.perform(function () {
  4. var targetclass = Java.use("com.xxxxxx");
  5. var newobj = targetclass.$new();
  6. result = newobj.getUniqueNumber();
  7. console.log("TIME_DESIGN is : " + result);
  8. return ret;
  9. })
  10. }

直接hook调用即可获取一个

记一次安卓测试多限制绕过

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

记一次安卓测试多限制绕过

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

原文始发于微信公众号(Web安全工具库):记一次安卓测试多限制绕过

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年6月25日09:02:18
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   记一次安卓测试多限制绕过https://cn-sec.com/archives/1830954.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息