吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 8288|回复: 24
收起左侧

[CTF] CTF攻防世界Reverse018_zorropub逆向分析【原创】

[复制链接]
pk8900 发表于 2019-12-17 20:01
本帖最后由 pk8900 于 2019-12-17 22:16 编辑

0x00:前言
好久没有发贴了,现在论坛里大神好多啊,看完大神的帖子,真不知道该写点什么,只能水一贴了,写一下昨天逆向一个CFT的过程和思路吧。

0x01:程序32/64位识别
CTF题目没有说明,只提供一个附件,下载后,发现无扩展名,估计是Linux程序,于是用十六进制编辑器(Uedit)打开查看,在文件头部发现 ELF 字符,确定为Linux程序,再往下查看,发现“/lib64/ld-linux-x86-64.so”字符,确认为64位Linux程序。
说一下Linux程序识别的方法,因为Linux系统中文件并非是通过扩展名来标识文件类型的,所以linux程序在WINDOWS系统里只能通过查看文件内容进行确定,(估计有相关的工具,之前下载了一个,发现不好用,就放弃了。)通过十六进制编辑器查看更方便一些。
linux程序32位和64位的区别如图:
file02.png

我认为通过查看字符串资源:/lib/ld-linux.so  或 /lib64/ld-linux-x86-64.so 来区分更方便一些。也可以用Linux的 FILE 命令查看,这个CTF程序为64位程序,那就用IDA64打开进行分析。
file.png
0x02:分析过程
IDA64加载后,查看函数列表,找到Main函数,F5分析伪代码如下:
[C] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
int __fastcall main(__int64 a1, char **a2, char **a3)
{
  size_t v3; // rax
  int v5; // [rsp+1Ch] [rbp-104h]
  int v6; // [rsp+20h] [rbp-100h]
  int i; // [rsp+24h] [rbp-FCh]
  unsigned int seed; // [rsp+28h] [rbp-F8h]
  unsigned int v9; // [rsp+2Ch] [rbp-F4h]
  char v10; // [rsp+30h] [rbp-F0h]
  char v11[16]; // [rsp+90h] [rbp-90h]
  char v12[32]; // [rsp+A0h] [rbp-80h]
  char s; // [rsp+C0h] [rbp-60h]
  char s1[40]; // [rsp+E0h] [rbp-40h]
  unsigned __int64 v15; // [rsp+108h] [rbp-18h]
 
  v15 = __readfsqword(0x28u);
  seed = 0;
  puts("Welcome to Pub Zorro!!");
  printf("Straight to the point. How many drinks you want?", a2);
  __isoc99_scanf("%d", &v5);                    // scanf 获取输入数字 drinks
  if ( v5 <= 0 )
  {
    printf("You are too drunk!! Get Out!!", &v5);
    exit(-1);
  }
  printf("OK. I need details of all the drinks. Give me %d drink ids:", (unsigned int)v5);
  for ( i = 0; i < v5; ++i )
  {
    __isoc99_scanf("%d", &v6);                  // scanf 获取输入数字 drink ids
    if ( v6 <= 16 || v6 > 65535 )               // drink ids 范围:16-65534
    {
      puts("Invalid Drink Id.");
      printf("Get Out!!", &v6);
      exit(-1);
    }
    seed ^= v6;
  }
  i = seed;
  v9 = 0;
  while ( i )
  {
    ++v9;
    i &= i - 1;
  }
  if ( v9 != 10 )                               // ids 循环位操作进行计数,最终计数必须为10
  {
    puts("Looks like its a dangerous combination of drinks right there.");
    puts("Get Out, you will get yourself killed");
    exit(-1);
  }
  srand(seed);                                  // 置随机数种子
  MD5_Init((__int64)&v10);
  for ( i = 0; i <= 29; ++i )
  {
    v9 = rand() % 1000;                         // 生成随机数除1000取余
    sprintf(&s, "%d", v9);
    v3 = strlen(&s);
    MD5_Update(&v10, &s, v3);
    v12[i] = v9 ^ LOBYTE(dword_6020C0[i]);      // V12前30位与6020C0数据异或
  }
  v12[i] = 0;                                   // V12第30位添0
  MD5_Final(v11, &v10);
  for ( i = 0; i <= 15; ++i )
    sprintf(&s1[2 * i], "%02x", (unsigned __int8)v11[i]);
  if ( strcmp(s1, "5eba99aff105c9ff6a1a913e343fec67") )
  {
    puts("Try different mix, This mix is too sloppy");
    exit(-1);
  }
  return printf("\nYou choose right mix and here is your reward: The flag is nullcon{%s}\n", v12);// flag为V12的内容
}

通过静态分析Main函数流程,发现程序写脚本得到FLAG:
1、程序要求输入:Straight to the point. How many drinks you want?
输入数:V5 >=0,且后续V5并没有引用,所以只要随意输入大于0的数就可以。
2、"OK. I need details of all the drinks. Give me %d drink ids:", (unsigned int)v5
输入ids:变量为v6,seed ^= v6;运行后,ids存入seed中,后续作为生成随机数的种子。
3、对IDS进行较验:

[C] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
i = seed;
 v9 = 0;
 while ( i )
 {
   ++v9;
   i &= i - 1;
 }
 if ( v9 != 10 )                               // ids 循环位操作进行计数,最终计数必须为10
 {
   puts("Looks like its a dangerous combination of drinks right there.");
   puts("Get Out, you will get yourself killed");
   exit(-1);
 }

此代码进行了一个自减1并位与操作,核对计数为10,实现就是对输入的ids(16-65534) WORD值进行位较验,如果数字中有正好有10个位是1,则符合要求,由此可知最小可满足要求的数是1023,1023的二进制中低10位全是1,因此满足这一条件的数字应该不少,但可以写代码逐一列出。
4、用上一步的IDS【seed】做为随机数种子,生成30个小于1000的随机数,并分别与LOBYTE(dword_6020C0)异或,结果存到V12中,最后的flag就是V12中的内容(nullcon{%s}\n", v12)。
以上步骤分析完后,总结一下,可以通过写一个程序进行计算。
于是用VS2013写代码如下:

[C++] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
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
40
41
42
43
44
#include "IDA.h"
#include<stdio.h>
#include<stdlib.h>
 
bool myEnc(int n);//异或还原函数
int main()
{
        int i, j,n;
        for (int y = 16; y < 65534; y++){
                n = 0;
                int m = y;
                while (m)
                {
                        ++n;
                        m &= m - 1;
                        if (n>=11)
                                break;
                }
                if (n==10){
                        if (myEnc(y)){
                                printf("n=%d \n", y);
                                break;
                        }
                }
        }
system("pause");
return 0;
}
bool myEnc(int n){
        srand(n);
        int enclist[] = { 0x3C8, 0x32, 0x2CE, 0x302, 0x7F, 0x1B8, 0x37E, 0x188, 0x349, 0x27F, 0x5E, 0x234, 0x354, 0x1A3, 0x96, 0x340, 0x128, 0x2FC, 0x300, 0x28E, 0x126, 0x1B, 0x32A, 0x2F5, 0x15F, 0x368, 0x1EB, 0x79, 0x11D, 0x24E };
        unsigned char flag[31] = { 0 };
        int j;
        for (int i = 0; i < 29; i++)
        {
                j = rand() % 1000;
                flag[i] = j ^ LOBYTE(enclist[i]);
                if (flag[i]>128)
                        return false;//如果字符不可见,返回false
        }
        flag[30] = '\0';
        printf(" %s \n", flag); //打印输出找到的flag
        return true;
}


编译运行,可是没有找到flag,于是对程序反复检查,发现没有错误啊,不行动态调试吧,对CTF程序进行动态调试,扔到虚拟机64位ubuntu中,用IDA进行远程调试,输入第2步ids值输入:1023,发现了问题所在:
WINDOWS中,种子为1023时,生成的随机数为: 379  59  741  439  368  145  906  571  287  595  385  624  524  884  895  300......
Linux中,种子为1023时,生成的随机数为:808,14,219,336,499,953,745,120,164,303,30,151,640,588.....
原来在两个系统中种子相同,生成的随机数却不同,看来只能在linux中试一下了,于是在ubuntu中安装了VSCODE进行调试,结果和CTF程序运行一致,找到Flag.

[Asm] 纯文本查看 复制代码
1
2
3
4
[Running] cd "/root/C++/Hello/" && g++ hello.cpp -o hello && "/root/C++/Hello/"hello
sh: 1: pause: not found
 nu11c0n_s4yz_x0r1n6_1s_4m4z1ng 
n=59306

拼接后得到最终flag为:nullcon{nu11c0n_s4yz_x0r1n6_1s_4m4z1ng}
也就是原程序中输入ids:59306 即可得到flag
至此分析完成。至于程序中关于md5值计算的部分,大致分析应为生成的随机数除1000取余后,组成一个字串,对字串求MD5_32,这部分没进行验证,CTF中用的应该是OPENSSL库中的算法。
附上CTF程序:

Reverse018_zorropub.rar (2.97 KB, 下载次数: 23)

免费评分

参与人数 10威望 +1 吾爱币 +15 热心值 +7 收起 理由
YenKoc + 1 + 1 我很赞同!
笙若 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
Hmily + 1 + 7 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
pojieit + 1 + 1 用心讨论,共获提升!
lookerJ + 1 热心回复!
bypasshwid + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
金戋夕夕夕夕 + 1 + 1 我很赞同!
smile5 + 1 用心讨论,共获提升!
FleTime + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
xiaoyou66 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

yz3781286 发表于 2020-3-30 14:46
@PK8900楼主麻烦看一下   大佬分享的2009文泰刻绘  出错补丁的  分享没有了   可以重新分享或者发我邮箱吗467183485@qq.com    那边回复不了   我没法发私信  只能这样回复了  实在是抱歉
yz3781286 发表于 2020-3-31 10:34
@PK8900楼主麻烦看一下   大佬分享的2009文泰刻绘  出错补丁的  分享没有了   可以重新分享下吗 那边回复不了   我没法发私信  只能这样回复了  实在是抱歉  我百度网盘加好友了
xiaoyou66 发表于 2019-12-17 20:13
一人游弋 发表于 2019-12-17 20:14
前排支持大佬
头像被屏蔽
若时光安好 发表于 2019-12-17 20:15
提示: 该帖被管理员或版主屏蔽
Devil太初 发表于 2019-12-17 20:20
感谢大佬分享,力挺
117882697 发表于 2019-12-17 21:10
看看,学习学习
Mino 发表于 2019-12-17 23:45
膜拜一下大神
huaekinvod 发表于 2019-12-17 23:51
的 FILE 命令查看,这个CTF程
2Burhero 发表于 2019-12-18 02:16
看看试试
xxpl123 发表于 2019-12-18 09:00
跟随学习一下,感谢分享
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-5-20 07:11

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表