CTF-Ogeek之安卓逆向,mblockchain
本帖最后由 qdam 于 2019-8-26 10:48 编辑第一次在比赛中挑战安卓逆向题,神奇地做了出来,在此发帖纪念一下。
附件链接 https://pan.baidu.com/s/173IyPx85Y9aeoJJerfWr3Q , 提取码 ccem
改后缀为zip解压,dex2jar,然后上jd-gui,进行源码分析。
首先看到关键的验证部分,也就是checkFlag函数,然后我们进去看一下函数逻辑。
public static boolean checkFlag(String paramString1, String paramString2)
throws Exception
{
paramString1 = hash(paramString1.getBytes());
paramString1 = hash(new byte[] { paramString1, paramString1[(paramString1.length / 2)], paramString1[(paramString1.length - 1)] });
paramString2 = paramString2.getBytes();
int i = 0;
while (i < 10)
{
paramString2 = encrypt(paramString2, paramString1);
paramString1 = hash(paramString1);
i += 1;
}
return toHex(paramString2).equals("74f0b165db8a628716b53a9d4f6405980db2f833afa1ed5eeb4304c5220bdc0b541f857a7348074b2a7775d691e71b490402621e8a53bad4cf7ad4fcc15f20a8066e087fc1b2ffb21c27463b5737e34738a6244e1630d8fa1bf4f38b7e71d707425c8225f240f4bd2b03d6c2471e900b75154eb6f9dfbdf5a4eca9de5163f9b3ee82959f166924e8ad5f1d744c51416a1db89638bb4d1411aa1b1307d88c1fb5");
}
可以看到首先对paramString1进行经行了哈希(MD5),然后是10轮加密,最后paramString2要等于一长串字符。
这里hash()和toHex()函数功能就如名字一样,就不分析了。主要是对encrypt()函数的分析。
再来具体看一下源码。public static byte[] encrypt(byte[] paramArrayOfByte1, byte[] paramArrayOfByte2)
throws Exception
{
Object localObject = new SecretKeySpec(paramArrayOfByte2, "AES");
paramArrayOfByte2 = Cipher.getInstance("AES/ECB/PKCS5Padding");
paramArrayOfByte2.init(1, (Key)localObject);
localObject = new ByteArrayOutputStream();
paramArrayOfByte2 = new CipherOutputStream((OutputStream)localObject, paramArrayOfByte2);
paramArrayOfByte2.write(paramArrayOfByte1);
paramArrayOfByte2.flush();
paramArrayOfByte2.close();
return (B)((ByteArrayOutputStream)localObject).toByteArray();
}
这里出现了很对没有见到过的函数,但是不要紧,我们可以从函数名字去猜函数的功能,有些不好理解的我们可以去查阅android手册。
我们可以大致猜测出这里是进行了AES加密,而且是把paramArrayOfByte2作为密钥,paramArrayOfByte1作为明文加密。实际的情况也就是这样。
所以总的来说就是经行了10轮AES加密,每次的密钥是上一个密钥的MD5字符串(刚好是16个字符)。问题来了,怎么解密呢,我们知道MD5是单向散列的,不可逆的,只能顺着来。
其实在checkFlag中的前几步操作已经给了我们提示,paramString1 = hash(new byte[] { paramString1, paramString1[(paramString1.length / 2)], paramString1[(paramString1.length - 1)] });
这里只对三个字符求哈希值,咦,三个字符,好像很少,可以爆破哎(滑稽)。
然后我们就可以爆破这三个字符,把10个密钥求出来,再反解,看最初的明文有没有“flag”字符即可。
脚本如下,写的有点丑,大家懂意思就行了。
# -*- encoding:utf-8 -*-
import hashlib
from Crypto.Cipher import AES
from binascii import b2a_hex, a2b_hex
all='74f0b165db8a628716b53a9d4f6405980db2f833afa1ed5eeb4304c5220bdc0b541f857a7348074b2a7775d691e71b490402621e8a53bad4cf7ad4fcc15f20a8066e087fc1b2ffb21c27463b5737e34738a6244e1630d8fa1bf4f38b7e71d707425c8225f240f4bd2b03d6c2471e900b75154eb6f9dfbdf5a4eca9de5163f9b3ee82959f166924e8ad5f1d744c51416a1db89638bb4d1411aa1b1307d88c1fb5'
enc=a2b_hex(all)
for i in range(256):
print(i)
for j in range(256):
for k in range(256):
dec=enc
key=chr(i)+chr(j)+chr(k)
keys=
for x in range(9):
keys.append(hashlib.md5(keys).digest())
for y in range(10):
aes=AES.new(keys, 1)
dec=aes.decrypt(dec)
if "flag" in dec:
print(b2a_hex(dec))
这里爆破过程中dec有两个值,转一下字符看一下就行了。
参加CTF比赛 ,安卓逆向也就只能做出来java层的加密了 。加密一放到so层就蒙圈了{:1_908:} zhengyg 发表于 2019-8-29 10:29
主要是我还没明白,key和flag都是随意输入的吗,随意一个key肯定会有flag与之对应吗,题意我还没明白,昨 ...
就针对这道题而言,flag是唯一的,key的话不唯一,但是应该没有人会去解这个key,因为他之后是经过md5处理的。我觉得你还是需要重点关注一下checkFlag这个函数,好好理一下里面的逻辑关系。 感谢楼主的分享,收获颇多,看来以后要耐心看这些代码,不能太急躁了。 学习到了,感谢楼主 这个题目时干啥的呀,key和falg都要输入的吗?
为啥是爆破三个字符,10个密钥是啥,怎么出来的,字符串不是无限个???我哪里理解错了吗 zhengyg 发表于 2019-8-28 22:16
这个题目时干啥的呀,key和falg都要输入的吗?
为啥是爆破三个字符,10个密钥是啥,怎么出来的,字符串 ...
对,你把apk装到手机上看一下就知道了,key和flag都是需要自己输入的。然后你说的10个密钥是指在checkFlag()函数里面的while循环中的AES的密钥,因为有10轮AES,所以是10个密钥。 qdam 发表于 2019-8-29 08:45
对,你把apk装到手机上看一下就知道了,key和flag都是需要自己输入的。然后你说的10个密钥是指在checkFla ...
主要是我还没明白,key和flag都是随意输入的吗,随意一个key肯定会有flag与之对应吗,题意我还没明白,昨天才知道有OGeek这个东西,原谅我的无知啊 感谢楼主的题解! 感谢楼主的分享,我有几个问题,希望楼主解答一下:
1. Python代码中为什么要range(256)呢?
页:
[1]
2