freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

Frida与Android CTF
TideSec 2022-07-22 13:23:31 163950
所属地 山东省

Frida与Android CTF

前言

最近练习了下CTF中Android相关题目,发现三题分别考察了三个点: 1、Frida Java Hook与静态函数的主动调用 2、Frida遍历ClassLoader从而Hook动态加载的Ddex的函数 3、Frida Native Hook去反调试

第一题 Frida静态函数的主动调用

首先安装第一款APP运行发现提示说需要输入纯数字,另外当输入错误时会调试”请继续加油“。-w288-w288将该APK文件拖入jadx中进行静态分析发现,输入的用户名和密码的字符串拼接作为参数,然后传入vvvv方法。-w1039-w1039然后查看VVVV函数,发现input参数限制长度为5位,并且经过eeeee方法得到的结果和p一致,需要同时满足这两个条件,才可以获得flag-w582-w582继续分析eeeee方法,发现sssss方法在获取字符串的Sha-1值,ccccc方法将sha-1的摘要转化成了16进制字符串的形式,该算法不可逆。-w625-w625通过对以上分析得知,想要获取flag有两种思路: 1、根据flag长度为5位数字,进行爆破 2、通过frida hook函数返回值强制返回true 但如果使用第二种方法,发现该check失效,输入任何五位数字都可提示成功,也就失去了题目的意义。接下来我们编写frida脚本进行第一种方法爆破解题。 因反编译的结果不能完全相信,首先使用Objection分析,因两段逻辑判断,所以在APP中输入5位纯数字,发现objection中VVVV调用了eeeee,因此验证了Jadx反编译的内容确实没错。-w1072-w1072对com.kanxue.pediy1.VVVV类中的方法eeeee进行主动调用。 编写frida脚本,爆破拿到flag为66888-w1056-w1056
var CONTEXT = null;

function getObjClassName(obj) {
if (!jclazz) {
var jclazz = Java.use("java.lang.Class");
}
if (!jobj) {
var jobj = Java.use("java.lang.Object");
}
return jclazz.getName.call(jobj.getClass.call(obj));
}

function hookReturn() {
Java.perform(function () {
Java.use("com.kanxue.pediy1.VVVVV").VVVV.implementation = function (context, str) {
var result = this.VVVV(context, str)
console.log("context,str,result => ", context, str, result);
console.log("context className is => ", getObjClassName(context));
CONTEXT = context;
return true;
}
})
}
function invoke() {
Java.perform(function () {
//console.log("CONTEXT IS => ",CONTEXT)
var MainActivity = null;
Java.choose("com.kanxue.pediy1.MainActivity", {
onMatch: function (instance) {
MainActivity = instance;
},
onComplete: function () { }
})
var CONTEXT2 = Java.use("com.kanxue.pediy1.MainActivity$1").$new(MainActivity);
var javaString = Java.use("java.lang.String").$new("12345");
for (var x = 0; x < (99999 + 1); x++) {
var result = Java.use("com.kanxue.pediy1.VVVVV").VVVV(CONTEXT2, String(x));
console.log("now x is => ", String(x))
if (result) {
console.log("found result is => ", String(x))
break;
}
}
})

}

function main() {
hookReturn()
}
验证成功-w286-w286

第二题 Frida Hook动态加载Dex

通过Jadx分析发现了Dex的动态加载以及Native函数的引入。-w1388-w1388动态加载dex并调用动态加载的dex中的VVVV函数,首先解压该APK包,使用Jadx打开classes.dex文件发现本题算法和第一题一样-w1207-w1207所以可以编写frida脚本,通过枚举ClassLoader选择正确的classLoader再对函数进行主动调用爆破得到flag。-w800-w800
function invoke2() {
Java.perform(function () {
Java.enumerateClassLoaders({
onMatch: function (loader) {
try {
if (loader.findClass("com.kanxue.pediy1.VVVVV")) {
console.log("Successfully found loader")
console.log(loader);
Java.classFactory.loader = loader;
}
}
catch (error) {
console.log("find error:" + error)
}
},
onComplete: function () {
console.log("end1")
}
})
var javaString = Java.use("java.lang.String").$new("12345");
for (var x = 0; x < (99999 + 1); x++) {
var result = Java.use("com.kanxue.pediy1.VVVVV").VVVV(String(x));          
console.log("now x is => ", String(x))
if (result) {
console.log("found result is => ", String(x))
break;
}
}
})
}

function main() {

}
setImmediate(main)
不过测试发现flag不对-w290-w290继续分析发现还对stringFromJNI还做了一层处理,这个函数是native函数,所以要进行两层主动调用。-w946-w946
function invoke2() {
Java.perform(function () {
var MainActivity = null;
Java.choose("com.kanxue.pediy1.MainActivity",{
onMatch:function(instance){
MainActivity = instance;
},
onComplete:function(){}
})
var loader1 = null;
var loader2 = null;
Java.enumerateClassLoaders({
onMatch: function (loader) {
try {
if (loader.findClass("com.kanxue.pediy1.VVVVV")) {
console.log("Successfully found loader")
console.log(loader);
loader2 = loader;
Java.classFactory.loader = loader2;
}else if(loader.findClass("com.kanxue.pediy1.MainActivity")){console.log("Successfully found loader")
console.log(loader);
loader1 = loader;
}else{
}
}
catch (error) {
console.log("find error:" + error)
}
},
onComplete: function () {
console.log("end1")
}
})
var javaString = Java.use("java.lang.String").$new("12345");
for (var x = 0; x < (99999 + 1); x++) {
var result1 = MainActivity.stringFromJNI(String(100000 - x));
var result2 = Java.use("com.kanxue.pediy1.VVVVV").VVVV(String(result1));          
console.log("now x is => ", String(x))
if (result2) {
console.log("found result2 is => ", String(100000 - x))
break;
}
}
})
}
function main() {
}
setImmediate(main)
测试发现flag66998时正确-w291-w291当然也可以使用IDA查看so文件,分析stringFromJNI函数,发现将输入的数字字符串转换为int然后加了一进行返回,所以正确的flag应该为66999 - 1 = 66998-w921-w921

第三题 Native层去反调试

该题在第二题的基础上加入了native层对Frida的反调试。 反调试逻辑: 通过一直循环创建Socket连接,遍历端口,检查端口是否被占用,收到“REJECT”时,说明frida-server正在运行,然后直接kill掉进程。 测试也发现当通过frida进行hook时,APP会崩掉。-w1052-w1052绕过思路: 可以通过将系统中的kill进行替换,从而达到程序无法执行kill命令,保持APP正常运行来绕过反调试。编写相关脚本如下:
frida -U -f com.kanxue.pediy1 -l /Users/tale/Downloads/20220317/111.js  --no-pause
-w951-w951
function replaceKill(){
var kill_addr = Module.findExportByName("libc.so", "kill");
Interceptor.replace(kill_addr,new NativeCallback(function(arg0,arg1){
console.log("arg0=> ",arg0)
console.log("arg1=> ",arg1)
},"int",['int','int']))
}

function main() {
replaceKill();
}
发现frida-server和APP正常运行,从而成功绕过了对frida的反调试。 接下来将反调试代码加入到题目二中进行爆破可成功得到该题目flag99998。-w932-w932验证成功-w290-w290

总结

这三道题主要逻辑就是输入flag验证对错,并且flag的求解都是通过爆破长度为5位的数字,程序本身利用hash加密后与程序已有密文进行对比,总体来说可以使用Frida爆破一把梭。

参考资料

https://bbs.pediy.com/thread-260550.htm

# CTF
免责声明
1.一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。
2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。
3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
本文为 TideSec 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
TideSec LV.7
Tide安全团队 http://www.TideSec.com
  • 112 文章数
  • 121 关注者
某次攻防演练中对任意文件读取漏洞的利用
2023-07-05
记一次对某企业的渗透测试
2023-07-05
用户名密码加密的页面爆破学习
2023-07-05
文章目录