首页
社区
课程
招聘
[原创]CTF2018第四题分析(qwertyaa)
2018-6-22 15:59 2682

[原创]CTF2018第四题分析(qwertyaa)

2018-6-22 15:59
2682

终于抢到一血了耶...(^_^)

手工减弱花指令

拿到CM拖入IDA看到的第一眼就是指令太花,而且这个花似乎是在向去年秋季赛第二题致敬(没记错的话,连花指令的起始地址都是一样的...)
结果好多函数 F5 不能正常工作...
其实没多大关系,除了主程序以外其他地方都是插入了统一格式的花指令:

CALL A
B: JMP C
A: CALL B
C: ADD ESP,0x8
D: 后续代码

 

直接把第一处CALL A改成JMP D,然后用 U 和 E 键手动调整下函数范围,这样 IDA 就可以识别出来了。

梳理main函数脉络

通过结合 IDA 分析和 OD 调试,我们可以大概知道main做了这些事:

  1. 读入密钥
  2. 检查密钥长度是否为23,否则退出
  3. 将后20位直接变成十六进制值
  4. 将所得值传入 检验函数1(位于0x40125D),并记录返回结果
  5. 检查前三位是否位数字,否则退出
  6. 将前三位传入另一个函数,并记录返回结果
  7. 如果两次返回值都为真,则为真密钥,否则为假。
 

另外通过一定的猜测,我们可以得到这些信息(不确定,可能具有迷惑性):

  1. 程序中使用了MIRACL库进行大数运算。
  2. 程序中字符串多次提到关于栈溢出攻击的信息,虽然这个程序是否同样存在的栈溢出问题似乎并不是解决问题的关键。

手工RSA

然后我们来到0x40125D处,用调试跟踪几次就可以确定是在干嘛。(比如将密钥内容改为00,发现最后比较的内容也变成了00,可以猜测这是做了幂运算的底数。)

 

这个函数首先动态解密了两个长字符串,将他们拉出来:

M =
0019FDE4 32 30 38 43 42 42 37 43 44 36 45 43 43 36 34 35 208CBB7CD6ECC645
0019FDF4 31 36 44 30 37 44 39 37 38 46 35 46 30 36 38 31 16D07D978F5F0681
0019FE04 46 35 33 34 45 41 44 32 33 35 44 35 43 34 39 41 F534EAD235D5C49A
0019FE14 44 44 37 32 44 32 44 42 38 34 30 44 35 33 30 34 DD72D2DB840D5304

N =
0019FB8C 37 64 61 33 39 64 65 36 36 30 31 36 34 37 37 62 7da39de66016477b
0019FB9C 31 61 66 63 33 64 63 38 65 33 30 39 64 63 34 32 1afc3dc8e309dc42
0019FBAC 39 62 35 64 65 38 35 35 66 30 64 36 31 36 64 32 9b5de855f0d616d2
0019FBBC 32 35 62 35 37 30 62 36 38 62 38 38 61 35 38 35 25b570b68b88a585

 

然后程序将N3e9也就是十进制下1001,和输入密钥的hex值分别调用特定函数构造出大整数结构,然后将他们传入一个函数中(由1001和其他一系列特征我们不难猜出这里进行的是modPow),最后将返回值从大整数结构中提取出来并转化为16进制值并与M比对,函数具体内容如下:

  memset(&v8, 0, 0x88u);
  j_strdec((int)code1);
  j_strdec((int)code2);
  *(_DWORD *)(v19 + 564) = 16;
  v6 = (_DWORD *)newstr(0);
  v5 = (_DWORD *)newstr(0);
  v3 = (_DWORD *)newstr(0);
  v4 = (unsigned int *)newstr(0);
  copy(v3, &hashedcode);
  copy(v6, code2);
  copy(v5, "3e9");
  if ( strcmp(v3, v6) != -1 )
    return 0;
  modPow(v3, v5, (int)v6, v4);
  databak(0, v4, &v13, 0);
  free((int)v6);
  free((int)v5);
  free((int)v3);
  free((int)v4);
  sub_409CC0();
  v1 = strlen(&v13);
  j_tohex((int)&v13, (int)&v9, v1);
  return strcmp(code1, &v9) == 0;
}

上述过程其实就是判断下面这个式子是否成立:

 

M == INPUT^E (mod N) 其中E=1001

 

不难想到这是在使用RSA算法进行公钥加密,N既然是作者给的,应该不会很强,于是我想着应该可以暴力破解。
当然,这里为了实现10进制和16进制转换,我敲了个Java小程序:

import java.math.*;
class Main{
    static String x="601616731606062377067631775469716020478784069937";
    public static void main(String args[]) {
        BigInteger a=new BigInteger(x,10);
        System.out.println(a.toString(16));
    }
}

我使用 Mathematica 的EulerPhi函数死活算不出来结果,但我后来找到了一个叫factordb.com的网站,可以很方便的查询一些大整数分解的结果(包括作者给的这个N的分解)。

 

然后我也懒得上程序了,直接敲一段 Mathematica 命令。
我们将分解出来的两个因数记为PQ
那么我们的INPUT内容可由(由于DN是 Mathematica 的函数,我这里统一用小写变量)

d=PowerMod[e,-1,(p-1)*(q-1)]
input=PowerMod[m,d,n]

得到。具体原因可查阅有关这一算法的书籍,比如算法导论第31章。

 

将得到结果放到我们前面提到的Java程序中,并将得到结果用 php 的hex2bin函数解码,得到这一部分的密钥为iamahandsomeguyhaha1

暴力干翻前3字符

接下来继续分析有关检验前三个数字的代码?不了,这段代码除了看得头疼外,作者似乎还加入了随着CM是否检测到被调试而决定的是否更改其最终要求的密钥等机制,很是复杂。
但是这一段代码执行的挺快,连个Sleep都没有,可能数又只有1000种,妥妥的算设计失误(当然如果CM作者没有一点故意的设计失误,一个随便加上强密的CM怎么可能被我等破解呢?)。
于是我敲了一段批处理:

set a=100
echo %a%iamahandsomeguyhaha1 | CrackMe.exe > a.txt
:retry
set /a a=a+1
echo %a%iamahandsomeguyhaha1 | CrackMe.exe > b.txt
fc a.txt b.txt
if %ERRORLEVEL%==0 goto retry

闭眼休息一会儿后成功得到flag:520iamahandsomeguyhaha1


[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

最后于 2018-6-23 00:59 被qwertyaa编辑 ,原因: 校对
收藏
点赞1
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回