CTF攻防世界Reverse018_zorropub逆向分析【原创】
本帖最后由 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位的区别如图:
我认为通过查看字符串资源:/lib/ld-linux.so或 /lib64/ld-linux-x86-64.so 来区分更方便一些。也可以用Linux的 FILE 命令查看,这个CTF程序为64位程序,那就用IDA64打开进行分析。
0x02:分析过程
IDA64加载后,查看函数列表,找到Main函数,F5分析伪代码如下:
int __fastcall main(__int64 a1, char **a2, char **a3)
{
size_t v3; // rax
int v5; //
int v6; //
int i; //
unsigned int seed; //
unsigned int v9; //
char v10; //
char v11; //
char v12; //
char s; //
char s1; //
unsigned __int64 v15; //
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 = v9 ^ LOBYTE(dword_6020C0); // V12前30位与6020C0数据异或
}
v12 = 0; // V12第30位添0
MD5_Final(v11, &v10);
for ( i = 0; i <= 15; ++i )
sprintf(&s1, "%02x", (unsigned __int8)v11);
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进行较验:
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写代码如下:
#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 = { 0 };
int j;
for (int i = 0; i < 29; i++)
{
j = rand() % 1000;
flag = j ^ LOBYTE(enclist);
if (flag>128)
return false;//如果字符不可见,返回false
}
flag = '\0';
printf(" %s \n", flag); //打印输出找到的flag
return true;
}
编译运行,可是没有找到flag,于是对程序反复检查,发现没有错误啊,不行动态调试吧,对CTF程序进行动态调试,扔到虚拟机的64位ubuntu中,用IDA进行远程调试,输入第2步ids值输入:1023,发现了问题所在:
WINDOWS中,种子为1023时,生成的随机数为: 37959741439368145906571287595385624524884895300......
Linux中,种子为1023时,生成的随机数为:808,14,219,336,499,953,745,120,164,303,30,151,640,588.....
原来在两个系统中种子相同,生成的随机数却不同,看来只能在linux中试一下了,于是在ubuntu中安装了VSCODE进行调试,结果和CTF程序运行一致,找到Flag.
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程序:
@PK8900楼主麻烦看一下 大佬分享的2009文泰刻绘出错补丁的分享没有了 可以重新分享或者发我邮箱吗467183485@qq.com 那边回复不了 我没法发私信只能这样回复了实在是抱歉 @PK8900楼主麻烦看一下 大佬分享的2009文泰刻绘出错补丁的分享没有了 可以重新分享下吗 那边回复不了 我没法发私信只能这样回复了实在是抱歉我百度网盘加好友了 前排支持大佬 前排支持大佬 感谢大佬分享,力挺 看看,学习学习 膜拜一下大神 的 FILE 命令查看,这个CTF程 看看试试 跟随学习一下,感谢分享