[WUSTCTF2020]level3

拖进IDA找到主函数

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
char *v3; // rax
char v5; // [rsp+Fh] [rbp-41h]
char v6[56]; // [rsp+10h] [rbp-40h] BYREF
unsigned __int64 v7; // [rsp+48h] [rbp-8h]

v7 = __readfsqword(0x28u);
printf("Try my base64 program?.....\n>");
__isoc99_scanf("%20s", v6);
v5 = time(0LL);
srand(v5);
if ( (rand() & 1) != 0 )
{
v3 = base64_encode(v6);
puts(v3);
puts("Is there something wrong?");
}
else
{
puts("Sorry I think it's not prepared yet....");
puts("And I get a strange string from my program which is different from the standard base64:");
puts("d2G0ZjLwHjS7DmOzZAY0X2lzX3CoZV9zdNOydO9vZl9yZXZlcnGlfD==");
puts("What's wrong??");
}
return 0;
}

明显是Base加密

最后加密的字符串也给我们了,但经典的Base加密都无法解出flag,猜测位换表

关键在base64_encode是否换表

点进去

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
35
36
37
38
39
40
41
42
43
44
45
46
47
char *__fastcall base64_encode(char *a1)
{
int v1; // eax
int v2; // eax
int v4; // [rsp+1Ch] [rbp-54h]
int v5; // [rsp+20h] [rbp-50h]
int v6; // [rsp+24h] [rbp-4Ch]
int v7; // [rsp+28h] [rbp-48h]
int v8; // [rsp+2Ch] [rbp-44h]
char src[56]; // [rsp+30h] [rbp-40h] BYREF
unsigned __int64 v10; // [rsp+68h] [rbp-8h]

v10 = __readfsqword(0x28u);
v1 = strlen(a1);
v8 = v1 % 3;
v7 = v1 / 3;
memset(src, 0, 0x30uLL);
v6 = 0;
v4 = 0;
v5 = 0;
while ( v4 < v7 )
{
src[v6] = base64_table[a1[v5] >> 2];
src[v6 + 1] = base64_table[(16 * (a1[v5] & 3)) | (a1[v5 + 1] >> 4)];
src[v6 + 2] = base64_table[(4 * (a1[v5 + 1] & 0xF)) | (a1[v5 + 2] >> 6)];
v2 = v6 + 3;
v6 += 4;
src[v2] = base64_table[a1[v5 + 2] & 0x3F];
v5 += 3;
++v4;
}
if ( v8 == 1 )
{
src[v6] = base64_table[a1[v5] >> 2];
src[v6 + 1] = base64_table[16 * (a1[v5] & 3)];
strcat(src, "==");
}
else if ( v8 == 2 )
{
src[v6] = base64_table[a1[v5] >> 2];
src[v6 + 1] = base64_table[(16 * (a1[v5] & 3)) | (a1[v5 + 1] >> 4)];
src[v6 + 2] = base64_table[4 * (a1[v5 + 1] & 0xF)];
src[v6 + 3] = 61;
}
strcpy(a1, src);
return a1;
}

这里我们没有发现换表的

Crtl + E查看调用

最终在这里发现了换表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
_int64 O_OLookAtYou()
{
__int64 result; // rax
char v1; // [rsp+1h] [rbp-5h]
int i; // [rsp+2h] [rbp-4h]

for ( i = 0; i <= 9; ++i )
{
v1 = base64_table[i];
base64_table[i] = base64_table[19 - i];
result = 19 - i;
base64_table[result] = v1;
}
return result;
}

用python写的脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

import base64
# -*- coding: UTF-8 -*-
string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
base_table = list(string)
v1 = ''
for i in range(10):
v1 = base_table[i]
base_table[i] = base_table[19 - i]
result = 19 - i
base_table[result] = v1
end_table = "".join(base_table)
print(end_table)
str1 = 'd2G0ZjLwHjS7DmOzZAY0X2lzX3CoZV9zdNOydO9vZl9yZXZlcnGlfD=='
# 更改后的密码表
string1 = "TSRQPONMLKJIHGFEDCBAUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
# 原表
string2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
print(base64.b64decode(str1.translate(str.maketrans(string1, string2))))
#b'wctf2020{Base64_is_the_start_of_reverse}'

[FlareOn4]IgniteMe

IDA打开找到主函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void __noreturn start()
{
DWORD NumberOfBytesWritten; // [esp+0h] [ebp-4h] BYREF

NumberOfBytesWritten = 0;
hFile = GetStdHandle(0xFFFFFFF6);
dword_403074 = GetStdHandle(0xFFFFFFF5);
WriteFile(dword_403074, aG1v3M3T3hFl4g, 0x13u, &NumberOfBytesWritten, 0);
sub_4010F0();
if ( sub_401050() )
WriteFile(dword_403074, aG00dJ0b, 0xAu, &NumberOfBytesWritten, 0);
else
WriteFile(dword_403074, aN0tT00H0tRWe7r, 0x24u, &NumberOfBytesWritten, 0);
ExitProcess(0);
}

有用的只有一个 sub_401050()函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int sub_401050()
{
int v1; // [esp+0h] [ebp-Ch]
int i; // [esp+4h] [ebp-8h]
unsigned int j; // [esp+4h] [ebp-8h]
char v4; // [esp+Bh] [ebp-1h]

v1 = sub_401020((int)byte_403078);
v4 = sub_401000();
for ( i = v1 - 1; i >= 0; --i )
{
byte_403180[i] = v4 ^ byte_403078[i];
v4 = byte_403078[i];
}
for ( j = 0; j < 0x27; ++j )
{
if ( byte_403180[j] != (unsigned __int8)byte_403000[j] )
return 0;
}
return 1;
}

其中byte_403000是已知的

我自己试了几次一直找不到出错

找不到问题出在哪

看了Wp了解到v4是有一个初始值的

10000000000001110000000000000000 左移4位后变成 00000000011100000000000000001000 转变为 无符号16位0000000000001000 右移1位变成 0000000000000100 转变成char 就是 4。

最简单的方法是直接动调

上脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
s = [0x0D, 0x26, 0x49, 0x45, 0x2A, 0x17, 0x78, 0x44, 0x2B, 0x6C,
0x5D, 0x5E, 0x45, 0x12, 0x2F, 0x17, 0x2B, 0x44, 0x6F, 0x6E,
0x56, 0x09, 0x5F, 0x45, 0x47, 0x73, 0x26, 0x0A, 0x0D, 0x13,
0x17, 0x48, 0x42, 0x01, 0x40, 0x4D, 0x0C, 0x02, 0x69, 0x00]
flag = ''
v4 = 4

for i in range(len(s) - 1, -1, -1):
x = s[i] ^ v4
v4 = x
flag += chr(x)
flag_list = list(flag)
flag_list.reverse()
flag = ''.join(flag_list)
print(flag)

#R_y0u_H0t_3n0ugH_t0_1gn1t3@flare-on.com

[WUSTCTF2020]Cr0ssfun

IDA打开

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4[48]; // [rsp+0h] [rbp-30h] BYREF

puts(" _ _ _ _ _____ _____ _____ ");
puts("| | | | | | / ___|_ _| / ___| ");
puts("| | | | | | \\ `--. | | \\ `--. ___ ___ ");
puts("| |/\\| | | | |`--. \\ | | `--. \\/ _ \\/ __|");
puts("\\ /\\ / |_| /\\__/ / | | /\\__/ / __/ (__ ");
puts(" \\/ \\/ \\___/\\____/ \\_/ \\____/ \\___|\\___|");
while ( 1 )
{
puts("Input the flag");
__isoc99_scanf("%s", v4);
if ( (unsigned int)check((__int64)v4) == 1 )
break;
puts("0ops, your flag seems fake.");
puts("==============================");
rewind(_bss_start);
}
puts("Your flag is correct, go and submit it!");
return 0;
}

关键在check函数

点进去

1
2
3
4
5
6
7
8
9
10
BOOL8 __fastcall iven_1s_great(_BYTE *a1)
{
return a1[2] == 116
&& a1[9] == 99
&& a1[32] == 125
&& a1[19] == 118
&& a1[5] == 48
&& a1[14] == 110
&& (unsigned int)iven_and_grace(a1);
}
1
2
3
4
5
6
7
8
9
_BOOL8 __fastcall iven_is_handsome(_BYTE *a1)
{
return a1[10] == 112
&& a1[13] == 64
&& a1[3] == 102
&& a1[26] == 114
&& a1[20] == 101
&& (unsigned int)iven_is_c0ol(a1);
}

好吧都是一样的,就不往下面看了,总共33位

33位 = 125 = }

整理一下就成

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
35
36
37
38
39
a1=33*['']
a1[10] = 112
a1[13] = 64
a1[3] = 102
a1[26] = 114
a1[20] = 101
a1[7] = 48
a1[16] = 95
a1[11] = 112
a1[23] = 101
a1[30] = 117
a1[0] = 119
a1[6] = 50
a1[22] = 115
a1[31] = 110
a1[12] = 95
a1[15] = 100
a1[8] = 123
a1[18] = 51
a1[28] = 95
a1[21] = 114
a1[2] = 116
a1[9] = 99
a1[32] = 125
a1[19] = 118
a1[5] = 48
a1[14] = 110
a1[4] = 50
a1[17] = 114
a1[29] = 102
a1[17] = 114
a1[24] = 95
a1[1] = 99
a1[25] = 64
a1[27] = 101

for i in a1:
print(chr(i),end='')
#flag{cpp_@nd_r3verse_@re_fun}

[FlareOn6]Overlong

IDA32位打开

整个页面只有三个函数

找到字符串位置

好多好多

但是第一个主函数只循环了28次

结合程序运行

猜测后面的字符没有打印

拖进OD

这里先找到了字符串的长度应该是0XAF,而不是循环的0X1C次

直接数据窗口跟随,南通学长讲过的

OK

flag{I_a_M_t_h_e_e_n_C_o_D_i_n_g@flare-on.com}

还有一种写法是直接写脚本比较麻烦

网上找的代码借鉴一下

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
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
#include <stdio.h>
#include <string.h>
unsigned char encoded_str[] =
{
0xE0, 0x81, 0x89, 0xC0, 0xA0, 0xC1, 0xAE, 0xE0, 0x81, 0xA5,
0xC1, 0xB6, 0xF0, 0x80, 0x81, 0xA5, 0xE0, 0x81, 0xB2, 0xF0,
0x80, 0x80, 0xA0, 0xE0, 0x81, 0xA2, 0x72, 0x6F, 0xC1, 0xAB,
0x65, 0xE0, 0x80, 0xA0, 0xE0, 0x81, 0xB4, 0xE0, 0x81, 0xA8,
0xC1, 0xA5, 0x20, 0xC1, 0xA5, 0xE0, 0x81, 0xAE, 0x63, 0xC1,
0xAF, 0xE0, 0x81, 0xA4, 0xF0, 0x80, 0x81, 0xA9, 0x6E, 0xC1,
0xA7, 0xC0, 0xBA, 0x20, 0x49, 0xF0, 0x80, 0x81, 0x9F, 0xC1,
0xA1, 0xC1, 0x9F, 0xC1, 0x8D, 0xE0, 0x81, 0x9F, 0xC1, 0xB4,
0xF0, 0x80, 0x81, 0x9F, 0xF0, 0x80, 0x81, 0xA8, 0xC1, 0x9F,
0xF0, 0x80, 0x81, 0xA5, 0xE0, 0x81, 0x9F, 0xC1, 0xA5, 0xE0,
0x81, 0x9F, 0xF0, 0x80, 0x81, 0xAE, 0xC1, 0x9F, 0xF0, 0x80,
0x81, 0x83, 0xC1, 0x9F, 0xE0, 0x81, 0xAF, 0xE0, 0x81, 0x9F,
0xC1, 0x84, 0x5F, 0xE0, 0x81, 0xA9, 0xF0, 0x80, 0x81, 0x9F,
0x6E, 0xE0, 0x81, 0x9F, 0xE0, 0x81, 0xA7, 0xE0, 0x81, 0x80,
0xF0, 0x80, 0x81, 0xA6, 0xF0, 0x80, 0x81, 0xAC, 0xE0, 0x81,
0xA1, 0xC1, 0xB2, 0xC1, 0xA5, 0xF0, 0x80, 0x80, 0xAD, 0xF0,
0x80, 0x81, 0xAF, 0x6E, 0xC0, 0xAE, 0xF0, 0x80, 0x81, 0xA3,
0x6F, 0xF0, 0x80, 0x81, 0xAD
};
int decode_0(unsigned char* a1, unsigned char* a2) {
int v3; // [esp+0h] [ebp-8h]
unsigned char v4; // [esp+4h] [ebp-4h]

if ((a2[0] / 8) == 30)
{
v4 = a2[3] & 0x3F | ((a2[2] & 0x3F) << 6) | ((a2[1] & 0x3F) << 12) | ((a2[0] & 7) << 18);
v3 = 4;
}
else if ((a2[0] / 16) == 14)
{
v4 = a2[2] & 0x3F | ((a2[1] & 0x3F) << 6) | ((a2[0] & 0xF) << 12);
v3 = 3;
}
else if ((a2[0] / 32) == 6)
{
v4 = a2[1] & 0x3F | ((a2[0] & 0x1F) << 6);
v3 = 2;
}
else
{
v4 = a2[0];
v3 = 1;
}
*a1 = v4;
return v3;
}
void main()
{
int i = 0;
int v3;
unsigned char flag[100];
unsigned char* a2 = encoded_str;
unsigned char* d = flag;
int len = strlen((char*)a2);
for (i = 0; i < len; ++i)
{
a2 += decode_0(d, a2); // 具体解密

v3 = *d;
d++;
if (!v3)
break;
}
printf("%s", flag);

}

[FlareOn3]Challenge1

ida32位点进去

关键函数在这

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
_BYTE *__cdecl sub_401260(int a1, unsigned int a2)
{
int v3; // [esp+Ch] [ebp-24h]
int v4; // [esp+10h] [ebp-20h]
int v5; // [esp+14h] [ebp-1Ch]
int i; // [esp+1Ch] [ebp-14h]
unsigned int v7; // [esp+20h] [ebp-10h]
_BYTE *v8; // [esp+24h] [ebp-Ch]
int v9; // [esp+28h] [ebp-8h]
int v10; // [esp+28h] [ebp-8h]
unsigned int v11; // [esp+2Ch] [ebp-4h]

v8 = malloc(4 * ((a2 + 2) / 3) + 1);
if ( !v8 )
return 0;
v11 = 0;
v9 = 0;
while ( v11 < a2 )
{
v5 = *(unsigned __int8 *)(v11 + a1);
if ( ++v11 >= a2 )
{
v4 = 0;
}
else
{
v4 = *(unsigned __int8 *)(v11 + a1);
++v11;
}
if ( v11 >= a2 )
{
v3 = 0;
}
else
{
v3 = *(unsigned __int8 *)(v11 + a1);
++v11;
}
v7 = v3 + (v5 << 16) + (v4 << 8);
v8[v9] = aZyxabcdefghijk[(v7 >> 18) & 0x3F];
v10 = v9 + 1;
v8[v10] = aZyxabcdefghijk[(v7 >> 12) & 0x3F];
v8[++v10] = aZyxabcdefghijk[(v7 >> 6) & 0x3F];
v8[++v10] = aZyxabcdefghijk[v3 & 0x3F];
v9 = v10 + 1;
}
for ( i = 0; i < *(_DWORD *)&aZyxabcdefghijk[4 * (a2 % 3) + 64]; ++i )
v8[4 * ((a2 + 2) / 3) - i - 1] = 61;
v8[4 * ((a2 + 2) / 3)] = 0;
return v8;
}

一眼Base换表

1
2
3
4
5
6
7
8
9
10
11
12
import base64
import string

str1 = 'x2dtJEOmyjacxDemx2eczT5cVS9fVUGvWTuZWjuexjRqy24rV29q'
# 更改后的密码表
string1 = "ZYXABCDEFGHIJKLMNOPQRSTUVWzyxabcdefghijklmnopqrstuvw0123456789+/"
# 原表
string2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

print(base64.b64decode(str1.translate(str.maketrans(string1, string2))))

#flag{sh00ting_phish_in_a_barrel@flare-on.com}