easy-app
题目描述
2020太湖杯物联网安全大赛easy-app,这题在网上的题解很少且不够详细。故分享下。
下载附件安装后打开
随便输入字符串的话,弹窗error
把apk拖入jeb去分析
当我们点击check按钮的时候,程序会调用check方法,参数即是我们的输入字符串,check方法的实现在native-lib.so库中
对其进行解包操作,获得该so库
然后将其拖入到ida中,找到Java_com_example_myapplication_MainActivity_check函数。
但代码量太大了,为了增强对它的理解,这里我们采用ida动态调试的方法。
题目分析
上传ida的服务端,并对它赋予可执行权限,然将其运行起来
1 2 3 4 5 6 7 8 9 10 | >adb push android_server / data / local / tmp
>adb push android_server64 / data / local / tmp
[ 1 ] 11122
taimen: / data / local / tmp
Listening on 0.0 . 0.0 : 23946. ..
|
进行端口转发
1 | >adb forward tcp: 23946 tcp: 23946
|
手机端打开应用
ida进行远程调试配置
选中要调试的进程
点击ok,成功调试起来了,
在Modules模块窗口,通过ctal+f寻找到native-lib.so
双击这个so,即可查看so库中的函数
找到目标函数Java_com_example_myapplication_MainActivity_check(),继续双击这个函数,即可查看对应的汇编代码,
点击tab键对其进行反编译为伪代码
参数a1其实为_JNIEnv *env,我们导入jni.h文件,然后改变下它的数据类型
对获取v3(即输入的字符串开始调试)的地址处下断点
通过静态分析,可以得知我们要输入的字符串长度为38位,且前5位为"flag{",最后一位为“}”
而中间是32位是需要我们去逆向分析得到的,这里我们我们首先先自己随便构造用来调试。
1 2 3 4 | flag{ / / 5
0123456789 / / 10
abcdefghijklmnopqrstuv / / 22
} / / 1
|
这道题的代码量很大,完全静态分析的话回很耗时间。
这里用ida动态调试的方式。
起了个大怪,重启debug一次,发现原来是输入的时候少数如了个"d",额...麻了.
重新来下
首先将接收到的字符串,然后将其前5个字符存放到栈地址上。
然后比较输入的前五个字符是否和"flag{"一致。
然后比较输入的最后一个字符是否和"}"一致。
然后再去判断输入的字符串的长度是否为38即0x26
接下来经过了中间的32位字符串经过高位替换的函数7008D55D50()处理
1 2 3 4 5 6 | 处理之前
0123456789abcdef 30313233343536373839616263646566
ghijklmnopqrstuv 6768696a6b6c6d6e6f70717273747576
处理之后
60616263646566676879717273747576 - - - - - > `abcdefghyqrstuv
3738393a3b3c3d3e6f30616263646566 - - - - - > 789 :;< = >o0abcdef
|
然后再接下来就是个变异的tea算法处理
处理之后的字符串后面又经过了变异的base64的函数处理。(不小心直接run结束了,下次记得多打些断点。)
然后我们跟进去base64函数
通过动态调式可以得知这个base64函数并不是标准的base64算法,这次apk中将table表给替换成了"abcdefghijklmnopqrstuvwxyz!@#$%^&*()ABCDEFGHIJKLMNOPQRSTUVWXYZ+/",并且每加密三个明文得到4个密文,有将前3三个密文依次左移2个字符。
下面为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | flag{ 0123456789abcdefghijklmnopqrstuv }
71 FA BD 0b1110001 0b11111010 0b10111101
01110001 11111010 10111101
011100 01 1111 1010 10 111101
011100 011111 101010 111101
0x1c 0x1f 0x2a 0x3d
abcdefghijklmnopqrstuvwxyz!@
hex ( ord ( str [ 0x1c ]))
'0x23'
hex ( ord ( str [ 0x1f ]))
'0x5e'
>>> hex ( ord ( str [ 0x2a ]))
'0x47'
hex ( ord ( str [ 0x3d ]))
'0x5a'
正常应该加密成这样
0x235e47 5a
123 - > 231
但实际加密后是这样的
0x5e4723 5A
即这里最后一位是不变的,前三位发现都依次进行了循环右移 2 位
|
加密后的结果如下
1 2 3 | 5E 47 23 5A 68 67 62 2A 79 76 4E 4C 68 29 77 6C ^G
77 43 63 6F 70 64 24 66 40 73 74 4B 6D 74 24 62 wCcopd$f@stKmt$b
77 7A 46 71 58 45 4A 2F 59 51 76 3D 00 00 00 00 wzFqXEJ / YQv = ....
|
结果如果和"e)n*pNe%PQy!^oS(@HtkUu+Cd$#hmmK&ieytiWwYkIA="相等的话才会输出Congratulations,you found it!!!
所以这里需要我们进行逆向。
首先需要将"e)n*pNe%PQy!^oS(@HtkUu+Cd$#hmmK&ieytiWwYkIA="以4字节为单位,将每个单位的前三个字符依次右移2位。然后将其再进行base64解密,在解密的时候需要替换table为
1 | abcdefghijklmnopqrstuvwxyz!@
|
然后将base64解密后的结果再依次进行变异的tea算法进行解密。
tea算法解密后还需要将结果以16字节为单位进行高位移换操作,便是最终的flag了。
第一步
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | base64_out = "e)n*pNe%PQy!^oS(@HtkUu+Cd$#hmmK&ieytiWwYkIA="
print ( len (base64_out))
base64_ok = ""
temp = ""
for i in range ( len (base64_out)):
if i % 4 = = 0 :
temp = base64_out[i:i + 4 ]
base64_ok + = temp[ 2 ]
base64_ok + = temp[ 0 ]
base64_ok + = temp[ 1 ]
base64_ok + = temp[ 3 ]
else :
print ("")
print (base64_ok)
|
base64解密
1 2 3 4 5 6 7 8 9 10 11 12 13 | import base64
import string
import binascii
base64_ok = "ne)*epN%yPQ!S^o(t@Hk+UuC#d$hKmm&yietwiWYAkI="
outtab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
intab = "abcdefghijklmnopqrstuvwxyz!@#$%^&*()ABCDEFGHIJKLMNOPQRSTUVWXYZ+/"
teaout = (base64.b64decode(base64_ok.translate(base64_ok.maketrans(intab,outtab))))
print (teaout)
print ( type (teaout))
tea_out = binascii.hexlify(teaout).decode( 'utf-8' )
print (tea_out)
|
然后在进行tea解密
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | void decrypt(unsigned int * v, unsigned int * k)
{
unsigned int v0 = v[ 0 ], v1 = v[ 1 ], sum = 0xC6EF3720 , i;
unsigned int delta = 0x9e3779b9 ;
unsigned int k0 = k[ 0 ], k1 = k[ 1 ], k2 = k[ 2 ], k3 = k[ 3 ];
for (i = 0 ; i < 32 ; i + + )
{
v1 - = ((v0 << 4 ) + k2) ^ (v0 + sum ) ^ ((v0 >> 5 ) + k3);
v0 - = ((v1 << 4 ) + k0) ^ (v1 + sum ) ^ ((v1 >> 5 ) + k1);
sum - = delta;
}
v[ 0 ] = v0;
v[ 1 ] = v1;
}
int main(){
int i = 0 ;
unsigned int v[ 2 ] = { 0 , 0 }, k[ 4 ] = { 66 , 55 , 44 , 33 };
char tea_out[ 64 ] = "\x34\x48\xe1\x10\xfc\x5e\x63\x3d\x1a\xd9\xf3\xa2\x4d\xba\xca\xfb\x85\x26\x70\x37\x47\xb8\xc3\x20\x60\x81\x13\x58\x8e\xbc\x90\xab" ;
for (i = 0 ;i<strlen(tea_out);i + = 8 ){
unsigned int * v = (unsigned int * )&tea_out[i];
/ / printf( "%x %x\n" , v[ 0 ], v[ 1 ]);
decrypt(v, k);
printf( "%x %x\n" , v[ 0 ], v[ 1 ]);
}
return 0 ;
}
/ *
36346065 38673534
65353537 62303531
61333232 34363160
63316239 35356761
即 6560343634356738373535653135306232323361603136343962316361673535
* /
|
最后在进行高位替换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | tea = "6560343634356738373535653135306232323361603136343962316361673535"
tea1 = tea[:: 2 ]
tea2 = tea[ 1 :: 2 ]
print (tea1)
print (tea2)
temp = tea1[ 16 :] + tea1[: 16 ]
print (temp)
flag = ""
for i in range ( len (temp)):
flag + = temp[i] + tea2[i]
print (flag)
print (bytes.fromhex(flag))
|
然后再拼接上面的"flag{"和"}"就是正确的输入
1 | flag{ 504fd5787e5eae02bb3101f4921c175e }
|
参考
2020太湖杯物联网安全大赛easy-app的wp_sln_1550的博客-CSDN博客
2020太湖杯Reverse Writeup及复盘-安全客 - 安全资讯平台 (anquanke.com)
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!