qdam 发表于 2019-8-26 10:36

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有两个值,转一下字符看一下就行了。


syncking 发表于 2019-8-26 11:23

参加CTF比赛 ,安卓逆向也就只能做出来java层的加密了 。加密一放到so层就蒙圈了{:1_908:}

qdam 发表于 2019-8-29 13:02

zhengyg 发表于 2019-8-29 10:29
主要是我还没明白,key和flag都是随意输入的吗,随意一个key肯定会有flag与之对应吗,题意我还没明白,昨 ...

就针对这道题而言,flag是唯一的,key的话不唯一,但是应该没有人会去解这个key,因为他之后是经过md5处理的。我觉得你还是需要重点关注一下checkFlag这个函数,好好理一下里面的逻辑关系。

krypton12138 发表于 2019-8-26 23:06

感谢楼主的分享,收获颇多,看来以后要耐心看这些代码,不能太急躁了。

flowtcw 发表于 2019-8-27 18:13

学习到了,感谢楼主

zhengyg 发表于 2019-8-28 22:16

这个题目时干啥的呀,key和falg都要输入的吗?

为啥是爆破三个字符,10个密钥是啥,怎么出来的,字符串不是无限个???我哪里理解错了吗

qdam 发表于 2019-8-29 08:45

zhengyg 发表于 2019-8-28 22:16
这个题目时干啥的呀,key和falg都要输入的吗?

为啥是爆破三个字符,10个密钥是啥,怎么出来的,字符串 ...

对,你把apk装到手机上看一下就知道了,key和flag都是需要自己输入的。然后你说的10个密钥是指在checkFlag()函数里面的while循环中的AES的密钥,因为有10轮AES,所以是10个密钥。

zhengyg 发表于 2019-8-29 10:29

qdam 发表于 2019-8-29 08:45
对,你把apk装到手机上看一下就知道了,key和flag都是需要自己输入的。然后你说的10个密钥是指在checkFla ...

主要是我还没明白,key和flag都是随意输入的吗,随意一个key肯定会有flag与之对应吗,题意我还没明白,昨天才知道有OGeek这个东西,原谅我的无知啊

sketch_pl4ne 发表于 2019-8-29 14:01

感谢楼主的题解!

陈小胖 发表于 2019-9-3 00:31

感谢楼主的分享,我有几个问题,希望楼主解答一下:
1. Python代码中为什么要range(256)呢?
页: [1] 2
查看完整版本: CTF-Ogeek之安卓逆向,mblockchain