DDCTF2019官方Write Up——Android篇

栏目: 编程工具 · 发布时间: 5年前

内容简介:作者:bin233 唐山师范学院/大四/DDCTF2019 Android方向 TOP2第三届DDCTF高校闯关赛鸣锣开战,DDCTF是滴滴针对国内高校学生举办的网络安全技术竞技赛,由滴滴出行安全产品与技术部顶级安全专家出题,已成功举办两届。在过去两年,共有一万余名高校同学参加了挑战,其中部分优胜选手选择加入滴滴,参与到了解决出行领域安全问题的挑战中。通过这样的比赛,我们希望挖掘并培养更多的国际化创新型网络安全人才,共同守护亿万用户的出行安全。

DDCTF2019官方Write Up——Android篇

作者:bin233 唐山师范学院/大四/DDCTF2019 Android方向 TOP2

第三届DDCTF高校闯关赛鸣锣开战,DDCTF是滴滴针对国内高校学生举办的网络安全技术竞技赛,由滴滴出行安全产品与技术部顶级安全专家出题,已成功举办两届。在过去两年,共有一万余名高校同学参加了挑战,其中部分优胜选手选择加入滴滴,参与到了解决出行领域安全问题的挑战中。通过这样的比赛,我们希望挖掘并培养更多的国际化创新型网络安全人才,共同守护亿万用户的出行安全。

Android第一题:Breaking LEM

首先将apk拖入JEB进行反编译,来到入口类并找到点击事件函数。

DDCTF2019官方Write Up——Android篇

观察到 Java 层只负责传递输入内容到native层,因此直接分析so文件的Java_com_didictf_guesskey2019lorenz_MainActivity_stringFromJNI函数即可。

DDCTF2019官方Write Up——Android篇

该函数首先会将输入内容与字符串”ddctf-android-lorenz-“比较,如果输入长度不足则直接失败,否则将进行截断操作(如输入ddctf-android-lorenz-XXX将截断为XXX)。

DDCTF2019官方Write Up——Android篇

之后会对“XXX”进行逐字符验证,字符必须属于字符串”ABCDEFGHIJKLMNOPQRSTUVWXYZ123456″。接下来就是洛伦兹加密了,在GitHub找到该算法实现,发现该算法加解密是同一个函数。因此只需要拿到密文,再让apk跑一次就是明文。不出所料,洛伦兹加密只对XXX进行加密(设加密后为YYY)。随后会对YYY进行sha256运算。

DDCTF2019官方Write Up——Android篇

分析发现是五层sha256算法进行加密,最后与shaCorrect进行比较。通过查找交叉引用便能找到shaCorrect的真实字符串(在init_array中进行初始化)。

DDCTF2019官方Write Up——Android篇

接下来的任务便是暴力破解该sha256,比赛当晚我就跑完了7位及以下所有字符串。最后等到了提示,是8位字符串并告诉了前两位字符。因此,将其补齐为八位字符串,就能保证经过洛伦兹加密后的前两位密文是不变的,只需要暴力破解后六位字符串即可。最终运气爆棚,倒着爆一分钟就出来了。

DDCTF2019官方Write Up——Android篇

将爆出来的结果拼接上ddctf-android-lorenz-,让apk自动为我们解密出明文。

Android第二题 Have Fun

首先拖入JEB发现标识符被混淆成了不可见字符,由于文件不大,直接手动重命名反混淆。

DDCTF2019官方Write Up——Android篇

很容易追踪到对输入内容第一次加密的函数,o()、p()函数会将Assets中的dex文件释放到一个隐藏文件夹中,还偷偷改了字节码。

DDCTF2019官方Write Up——Android篇

Apk使用到第一代加固保护技术,通过DEXClassLoader热加载dex文件,继续跟进dexLoader函数中。

DDCTF2019官方Write Up——Android篇

为了更快更准确的拿到dex文件,使用IDA动态调试dex,便能直接得到dex文件路径以及即将被加载的dex文件(直接从assets中拿到的dex文件算法是错误的)。

DDCTF2019官方Write Up——Android篇

正确的算法实现如下:

DDCTF2019官方Write Up——Android篇

接下来程序会删除该dex文件,最后调用so层函数。So文件进行了section加密,但直接静态分析就够了。从JNI_Onload中得到动态注册的三元组,并找到具体函数位置。

DDCTF2019官方Write Up——Android篇

DDCTF2019官方Write Up——Android篇

程序会对输入内容进行16进制转换并与内存中的固定数据进行比较,该数据如下图所示。

DDCTF2019官方Write Up——Android篇

解题脚本如下:

DDCTF2019官方Write Up——Android篇

Android第三题 不一样的Service

本题使用了控制流平坦化,画面实在是太美,强行带混淆调试。首先JAVA层会开启一个service参与输入内容的验证,没有什么关键逻辑,重点关注so层。从JNI_OnLoad找到动态注册的函数如下:

很容易发现如下反调试检测的函数,这里先不去关心。

DDCTF2019官方Write Up——Android篇

DDCTF2019官方Write Up——Android篇

DDCTF2019官方Write Up——Android篇

接下来留意到Parcel的处理函数,创建结构体方便后续分析,动态调试中重点关注readString的调用。

DDCTF2019官方Write Up——Android篇

单步跟踪发现如下函数会使用到readString函数(偏移0x1DB50)。

DDCTF2019官方Write Up——Android篇

DDCTF2019官方Write Up——Android篇

跟到如上图位置(偏移0x10458),终于拿到java层输入的内容,接着进入到sendInput1函数(偏移0x1B0D4)。

DDCTF2019官方Write Up——Android篇

这里会发现程序使用socket将输入内容发送了出去,接着进入recvResult函数(偏移0x1470C)。

DDCTF2019官方Write Up——Android篇

发现recv的数据竟然与send的数据不同,而且调试多次发现每次recv到的内容还都不一样,暂且放下该问题。接着接收的数据进行分析,程序会将该内容与固定的内存数据(称之为enFlag)进行比较(偏移0x8540)。

DDCTF2019官方Write Up——Android篇

DDCTF2019官方Write Up——Android篇

后来想到还有一个service进程,开始调试service进程。跟踪到validate函数,发现如果输入长度为32位就会返回dd字符串(并且在主进程也有对recv的结果是否为ddd的验证,否则都不会接收到那个奇怪的内容)。

DDCTF2019官方Write Up——Android篇

第一次加密操作:单步慢慢跟进很容易发现,这里使用 python 实现如下:

DDCTF2019官方Write Up——Android篇

第二次加密操作:会先保存前两个元素,后面元素每两个进行异或,处理完后将刚才保存的元素放到最后。伪代码如下:

DDCTF2019官方Write Up——Android篇

DDCTF2019官方Write Up——Android篇

DDCTF2019官方Write Up——Android篇

第三次加密操作:会再与某个内存数据(称之为key)进行逐位异或,最后send出去,这就是在主线程recv的数据与send的数据不同的原因(主进程与服务进程进行socket通信,因而之前IDA只能控制主进程空间)。

将key与enFlag逐位异或就完成了一次解密,但发现最后两个元素明显不处于ASCII码表中,所以推测自己得到了一个错误的key(印证了之前recv多个不同结果的现象)。

因此先随便输入32位字符串,自行实现第一二次加密操作进行加密,然后将其与主进程recv的数据进行异或,这样就得到了多组key,必然有一个key是真实的。

将这些key继续与enFlag异或,其中一个key异或结果如下图所示。

68对应字符‘D’,而69正好是第一次加密加了下标1导致的,因此也是‘D’(不正好像DDCTF吗?可以推断出自己已经得到了正确的数据)。接下来的问题就是破解“第二次加密”了,直接无脑爆破不太现实,这里提供两种解密方式:

逆向猜解法:

可以推测最后一个元素数据是“}”,那么“第一次加密后”他就是“}”+31=156。所以只需要猜解倒数第二个元素,然后逆着异或。具体脚本如下:

def myPrint(res):
ret=[]
for i in range(32):
ret+=chr(res[i]-i)
print "".join(ret)

for j in range(160):
ispass=0
flag=[ 1 , 18 , 15 , 215 , 22 , 254 , 12 , 9 , 42 , 21 , 20 , 50 , 232 , 22 , 242 , 204 , 1 , 248 , 2 , 246 , 244 , 248 , 4 , 251 , 221 , 202 , 22 , 3 , 27 , 210 , 68 , 69 ]
flag[30]=j
flag[31]=156
for i in range(1,31):
flag[30-i] = flag[32-i]^flag[30-i]
if(flag[30-i]<33 or flag[30-i]>160):
ispass=1
break
if(ispass==0):
flag[0]=68
flag[1]=69
myPrint(flag)

DDCTF2019官方Write Up——Android篇

正向异或法:

既然我们已经看“DD”字符串了,那么后面必然是“CTF”,正向再异或一遍,具体脚本如下:

flag=[ 1 , 18 , 15 , 215 , 22 , 254 , 12 , 9 , 42 , 21 , 20 , 50 , 232 , 22 , 242 , 204 , 1 , 248 , 2 , 246 , 244 , 248 , 4 , 251 , 221 , 202 , 22 , 3 , 27 , 210 , 68 , 69 ]
tmp=[]
tmp.append(flag.pop(30))
tmp.append(flag.pop(30))
tmp+=flag

tmp[2]=ord('C')+2
tmp[3]=ord('T')+3
for i in range(0,30):
tmp[i+2]= tmp[i] ^ flag[i]
for i in range(32):
tmp[i]-=i
print "".join(map(lambda x:chr(x),tmp))

DDCTF2019官方Write Up——Android篇


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Music Recommendation and Discovery

Music Recommendation and Discovery

Òscar Celma / Springer / 2010-9-7 / USD 49.95

With so much more music available these days, traditional ways of finding music have diminished. Today radio shows are often programmed by large corporations that create playlists drawn from a limited......一起来看看 《Music Recommendation and Discovery》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

html转js在线工具
html转js在线工具

html转js在线工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具