0%

WUSTCTF2020 RE Writeup

0x00

好久没做题了,IDA都还是6.8的版本,重装了Python环境连IDA Python都没有。协会也有自己的CTF比赛了,RE题目整体都比较简单,是面向萌新的,我做题过程基本上都很偷懒,很多都是调试的时候硬扒下来的。

比赛地址:https://ctfgame.w-ais.cn

0x01 Cr0ssFun

拖进IDA,F5看反汇编,逻辑很简单,输入字符串,然后check函数判断

看check函数,发现直接return iven_is_handsome()

image-20200329170320204

后面都是一层一层的套娃,把这些提取出来放到文本,正则替换一下,放入Python运行即可,如果套娃层数多的话可以写IDA Python提取

wctf2020{cpp_@nd_r3verse_@re_fun}

0x02 level1

output.txt里面有一些数字,程序逻辑很简单,逆过来就好了

image-20200329184330101

1
2
3
4
5
6
7
8
9
10
a=[0,198,232,816,200,1536,300,6144,984,51200,
570,92160,1200,565248,756,1474560,800,6291456,1782,65536000]
flag='w'
for i in range(1,len(a)):
if i&1:
flag += chr(a[i]>>i)
else:
flag += chr(a[i]//i)

print(flag)

wctf2020{d9-dE6-20c}

0x03 level2

用upx加壳了

image-20200329184535523

直接用upx shell脱壳

image-20200329184644549

wctf2020{Just_upx_-d}

0x04 level3

这题是修改了base64编码表

image-20200329184905028

进行修改的函数

image-20200329184846351

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import base64

a = list('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/')
for i in range(10):
tmp = a[i]
a[i] = a[19 - i]
a[19 - i] = tmp
a = ''.join(a)
#print(a)

b = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

base_fix = 'd2G0ZjLwHjS7DmOzZAY0X2lzX3CoZV9zdNOydO9vZl9yZXZlcnGlfD=='
table = ''.maketrans(a, b)
print(base64.b64decode(base_fix.translate(table)))

wctf2020{Base64_is_the_start_of_reverse}

0x05 level4

提示有个二叉树的数据结构

image-20200329213240725

type1是后序遍历

image-20200329213317181

type2是中序遍历

image-20200329213328205

显然flag就是求先序遍历了,直接用网上的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <cstring>

/*
输入树的后序序列和中序序列输出前序数列
*/

using namespace std;

string a = "20f0Th{2tsIS_icArE}e7__w"; //中序
string b = "2f0t02T{hcsiI_SwA__r7Ee}"; //后序

void f(int ab, int ae, int bb, int be){
if(ab > ae)return;
char root = a[ae];
cout << root;
int i;
for(i=bb; b[i]!=root; i++);
f(ab, ab+i-bb-1, bb, i-1);
f(ab+i-bb, ae-1, i+1, be);
}

int main(){
int l = a.length();
f(0,l-1,0,l-1);

return 0;
}

wctf2020{This_IS_A_7reE}

0x06 funnyre

先运行一下,发现啥都没提示就结束了,先看下start函数,传了init和main的地址

image-20200329140109009

先看init,F5一下,

image-20200329144713196

可以看到里面有个do while循环,看下off_602E08

image-20200329144919439

可以知道当v5为0时调用sub_401E90,V5为1时调用sub_4004C0,这里盲猜v4 = 2,动态调试一下,先在init开头下断点

把要调试的main放入linux下,linux_serverx64也放进去,先用chmod 766 给两个赋予权限,运行linux_serverx64

image-20200329141327051

image-20200329150234959

选择Remote Linux debugger,填上被调试文件的路径和目录,还有Linux的ip地址,参数暂时不填

image-20200329150300083

一路f8到调用函数的地方,可以看到v4确实为2

image-20200329150817741

在call的地方f7进去,在sub_401E90逛了一圈,发现它对put函数的地址操作了一番,好像没找到有用的地方。出来sub_401E90后第二次f7进去就是进sub_4004C0,里面有很多对byte的异或操作

image-20200329151431453

通过动调看下byte异或后变成了啥。在retn的地方下个断点,按f9到这个地方

image-20200329151554704

再去看下byte_603048,按R键把hex转成char,可以看到 you get flag!

image-20200329145935032

init对成功后提示的字符串进行了初始化,继续看main函数,有很多个比较,R转换后发现是flag{},‘}’在第37位,所以flag长度为37,中间的部分长度为32

image-20200329143602566

后面是对中间32位每一位都进行xor或者add的操作,中间穿插着花指令,没办法反编译,但是其余正常的指令比较简单,影响不大

image-20200329155328775

搜一下byte prt [rdx + rax +5],有特别多的结果

image-20200329155858609

继续动调,修改下调试参数,填flag{1*32}试下

image-20200329141438833

在最后一个操作结束后下个断点,把其他断点先删除

image-20200329160307204

在断点处停下在寄存器RDX处右键,跳转过去看下heap

image-20200329161006668

可以看到原本的 '1' 经过一系列操作变成了0xd9,其实就是 '1' -> 0xd9 的映射

image-20200329161233347

同时在停下来的地方,可以看到有一个_memcmp,比较中间32位,将unk_4025c0里面这段提取出来

image-20200329161704061

现在的思路就比较清晰了,获取字符[a-zA-Z0-9]映射,然后把unk_4025c0逆映射回来,这里为了偷懒,直接用动调获取

image-20200329163529158

image-20200329224311

image-20200329163900999

image-20200329163946289

把变化后的字节提取出来

1
2
3
4
5
6
7
8
9
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
unk_4025c0=[0xD9,0x2C,0x27,0xD6,0xD8,0x2A,0xDA,0x2D,
0xD7,0x2C,0xDC,0xE1,0xDB,0x2C,0xD9,0xDD,
0x27,0x2D,0x2A,0xDC,0xDB,0x2C,0xE1,0x29,
0xDA,0xDA,0x2C,0xDA,0x2A,0xD9,0x29,0x2A]

before = list('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!_') #补全64个
after = [0x29,0x26,0x27,0x2C,0x2D,0x2A,0x2B,0x30,
0x31,0x2E,0x2F,0x34,0x35,0x32,0x33,0x18,
0x19,0x16,0x17,0x1C,0x1D,0x1A,0x1B,0x20,
0x21,0x1E,0x49,0x46,0x47,0x4C,0x4D,0x4A,
0x4B,0x50,0x51,0x4E,0x4F,0x54,0x55,0x52,
0x53,0x38,0x39,0x36,0x37,0x3C,0x3D,0x3A,
0x3B,0x40,0x41,0x3E,0xD8,0xD9,0xD6,0xD7,
0xDC,0xDD,0xDA,0xDB,0xE0,0xE1,0xE9,0x43]

Map = {}

for i in range(len(after)):
Map[after[i]] = before[i]

print(Map)

'''
#判断unk_4025c0内的hex是否全在字典内
for i in unk_4025c0:
if i not in Map:
print(i, 'not in Map')
'''

flag = ''
for i in unk_4025c0:
flag += Map[i]

print('flag{' + flag + '}')

flag{1dc20f6e3d497d15cef47d9a66d6f1af}