2019KanXueQ2-RCTF-SCTF

把之前的一些writeup整理一下

看雪CTFQ2

第一题:神秘来信

签到题,没什么好说的

1
if ( v4 < 7 && v14 == 51 && v13 == 53 && v12 == 51 && v11 + v10 + v9 == 149 )

判断成立即可

401353

第六题:消失的岛屿

main函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // eax
uint8_t bindata; // [esp+11h] [ebp-3Fh]
const char *v6; // [esp+48h] [ebp-8h]
char *v7; // [esp+4Ch] [ebp-4h]

__main();
printf("please enter Serial:");
scanf(" %s", &bindata);
if ( strlen((const char *)&bindata) > 0x31 )
puts("error");
v7 = (char *)calloc(1u, 0x400u);
v3 = strlen((const char *)&bindata);
base64_encode(&bindata, v7, v3);
v6 = "!NGV%,$h1f4S3%2P(hkQ94==";
if ( !strcmp("!NGV%,$h1f4S3%2P(hkQ94==", v7) )
puts("Success");
else
puts("Please Try Again");
free(v7);
system("pause");
return 0;
}

逻辑非常简单,将输入base64_encode之后与常量比较,看一眼base64_encode

没什么问题,很base64,进入charEncrypt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
char __cdecl charEncrypt(int data)
{
int dataa; // [esp+18h] [ebp+8h]

dataa = aTuvwxtulmnopqr[data];
if ( dataa > 0x40 && dataa <= 0x5A )
return 0x9B - dataa;
if ( dataa > 0x60 && dataa <= 0x7A )
return dataa - 0x40;
if ( dataa > 0x2F && dataa <= 0x39 )
return dataa + 0x32;
if ( dataa == 0x2B )
return 0x77;
if ( dataa == 0x2F )
dataa = 0x79;
return dataa;
}

拿到table ‘tuvwxTUlmnopqrs7YZabcdefghij8yz0123456VWXkABCDEFGHIJKLMNOPQRS9+/‘

看到table还经过了简单的变换,直接复制出来然后解base64就搞定了

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
import string
import base64

def fun(dataa):
if dataa > 0x40 and dataa <= 0x5A:
dataa = 0x9B - dataa
elif dataa > 0x60 and dataa <= 0x7A:
dataa = dataa - 0x40
elif dataa > 0x2F and dataa <= 0x39:
dataa = dataa + 0x32
elif dataa == 0x2B:
dataa = 0x77
elif dataa == 0x2F:
dataa = 0x79
return dataa

base64_table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
data = 'tuvwxTUlmnopqrs7YZabcdefghij8yz0123456VWXkABCDEFGHIJKLMNOPQRS9+/'
table = ''
cipher_text = '!NGV%,$h1f4S3%2P(hkQ94=='
for i in data:
table += chr(fun(ord(i)))
print table

key = base64.b64decode(cipher_text.translate(string.maketrans(table,base64_table)))
print key

KanXue2019ctf_st

第四题:达芬奇密码

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
int __thiscall sub_401EA0(CWnd *this)
{
CWnd *v1; // esi
int v2; // eax
WCHAR String; // [esp+Ch] [ebp-310h]
char v5; // [esp+Eh] [ebp-30Eh]
char v6; // [esp+20Ch] [ebp-110h]
char v7; // [esp+20Dh] [ebp-10Fh]
DWORD v8; // [esp+30Ch] [ebp-10h]
CWnd *v9; // [esp+310h] [ebp-Ch]
int v10; // [esp+314h] [ebp-8h]
DWORD flOldProtect; // [esp+318h] [ebp-4h]

v1 = this;
v9 = this;
String = 0;
memset(&v5, 0, 0x1FEu);
v6 = 0;
memset(&v7, 0, 0xFFu);
CWnd::GetDlgItemTextW(v1, 1000, &String, 20);
if ( wcslen(&String) == 16 )
{
v2 = 0;
while ( !(*(&String + v2) & 0xFF00) )
{
*(&v6 + v2) = *((_BYTE *)&String + 2 * v2);
if ( ++v2 >= 16 )
{
v8 = 64;
flOldProtect = 0;
VirtualProtect(sub_4010E0, 0xD17u, 0x40u, &flOldProtect);
if ( GetLastError() )
return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0);
qmemcpy(sub_4010E0, &byte_5647B8, 0x330u);
VirtualProtect(sub_4010E0, 0xD17u, flOldProtect, &v8);
if ( !GetLastError() )
{
v10 = 0;
v10 = sub_4010E0();
if ( v10 == 1 )
return CWnd::MessageBoxW(v9, L"Congratulations! You are right!", 0, 0);
}
v1 = v9;
return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0);
}
}
}
return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0);
}

程序逻辑十分简单,首先判断输入字符串长度是否为十六,之后将宽字符转换为字符然后调 VirtualProtect这个API,之后qmemcpy(sub_4010E0, &byte_5647B8, 0x330u);将一段字符复制到sub_4010E0上然后继续用调 VirtualProtect这个API,进入if语句,调用sub_4010E0函数,返回值v10 = 1时正确

看似十分复杂,确实非常复杂,静态分析是不可能的,这辈子都不可能了,果断OD动态开调,发现真正对字符串加密就只有if语句中的sub_4010E0函数,即od中的TheDaVin.002A10E0

1
2
3
4
002A1FAA  |.  8D85 F0FEFFFF lea eax,[local.68]
002A1FB0 50 push eax
002A1FB1 E8 2AF1FFFF call TheDaVin.002A10E0
002A1FB6 |. 8945 F8 mov [local.2],eax

菜鸡只能逐行看汇编代码,再转换为人类能看懂的语言

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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
test = '???? ???? ???? ????' 
assest (test.len == 16)
table = [0x16,0x96,0x8c,0xe3,0x81,0x98,0x6e,0x64,
0x84,0x08,0xdc,0x81,0xbe,0x4d,0x48,0x4f]

for i in range(8):
test[i] ^ table[i] -> [008FEFF4+i]
test[8+i] ^ table[8+i] -> [008EF00C+i]

&arr1 = 008FEFF4
&arr2 = 008EF00C
assest(arr1.len == 8 and arr2.len == 8)
008FF03C: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
ecx = 0
edi = 008FEFFC
edx = 008FF03C
bx = 0
for i in arr1:
for j in arr1:
ax = i*j
ax += bx
(ax & 0x00ff) -> [edi + ecx]
(ax >> 8) -> [edi + 0x08]
bx = [edi + 0x08]
ecx++
ecx = 0
eax = 0
for i in range(9):
eax += [edi + i] + [edx + i]
(eax & 0x00ff) -> [edx+i]
eax >>= 8
ecx = 0
edi++
edx++


ecx = 0
edi = 008FEFFC
edx = 008FF050
bx = 0
for i in arr2:
for j in arr2:
ax = i*j
ax += bx
(ax & 0x00ff) -> [edi + ecx]
(ax >> 8) -> [edi + 0x08]
bx = [edi + 0x08]
ecx++
ecx = 0
eax = 0
for i in range(9):
eax += [edi + i] + [edx + i]
(eax & 0x00ff) -> [edx+i]
eax >>= 8
ecx = 0
edi++
edx++

addr = 008FF050
ss = 008FF014
eax = 0
for i in range(0x11):
dx = [addr + i]
dx *= 0x07
eax += dx
(eax & 0x00ff) -> [ss + i]
eax >>= 8
eax -> [addr+0x11]

addl = 008FF03C

sa = 008FF028
dl = 0
for i in range(0x11):
cx = [ss + i]
si = [addl + i]
if i > 0:
assest(cx == si)
if i = 0:
assest(si == cx + 8)
# si -= cx
# #cx = dl
# #si -= cx
# ecx = si
# #cl -> [sa + i]
# (si & 0x0f) -> [sa + i]
# assest (ecx >= 0)
# if ecx < 0:
# dl = 1
# else:
# dl = 0

# assest ([sa] == 0x08)
# for i in range(1,11):
# assest ([sa+i] == 0x00)

仔细一看wtf,就是解一条方程

x^2 = 7 * y^2 + 8

嗯,双曲线方程,求64位整数解

将解和table异或一下就可以得到flag了(flag = x<<64 + y,小端序

作为一个数学蒻鶸,这怎么解得出来呢!闲着没事干搜了一下椭圆曲线,发现可以根据小整数解组推出大整数解组?果断爆破

1
2
3
4
5
6
7
8
9
for i in range(0xffff):
for j in range(0xffff):
ax = i
bx = j
ax *= ax
bx *= bx
bx *= 7
if ax-bx == 8:
print hex(i),hex(j),hex(ax),hex(bx)

爆破得到这么几组整数解

1
2
3
4
0x6 0x2 0x24 0x1c
0x5a 0x22 0x1fa4 0x1f9c
0x59a 0x21e 0x1f60a4 0x1f609c
0x5946 0x21be 0x1f21bf24 0x1f21bf1c

蒻鶸怎么可能看得懂数学原理呢,自己动手找规律完事,依据两组小整数解组就可以理论上求出无穷大的整数解组

求解代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
x1 = 0x6
y1 = 0x2
x2 = 0x5a
y2 = 0x22

print hex(x1),hex(y1)
print hex(x2),hex(y2)

for i in range(3,17):
x3 = x2*0x10 - x1
y3 = y2*0x10 - y1
print hex(x3) , hex(y3)
x1,x2 = x2,x3
y1,y2 = y2,y3

可以得到63位和64位的解,和table异或一下

1
2
0x557f3b3b9e1a55a 0x2050988b2bd38de
0x552965d47892d506 0x20302760c38eb6fe

得到

1
2
0x61396b325a6d334c 0x4d4d443633613053
0x3147fd559b1e4310 0x4f484dbe81dc088d

排除第二组解,求出flag

1
'0x61396b325a6d334c'[2:].decode('hex')[::-1]+'0x4d4d443633613053'[2:].decode('hex')[::-1]

L3mZ2k9aS0a36DMM

ps:逆向方面不难,耐心点追踪汇编代码仿佛看源码,主要是最后的解方程

RCTF babyre

main函数校验flag长度是否为16位,若是则依次对flag加密后,而后若flag正确则输出常量”Bingo!“(此处有多解)

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
if ( v6 > 0x10 )                              // flag.len == 16
{
puts("input is too long!");
}
else if ( v6 == 16 )
{
v7 = sub_55A527C5BC00((unsigned __int64)&flag, 16, &ptr);// v7 == 8,ptr为flag 变换后的八个字符
if ( v7
&& (v8 = sub_55A527C5C180(ptr, v7, (__int64)&::a3, 16, &dest), (v9 = (char *)v8) != 0LL)
&& dest > 0
&& (unsigned __int16)sub_55A527C5C3D0((__int64)v8, dest) == 0x69E2 )
{
for ( i = 0LL; dest > (signed int)i; ++i )
v9[i] ^= 0x17u;
puts(v9);
if ( ptr )
free(ptr);
free(v9);
}
else
{
puts("input flag is wrong!");
}
}
else
{
puts("input is too short!");
}

第一个函数很简单,主要是对flag通过置换表进行置换,如 ’ab’ 则 置换为 0xab
由于小端序,所以如果我们输入是‘1234567890123456’(128bits)那么经过置换后变成一个64位的整数 0x5634129078563412(64bits)(由于置换表的缘故此处也存在多解,譬如 ‘f’ ‘F’ ‘~’ 均被置换为0xF)
第二个函数是对flag置换后形成的64位整数进行32轮的轮换加密形成一个新的64位整数X ,然后校验高8位是否小于4,并将BYTE(BYTE7(X))(X)置0
轮换加密函数很长,但是由于a2 < -1且 v27 <= 2 所以只需分析那一小部分即可

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
if ( a2 < -1 )
{
v24 = -a2;
v25 = 0x9E3779B9 * (52 / v24 + 6); // v24 == 2
if ( v25 )
{
srch = &src[v24 - 1];
v27 = ~v4;
v44 = &src[~v4];
v28 = ~v4 - 2 - ((~v4 - 3) & 0xFFFFFFFE);
do
{
v29 = v25 >> 2;
if ( v27 <= 2 ) // true
{
v31 = v27;
}
else // false
{

}
srcl_ = &src[v31];
do
{
srcl = *(srcl_ - 1);
--srcl_;
srch_next = srcl_[1]
- (((srch_next ^ v25)
+ (srcl ^ *(_DWORD *)(tables + 4LL * (((unsigned __int8)v29 ^ (unsigned __int8)v31) & 3)))) ^ (((srch_next >> 3) ^ 16 * srcl) + ((srcl >> 5) ^ 4 * srch_next)));
srcl_[1] = srch_next; // 后八位0x56341290替换为 0x45d10f13 小端
--v31;
}
while ( v31 );
srcl_next = *src
- (((((unsigned int)*srch >> 5) ^ 4 * srch_next) + (16 * *srch ^ (srch_next >> 3))) ^ ((*(_DWORD *)(tables + 4LL * (v29 & 3)) ^ *srch) + (v25 ^ srch_next)));
v42 = v25 == 0x9E3779B9;
v25 += 0x61C88647;
srch_next = srcl_next;
*src = srcl_next; // 前四位0x78654321替换为 0x35158711 小端
}
while ( !v42 );
}
return 0LL;
}
return result;
}

第三个函数是一些位操作,并且验证返回值是否为 0x69E2,如果是则将X按字节从低到高异或0x17输出
题目提示输出’Bingo!’,所以将其异或回去得到一个长度为6的byte数组,(由于小端序所以是64位整数的低48位)合理猜测高8位为0x02,所以只需爆破次高8位数据即可(由此构造一个64位数经前两轮解密而后动态调试验证第三个函数返回了0x69E2且输出’Bingo!’)
题目提示 md5(rctf{your answer}) == 5f8243a662cf71bf31d2b2602638dc1d
由此可以爆破flag了,算了一下大概需要 0xff*(2^n+km)次运算(n取决于flag经过第一轮置换字母的数量,km取决了flag经过第一轮置换f的数量,k是常量)
权衡了一下先爆破第二轮加密过程的多解,第一轮默认为小写,且0xf均视为’f’
幸运的是爆破到第二个flag就出来了
rctf{05e8a376e4e0446e}
加解密脚本如下

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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#include<iostream>
#include<string>
#include <openssl/md5.h>
#include<cstdio>
#include<cstring>
using namespace std;
// -lcrypto
string MD5(const string& src )
{
MD5_CTX ctx;

string md5_string;
unsigned char md[16] = { 0 };
char tmp[33] = { 0 };

MD5_Init( &ctx );
MD5_Update( &ctx, src.c_str(), src.size() );
MD5_Final( md, &ctx );

for( int i = 0; i < 16; ++i )
{
memset( tmp, 0x00, sizeof( tmp ) );
sprintf( tmp, "%02X", md[i] );
md5_string += tmp;
}
return md5_string;
}
// encrypt01
unsigned long long encrypt01 (string str)
{
if (str.length() != 16) return 0;
unsigned char byte_5626AE9EB620[55] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
};
unsigned long long res = 0;
unsigned long long ptr = 0;
for (int i = 0; i < str.length(); i+=2)
{
unsigned char a = byte_5626AE9EB620[str[i] - 0x30];
unsigned char b = byte_5626AE9EB620[str[i+1] - 0x30];
ptr = (a << 4) + b;
ptr <<= 4*i;
res += ptr;
}
return res;
}

string decrypt01 (unsigned long long src)
{
unsigned char byte_5626AE9EB620[55] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
};
char chars [16]={0};
unsigned char dest = 0xff;
for (int i = 0; i < 16; i+=2)
{
unsigned char ch = src & dest ;
src >>= 8;
if (((ch & 0xf0) >> 4) <= 9)
chars[i] = ((ch & 0xf0) >> 4) + 0x30;
else
chars[i] = ((ch & 0xf0) >> 4) + 0x57;
if ((ch & 0x0f) <= 9)
chars[i+1] = (ch & 0x0f) + 0x30;
else
chars[i+1] = (ch & 0x0f) + 0x57;
}
return chars;
}

// encrypt02
unsigned long long encrypt02 (unsigned long long src){
unsigned int v25 = (0x9E3779B9 * (52 / 2 + 6) & 0xffffffff);
unsigned int l = src & 0xffffffff;
unsigned int r = (src >> 32) & 0xffffffff;
int i = 0;
bool flag = 0;
do{
int v31 = 1;
unsigned int v29 = v25 >> 2;
int table[] = {0xE0C7E0C7, 0xC6F1D3D7, 0xC6D3C6D3, 0xC4D0D2CE};
r = r - (((l^v25) + (l^table[(v29^v31)&3])) ^ (((l>>3)^16*l) + ((l>>5)^4*l)));
l = l - (((v25^r) + (r^table[(v29&3)])) ^ (((r>>5)^4*r) + (16*r^(r>>3))));
flag = v25 == 0x9E3779B9;
v25 += 0x61C88647;
}while (!flag);
unsigned long long res = ((unsigned long long)r << 32) + l;
return res;
}


//decrypt02
unsigned long long decrypt02(unsigned long long dest){
unsigned int v25 = 0x9E3779B9;
unsigned int l = dest & 0xffffffff;
unsigned int r = (dest >> 32) & 0xffffffff;
bool flag = 0;
do{
int v31 = 1;
unsigned int v29 = v25 >> 2;
int table[] = {0xE0C7E0C7, 0xC6F1D3D7, 0xC6D3C6D3, 0xC4D0D2CE};
l = l + (((v25^r) + (r^table[(v29&3)])) ^ (((r>>5)^4*r) + (16*r^(r>>3))));
r = r + (((l^v25) + (l^table[(v29^v31)&3])) ^ (((l>>3)^16*l) + ((l>>5)^4*l)));
flag = v25 ==(0x9E3779B9 * (52 / 2 + 6) & 0xffffffff);
v25 -= 0x61C88647;
}while(!flag);
unsigned long long res = ((unsigned long long)r << 32) + l;
return res;
}

int main(){
//BYTE7(crack) == 02
unsigned long long crack = (unsigned long long)0x02 << 56;
string str = "Bingo!";
int i = str.length();
while(i--) crack += ((unsigned long long)(str[i]^0x17) << (i * 8));

string flagmd5 = "5F8243A662CF71BF31D2B2602638DC1D";

for (unsigned long long i = 0x0; i <= 0xff; i+=0x1)
{
crack += (unsigned long long)0x1 << 48;
cout << hex << crack << endl;
string flag = "";
flag += "rctf{";
flag += decrypt01(decrypt02(crack));
flag += "}";
if (MD5(flag) == flagmd5)
{
cout << "success!" << endl;
cout << flag << endl;
return 0;
}
}

//encrypt03
// int v11 = r >> 24;
// if (v11 <= 4)//true
// {
// int v13 = 8 - v11;//v13 == 6
// //dest = v13
// //将src[v13]置0
// }

//encrypt04
//i=0
//v13 = 8 - 2
//依次将v[i++]二进制逆序,循环进行下列level1算法直到v[v13-1],算出最终结果为0x4796

//encrytp05
//移位操作0x4796 -> 0x69e2

return 0;
}

SCTF babyre

总共有三关

第一关 三维迷宫

没啥好说的,走迷宫就是了

入口时0x73,出口是0x23

障碍物是0x2A,通道是0x2E

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
# 三维迷宫 方向 上下左右:wsad 空间上下:xy
# 最短路径ddwwxxssxaxwwaasasyywwdd

[
0x2A, 0x2A, 0x2A, 0x2A, 0x2A,
0x2A, 0x2A, 0x2A, 0x2A, 0x2A,
0x2A, 0x2A, 0x2A, 0x2A, 0x2E,
0x2A, 0x2A, 0x2A, 0x2A, 0x2E,
0x2A, 0x2A, 0x73, 0x2E, 0x2E,

0x2A, 0x2E, 0x2E, 0x2A, 0x2A,
0x2A, 0x2A, 0x2A, 0x2A, 0x2E,
0x2A, 0x2A, 0x2A, 0x2A, 0x2E,
0x2A, 0x2A, 0x2A, 0x2A, 0x2A,
0x2A, 0x2A, 0x2A, 0x2A, 0x2A,

0x2A, 0x2E, 0x2E, 0x2A, 0x2A,
0x2A, 0x2E, 0x2E, 0x2A, 0x2A,
0x2E, 0x2E, 0x23, 0x2A, 0x2E,
0x2E, 0x2A, 0x2A, 0x2A, 0x2E,
0x2E, 0x2A, 0x2A, 0x2A, 0x2E,

0x2A, 0x2A, 0x2A, 0x2A, 0x2A,
0x2A, 0x2A, 0x2A, 0x2A, 0x2A,
0x2A, 0x2A, 0x2A, 0x2A, 0x2A,
0x2A, 0x2A, 0x2A, 0x2A, 0x2A,
0x2E, 0x2A, 0x2A, 0x2E, 0x2E,

0x2A, 0x2A, 0x2A, 0x2A, 0x2A,
0x2A, 0x2A, 0x2E, 0x2E, 0x2A,
0x2A, 0x2E, 0x2E, 0x2E, 0x2A,
0x2E, 0x2E, 0x2A, 0x2E, 0x2A,
0x2E, 0x2A, 0x2A, 0x2E, 0x2A,
]

第二关

字符串输入后进入一个函数加密,加密之后与 “sctf_9102“比较

跟踪加密函数发现每4个字符为一轮通过一些查找、移位、或操作将之编码为三个字符

直接脚本爆破

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
data = [0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000003E, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000003F, 0x00000034, 0x00000035, 0x00000036, 0x00000037, 0x00000038, 0x00000039, 0x0000003A, 0x0000003B, 0x0000003C, 0x0000003D, 0x0000007F, 0x0000007F, 0x0000007F, 0x00000040, 0x0000007F, 0x0000007F, 0x0000007F, 0x00000000, 0x00000001, 0x00000002, 0x00000003, 0x00000004, 0x00000005, 0x00000006, 0x00000007, 0x00000008, 0x00000009, 0x0000000A, 0x0000000B, 0x0000000C, 0x0000000D, 0x0000000E, 0x0000000F, 0x00000010, 0x00000011, 0x00000012, 0x00000013, 0x00000014, 0x00000015, 0x00000016, 0x00000017, 0x00000018, 0x00000019, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000001A, 0x0000001B, 0x0000001C, 0x0000001D, 0x0000001E, 0x0000001F, 0x00000020, 0x00000021, 0x00000022, 0x00000023, 0x00000024, 0x00000025, 0x00000026, 0x00000027, 0x00000028, 0x00000029, 0x0000002A, 0x0000002B, 0x0000002C, 0x0000002D, 0x0000002E, 0x0000002F, 0x00000030, 0x00000031, 0x00000032, 0x00000033, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F]

# Python>'sctf_9102'.encode('hex')
# 736374665f39313032
pass2 = ''
dest = [0x736374,0x665f39,0x313032]
for d in range(3):
for i in range(0x20,0x80):
for j in range(0x20,0x80):
for k in range(0x20,0x80):
for l in range(0x20,0x80):
h = (((((data[i]<<6)|data[j])<<6)|data[k])<<6)|data[l]
if h == dest[d]:
print chr(i) + chr(j) + chr(k) + chr(l)
pass2 += chr(i) + chr(j) + chr(k) + chr(l)
print pass2
# pass2 = c2N0Zl85MS=yMT=yMTAy

# pass2_res = c2N0Zl85MTAy

第三轮爆破出了三个结果,排除掉前两个,pass2为 c2N0Zl85MTAy

第三关

第三关看似很复杂,其实并不难,尤其是可以F5看伪代码

首先定义了一个长度为16的数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
v8 = 0xBE;
v9 = 4;
v10 = 6;
v11 = 0x80;
v12 = 0xC5;
v13 = 0xAF;
v14 = 0x76;
v15 = 0x47;
v16 = 0x9F;
v17 = 0xCC;
v18 = 0x40;
v19 = 0x1F;
v20 = 0xD8;
v21 = 0xBF;
v22 = 0x92;
v23 = 0xEF;

不难发现这是加密后的数组常量

同理得到输入也为长度为16的字符串,加密算法如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
v1 = (a1[6] << 8) | (a1[5] << 16) | (a1[4] << 24) | a1[7];
v2 = (a1[10] << 8) | (a1[9] << 16) | (a1[8] << 24) | a1[11];
v3 = (a1[14] << 8) | (a1[13] << 16) | (a1[12] << 24) | a1[15];
v7 = 0;
v5 = 4;
v40 = sub_55FF5657978A((a1[2] << 8) | (a1[1] << 16) | (*a1 << 24) | (unsigned int)a1[3]);
v41 = sub_55FF5657978A(v1);
v42 = sub_55FF5657978A(v2);
v43 = sub_55FF5657978A(v3);
do
{
*(&v40 + v5) = sub_55FF5657A43B(*(&v40 + v7), *(&v40 + v7 + 1), *(&v40 + v7 + 2), *(&v40 + v7 + 3));
++v7;
++v5;
}
while ( v5 <= 29 );

sub_55FF5657978A类似于将之改为大端序

重点是sub_55FF5657A43B函数,函数体如下

a1 ^ (unsigned int)sub_55FF5657A464(a2 ^ a3 ^ a4);```
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

所以加密过程大致如下

将输入的16个字符以大端序的形式转换为4个int,视为a0,a1,a2,a3,并用这四个int生成下一个int a4

**a4= a0 ^ sub_55FF5657A464(a1 ^ a2 ^ a3)** 直到 **a29**,而刚开始定义的常量数组分别对应 **a26 a27 a28 a29**

解密过程往前递推即可 **a25= a29 ^ sub_55FF5657A464(a26 ^ a27 ^ a28)** ……

脚本如下

```python
aaa = [0xbe040680,0xc5af7647,0x9fcc401f,0xd8bf92ef]
d = [
0x000000D6, 0x00000090, 0x000000E9, 0x000000FE, 0x000000CC, 0x000000E1, 0x0000003D, 0x000000B7, 0x00000016, 0x000000B6, 0x00000014, 0x000000C2, 0x00000028, 0x000000FB, 0x0000002C, 0x00000005, 0x0000002B, 0x00000067, 0x0000009A, 0x00000076, 0x0000002A, 0x000000BE, 0x00000004, 0x000000C3, 0x000000AA, 0x00000044, 0x00000013, 0x00000026, 0x00000049, 0x00000086, 0x00000006, 0x00000099, 0x0000009C, 0x00000042, 0x00000050, 0x000000F4, 0x00000091, 0x000000EF, 0x00000098, 0x0000007A, 0x00000033, 0x00000054, 0x0000000B, 0x00000043, 0x000000ED, 0x000000CF, 0x000000AC, 0x00000062, 0x000000E4, 0x000000B3, 0x0000001C, 0x000000A9, 0x000000C9, 0x00000008, 0x000000E8, 0x00000095, 0x00000080, 0x000000DF, 0x00000094, 0x000000FA, 0x00000075, 0x0000008F, 0x0000003F, 0x000000A6, 0x00000047, 0x00000007, 0x000000A7, 0x000000FC, 0x000000F3, 0x00000073, 0x00000017, 0x000000BA, 0x00000083, 0x00000059, 0x0000003C, 0x00000019, 0x000000E6, 0x00000085, 0x0000004F, 0x000000A8, 0x00000068, 0x0000006B, 0x00000081, 0x000000B2, 0x00000071, 0x00000064, 0x000000DA, 0x0000008B, 0x000000F8, 0x000000EB, 0x0000000F, 0x0000004B, 0x00000070, 0x00000056, 0x0000009D, 0x00000035, 0x0000001E, 0x00000024, 0x0000000E, 0x0000005E, 0x00000063, 0x00000058, 0x000000D1, 0x000000A2, 0x00000025, 0x00000022, 0x0000007C, 0x0000003B, 0x00000001, 0x00000021, 0x00000078, 0x00000087, 0x000000D4, 0x00000000, 0x00000046, 0x00000057, 0x0000009F, 0x000000D3, 0x00000027, 0x00000052, 0x0000004C, 0x00000036, 0x00000002, 0x000000E7, 0x000000A0, 0x000000C4, 0x000000C8, 0x0000009E, 0x000000EA, 0x000000BF, 0x0000008A, 0x000000D2, 0x00000040, 0x000000C7, 0x00000038, 0x000000B5, 0x000000A3, 0x000000F7, 0x000000F2, 0x000000CE, 0x000000F9, 0x00000061, 0x00000015, 0x000000A1, 0x000000E0, 0x000000AE, 0x0000005D, 0x000000A4, 0x0000009B, 0x00000034, 0x0000001A, 0x00000055, 0x000000AD, 0x00000093, 0x00000032, 0x00000030, 0x000000F5, 0x0000008C, 0x000000B1, 0x000000E3, 0x0000001D, 0x000000F6, 0x000000E2, 0x0000002E, 0x00000082, 0x00000066, 0x000000CA, 0x00000060, 0x000000C0, 0x00000029, 0x00000023, 0x000000AB, 0x0000000D, 0x00000053, 0x0000004E, 0x0000006F, 0x000000D5, 0x000000DB, 0x00000037, 0x00000045, 0x000000DE, 0x000000FD, 0x0000008E, 0x0000002F, 0x00000003, 0x000000FF, 0x0000006A, 0x00000072, 0x0000006D, 0x0000006C, 0x0000005B, 0x00000051, 0x0000008D, 0x0000001B, 0x000000AF, 0x00000092, 0x000000BB, 0x000000DD, 0x000000BC, 0x0000007F, 0x00000011, 0x000000D9, 0x0000005C, 0x00000041, 0x0000001F, 0x00000010, 0x0000005A, 0x000000D8, 0x0000000A, 0x000000C1, 0x00000031, 0x00000088, 0x000000A5, 0x000000CD, 0x0000007B, 0x000000BD, 0x0000002D, 0x00000074, 0x000000D0, 0x00000012, 0x000000B8, 0x000000E5, 0x000000B4, 0x000000B0, 0x00000089, 0x00000069, 0x00000097, 0x0000004A, 0x0000000C, 0x00000096, 0x00000077, 0x0000007E, 0x00000065, 0x000000B9, 0x000000F1, 0x00000009, 0x000000C5, 0x0000006E, 0x000000C6, 0x00000084, 0x00000018, 0x000000F0, 0x0000007D, 0x000000EC, 0x0000003A, 0x000000DC, 0x0000004D, 0x00000020, 0x00000079, 0x000000EE, 0x0000005F, 0x0000003E, 0x000000D7, 0x000000CB, 0x00000039, 0x00000048, 0x000000C6, 0x000000BA, 0x000000B1, 0x000000A3, 0x00000050, 0x00000033, 0x000000AA, 0x00000056, 0x00000097, 0x00000091, 0x0000007D, 0x00000067, 0x000000DC, 0x00000022, 0x00000070, 0x000000B2, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
]

for i in range(len(d)):
if d[i] == 0xa6:
print i
def pass3_re(x):
x &= 0xffffffff
a = d[x&0xff]
b = (d[(x&0xffff)>>8]<<8)
c = (d[(x&0xffffff)>>16]<<16)
e = (d[(x&0xffffffff)>>24]<<24)
x = a|b|c|e
x &= 0xffffffff
a = (((x<<12)|(x>>20))&0xffffffff)
b = (((x>>6)|(x<<26))&0xffffffff)
c = (((x<<8)|(x>>24))&0xffffffff)
e = (((x>>2)|(x<<30))&0xffffffff)
x = a^b^c^e
return x&0xffffffff

l = []
for i in range(30 - 4):
l.append(0)
for i in aaa:
l.append(i)

cx = 25
for i in range(26):
l[cx] = pass3_re(l[cx+1] ^ l[cx+2] ^ l[cx+3]) ^ l[cx+4]
cx -= 1


pass3 = ''
for i in range(4):
pass3 += (hex(l[i])[2:]).decode('hex')[::-1]

print pass3

由此得到

pass1 : ddwwxxssxaxwwaasasyywwdd

pass2 : c2N0Zl85MTAy

pass3 : fl4g_is_s0_ug1y!

最终flag为 sctf{ddwwxxssxaxwwaasasyywwdd-c2N0Zl85MTAy(fl4g_is_s0_ug1y!)}

ps : 第一关其实输入为 sxss sxsads……都是可以check的,但是并不是基于三维迷宫来做的,而题目又提示plz tell me the shortest password1实在坑了好久

0%