【wp】2021强网杯-ExtremelySlow

继续填坑ing,是强网杯的一道Misc题ExtremelySlow,跟npy合力做的一道杂项题(双双转行Misc手就离谱x),当时捞了个七血,可惜这个题最后被打穿了(

赛中的大概思路就是把传的一个一个字节按顺序提取出来并复原出传输的文件,因为两个工具都不兼容python3.10所以直接手逆pyc+手抠隐写字节码,最后拿到flag。

这里还进行了一点赛后改进,包括魔改stegosaurus兼容到3.10。

【Misc】ExtremelySlow

赛中思路

流量包中提取复原出文件

查看流量包,注意到HTTP/1.1 206 Partial Content的包都是对get包的回答,并且都是/latest文件的一个字节。

(一字节一字节地传,难怪叫ExtremelySlow= =)

双击HTTP/1.1 206 Partial Content包查看(以No.10这个包为例):

可以知道这个包返回的是文件的第0字节0x6f,其余包同理。

所以选中所有这样的回复包,然后导出为纯文本,保存在out.txt中。

然后写脚本从txt里提取这些字节并按顺序还原出源文件(直接把npy脚本搬过来了):

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
import json
import re

with open('./out.txt', 'r') as f:
data = f.read()

data = data.split('\n')
r1 = []
r2 = []
for d in data:
if 'content-range:' in d:
r1.append(d)
if '0000 ' in d and not '0.290470000' in d:
r2.append(d)

# print(len(r1))
# print(len(r2))

data = []
for i in range(len(r1)):
data.append((re.findall(r"\d+\.?\d*",r1[i])[0], '0x'+r2[i][6:8]))

data = sorted(data, key=lambda t: int(t[0]))
# for d in data:
# print(d)
result = [int(d[1],16) for d in data]

# print(len(result))
with open('./res', 'wb') as f:
f.write(bytes(result))

pyc逆向

拿到这个文件,用010打开看到经典第二行E3000000…, 逆向手dna狂喜, 直接猜测是pyc文件,加上pyc后缀。

用uncompyle6和pycdc反编译,但是都由于魔数不对报错,于是把第一个字节改成0x55,假装这是3.8的pyc,反编译依然失败,于是只能用pycdas(zrax/pycdc: C++ python bytecode disassembler and decompiler)反汇编出字节码。

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
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
res.pyc (Python 3.8)
[Code]
File Name: main.py
Object Name: <module>
Arg Count: 0
Pos Only Arg Count: 0
KW Only Arg Count: 0
Locals: 0
Stack Size: 7
Flags: 0x00000040 (CO_NOFREE)
[Names]
'sys'
'hashlib'
'sha256'
'KSA'
'PRGA'
'RC4'
'xor'
'__name__'
'w'
'e'
'b'
's'
't'
'm'
'n'
'list'
'map'
'sorted'
'items'
'stream'
'print'
'decode'
'stdin'
'buffer'
'read'
'p'
'c'
'digest'
[Var Names]
[Free Vars]
[Cell Vars]
[Constants]
0
None
(
'sha256'
)
[Code]
File Name: main.py
Object Name: KSA
Arg Count: 1
Pos Only Arg Count: 0
KW Only Arg Count: 0
Locals: 5
Stack Size: 5
Flags: 0x00000043 (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)
[Names]
'len'
'list'
'range'
[Var Names]
'key'
'keylength'
'S'
'j'
'i'
[Free Vars]
[Cell Vars]
[Constants]
None
256
0
[Disassembly]
0 LOAD_GLOBAL 0: len
2 LOAD_FAST 0: key
4 CALL_FUNCTION 1
6 STORE_FAST 1: keylength
8 LOAD_GLOBAL 1: list
10 LOAD_GLOBAL 2: range
12 LOAD_CONST 1: 256
14 CALL_FUNCTION 1
16 CALL_FUNCTION 1
18 STORE_FAST 2: S
20 LOAD_CONST 2: 0
22 STORE_FAST 3: j
24 LOAD_GLOBAL 2: range
26 LOAD_CONST 1: 256
28 CALL_FUNCTION 1
30 GET_ITER
32 FOR_ITER 29 (to 63)
34 STORE_FAST 4: i
36 LOAD_FAST 3: j
38 LOAD_FAST 2: S
40 LOAD_FAST 4: i
42 BINARY_SUBSCR
44 BINARY_ADD
46 LOAD_FAST 0: key
48 LOAD_FAST 4: i
50 LOAD_FAST 1: keylength
52 BINARY_MODULO
54 BINARY_SUBSCR
56 BINARY_ADD
58 LOAD_CONST 1: 256
60 BINARY_MODULO
62 STORE_FAST 3: j
64 LOAD_FAST 2: S
66 LOAD_FAST 3: j
68 BINARY_SUBSCR
70 LOAD_FAST 2: S
72 LOAD_FAST 4: i
74 BINARY_SUBSCR
76 ROT_TWO
78 LOAD_FAST 2: S
80 LOAD_FAST 4: i
82 STORE_SUBSCR
84 LOAD_FAST 2: S
86 LOAD_FAST 3: j
88 STORE_SUBSCR
90 JUMP_ABSOLUTE 16
92 LOAD_FAST 2: S
94 RETURN_VALUE
'KSA'
[Code]
File Name: main.py
Object Name: PRGA
Arg Count: 1
Pos Only Arg Count: 0
KW Only Arg Count: 0
Locals: 4
Stack Size: 4
Flags: 0x00000063 (CO_OPTIMIZED | CO_NEWLOCALS | CO_GENERATOR | CO_NOFREE)
[Names]
[Var Names]
'S'
'i'
'j'
'K'
[Free Vars]
[Cell Vars]
[Constants]
None
0
True
1
256
[Disassembly]
0 <INVALID>
2 LOAD_CONST 1: 0
4 STORE_FAST 1: i
6 LOAD_CONST 1: 0
8 STORE_FAST 2: j
10 NOP
12 LOAD_FAST 1: i
14 LOAD_CONST 3: 1
16 BINARY_ADD
18 LOAD_CONST 4: 256
20 BINARY_MODULO
22 STORE_FAST 1: i
24 LOAD_FAST 2: j
26 LOAD_FAST 0: S
28 LOAD_FAST 1: i
30 BINARY_SUBSCR
32 BINARY_ADD
34 LOAD_CONST 4: 256
36 BINARY_MODULO
38 STORE_FAST 2: j
40 LOAD_FAST 0: S
42 LOAD_FAST 2: j
44 BINARY_SUBSCR
46 LOAD_FAST 0: S
48 LOAD_FAST 1: i
50 BINARY_SUBSCR
52 ROT_TWO
54 LOAD_FAST 0: S
56 LOAD_FAST 1: i
58 STORE_SUBSCR
60 LOAD_FAST 0: S
62 LOAD_FAST 2: j
64 STORE_SUBSCR
66 LOAD_FAST 0: S
68 LOAD_FAST 0: S
70 LOAD_FAST 1: i
72 BINARY_SUBSCR
74 LOAD_FAST 0: S
76 LOAD_FAST 2: j
78 BINARY_SUBSCR
80 BINARY_ADD
82 LOAD_CONST 4: 256
84 BINARY_MODULO
86 BINARY_SUBSCR
88 STORE_FAST 3: K
90 LOAD_FAST 3: K
92 YIELD_VALUE
94 POP_TOP
96 JUMP_ABSOLUTE 6
'PRGA'
[Code]
File Name: main.py
Object Name: RC4
Arg Count: 1
Pos Only Arg Count: 0
KW Only Arg Count: 0
Locals: 2
Stack Size: 2
Flags: 0x00000043 (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)
[Names]
'KSA'
'PRGA'
[Var Names]
'key'
'S'
[Free Vars]
[Cell Vars]
[Constants]
None
[Disassembly]
0 LOAD_GLOBAL 0: KSA
2 LOAD_FAST 0: key
4 CALL_FUNCTION 1
6 STORE_FAST 1: S
8 LOAD_GLOBAL 1: PRGA
10 LOAD_FAST 1: S
12 CALL_FUNCTION 1
14 RETURN_VALUE
'RC4'
[Code]
File Name: main.py
Object Name: xor
Arg Count: 2
Pos Only Arg Count: 0
KW Only Arg Count: 0
Locals: 2
Stack Size: 5
Flags: 0x00000003 (CO_OPTIMIZED | CO_NEWLOCALS)
[Names]
'bytes'
'map'
[Var Names]
'p'
'stream'
[Free Vars]
[Cell Vars]
'stream'
[Constants]
None
[Code]
File Name: main.py
Object Name: <lambda>
Arg Count: 1
Pos Only Arg Count: 0
KW Only Arg Count: 0
Locals: 1
Stack Size: 3
Flags: 0x00000013 (CO_OPTIMIZED | CO_NEWLOCALS | CO_NESTED)
[Names]
'__next__'
[Var Names]
'x'
[Free Vars]
'stream'
[Cell Vars]
[Constants]
None
[Disassembly]
0 LOAD_FAST 0: x
2 LOAD_DEREF 0: stream
4 LOAD_METHOD 0: __next__
6 CALL_METHOD 0
8 BINARY_XOR
10 RETURN_VALUE
'xor.<locals>.<lambda>'
[Disassembly]
0 LOAD_GLOBAL 0: bytes
2 LOAD_GLOBAL 1: map
4 LOAD_CLOSURE 0: stream
6 BUILD_TUPLE 1
8 LOAD_CONST 1: <CODE> <lambda>
10 LOAD_CONST 2: 'xor.<locals>.<lambda>'
12 MAKE_FUNCTION 8
14 LOAD_FAST 0: p
16 CALL_FUNCTION 2
18 CALL_FUNCTION 1
20 RETURN_VALUE
'xor'
'__main__'
b'\xf6\xef\x10H\xa9\x0f\x9f\xb5\x80\xc1xd\xae\xd3\x03\xb2\x84\xc2\xb4\x0e\xc8\xf3<\x151\x19\n\x8f'
b'$\r9\xa3\x18\xddW\xc9\x97\xf3\xa7\xa8R~'
b'geo'
b'}\xce`\xbej\xa2\x120\xb5\x8a\x94\x14{\xa3\x86\xc8\xc7\x01\x98\xa3_\x91\xd8\x82T*V\xab\xe0\xa1\x141'
b"Q_\xe2\xf8\x8c\x11M}'<@\xceT\xf6?_m\xa4\xf8\xb4\xea\xca\xc7:\xb9\xe6\x06\x8b\xeb\xfabH\x85xJ3$\xdd\xde\xb6\xdc\xa0\xb8b\x961\xb7\x13=\x17\x13\xb1"
115
97
117
114
(
2
8
11
10
)
119
116
124
127
(
3
7
9
12
)
[Code]
File Name: main.py
Object Name: <dictcomp>
Arg Count: 1
Pos Only Arg Count: 0
KW Only Arg Count: 0
Locals: 2
Stack Size: 6
Flags: 0x00000043 (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)
[Names]
'n'
[Var Names]
'.0'
'x'
[Free Vars]
[Cell Vars]
[Constants]
[Disassembly]
0 BUILD_MAP 0
2 LOAD_FAST 0: .0
4 FOR_ITER 9 (to 15)
6 STORE_FAST 1: x
8 LOAD_FAST 1: x
10 LOAD_FAST 1: x
12 LOAD_GLOBAL 0: n
14 LOAD_FAST 1: x
16 BINARY_SUBSCR
18 BINARY_XOR
20 MAP_ADD 2
22 JUMP_ABSOLUTE 2
24 RETURN_VALUE
'<dictcomp>'
[Code]
File Name: main.py
Object Name: <genexpr>
Arg Count: 1
Pos Only Arg Count: 0
KW Only Arg Count: 0
Locals: 2
Stack Size: 3
Flags: 0x00000063 (CO_OPTIMIZED | CO_NEWLOCALS | CO_GENERATOR | CO_NOFREE)
[Names]
'bit_count'
[Var Names]
'.0'
'i'
[Free Vars]
[Cell Vars]
[Constants]
None
[Disassembly]
0 <INVALID>
2 LOAD_FAST 0: .0
4 FOR_ITER 9 (to 15)
6 STORE_FAST 1: i
8 LOAD_FAST 1: i
10 LOAD_METHOD 0: bit_count
12 CALL_METHOD 0
14 LOAD_FAST 1: i
16 BUILD_TUPLE 2
18 YIELD_VALUE
20 POP_TOP
22 JUMP_ABSOLUTE 2
24 LOAD_CONST 0: None
26 RETURN_VALUE
'<genexpr>'
[Code]
File Name: main.py
Object Name: <lambda>
Arg Count: 1
Pos Only Arg Count: 0
KW Only Arg Count: 0
Locals: 1
Stack Size: 2
Flags: 0x00000043 (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)
[Names]
[Var Names]
'x'
[Free Vars]
[Cell Vars]
[Constants]
None
1
[Disassembly]
0 LOAD_FAST 0: x
2 LOAD_CONST 1: 1
4 BINARY_SUBSCR
6 RETURN_VALUE
'<lambda>'
[Disassembly]
0 LOAD_CONST 0: 0
2 LOAD_CONST 1: None
4 IMPORT_NAME 0: sys
6 STORE_NAME 0: sys
8 LOAD_CONST 0: 0
10 LOAD_CONST 2: ('sha256',)
12 IMPORT_NAME 1: hashlib
14 IMPORT_FROM 2: sha256
16 STORE_NAME 2: sha256
18 POP_TOP
20 LOAD_CONST 3: <CODE> KSA
22 LOAD_CONST 4: 'KSA'
24 MAKE_FUNCTION 0
26 STORE_NAME 3: KSA
28 LOAD_CONST 5: <CODE> PRGA
30 LOAD_CONST 6: 'PRGA'
32 MAKE_FUNCTION 0
34 STORE_NAME 4: PRGA
36 LOAD_CONST 7: <CODE> RC4
38 LOAD_CONST 8: 'RC4'
40 MAKE_FUNCTION 0
42 STORE_NAME 5: RC4
44 LOAD_CONST 9: <CODE> xor
46 LOAD_CONST 10: 'xor'
48 MAKE_FUNCTION 0
50 STORE_NAME 6: xor
52 LOAD_NAME 7: __name__
54 LOAD_CONST 11: '__main__'
56 COMPARE_OP 2 (==)
58 POP_JUMP_IF_FALSE 139
60 LOAD_CONST 12: b'\xf6\xef\x10H\xa9\x0f\x9f\xb5\x80\xc1xd\xae\xd3\x03\xb2\x84\xc2\xb4\x0e\xc8\xf3<\x151\x19\n\x8f'
62 STORE_NAME 8: w
64 LOAD_CONST 13: b'$\r9\xa3\x18\xddW\xc9\x97\xf3\xa7\xa8R~'
66 STORE_NAME 9: e
68 LOAD_CONST 14: b'geo'
70 STORE_NAME 10: b
72 LOAD_CONST 15: b'}\xce`\xbej\xa2\x120\xb5\x8a\x94\x14{\xa3\x86\xc8\xc7\x01\x98\xa3_\x91\xd8\x82T*V\xab\xe0\xa1\x141'
74 STORE_NAME 11: s
76 LOAD_CONST 16: b"Q_\xe2\xf8\x8c\x11M}'<@\xceT\xf6?_m\xa4\xf8\xb4\xea\xca\xc7:\xb9\xe6\x06\x8b\xeb\xfabH\x85xJ3$\xdd\xde\xb6\xdc\xa0\xb8b\x961\xb7\x13=\x17\x13\xb1"
78 STORE_NAME 12: t
80 LOAD_CONST 17: 115
82 LOAD_CONST 18: 97
84 LOAD_CONST 19: 117
86 LOAD_CONST 20: 114
88 LOAD_CONST 21: (2, 8, 11, 10)
90 BUILD_CONST_KEY_MAP 4
92 STORE_NAME 13: m
94 LOAD_CONST 22: 119
96 LOAD_CONST 23: 116
98 LOAD_CONST 24: 124
100 LOAD_CONST 25: 127
102 LOAD_CONST 26: (3, 7, 9, 12)
104 BUILD_CONST_KEY_MAP 4
106 STORE_NAME 14: n
108 LOAD_NAME 13: m
110 LOAD_CONST 27: <CODE> <dictcomp>
112 LOAD_CONST 28: '<dictcomp>'
114 MAKE_FUNCTION 0
116 LOAD_NAME 14: n
118 GET_ITER
120 CALL_FUNCTION 1
122 INPLACE_OR
124 STORE_NAME 13: m
126 LOAD_NAME 13: m
128 LOAD_CONST 29: <CODE> <genexpr>
130 LOAD_CONST 30: '<genexpr>'
132 MAKE_FUNCTION 0
134 LOAD_NAME 10: b
136 GET_ITER
138 CALL_FUNCTION 1
140 INPLACE_OR
142 STORE_NAME 13: m
144 LOAD_NAME 5: RC4
146 LOAD_NAME 15: list
148 LOAD_NAME 16: map
150 LOAD_CONST 31: <CODE> <lambda>
152 LOAD_CONST 32: '<lambda>'
154 MAKE_FUNCTION 0
156 LOAD_NAME 17: sorted
158 LOAD_NAME 13: m
160 LOAD_METHOD 18: items
162 CALL_METHOD 0
164 CALL_FUNCTION 1
166 CALL_FUNCTION 2
168 CALL_FUNCTION 1
170 CALL_FUNCTION 1
172 STORE_NAME 19: stream
174 LOAD_NAME 20: print
176 LOAD_NAME 6: xor
178 LOAD_NAME 8: w
180 LOAD_NAME 19: stream
182 CALL_FUNCTION 2
184 LOAD_METHOD 21: decode
186 CALL_METHOD 0
188 CALL_FUNCTION 1
190 POP_TOP
192 LOAD_NAME 0: sys
194 LOAD_ATTR 22: stdin
196 LOAD_ATTR 23: buffer
198 LOAD_METHOD 24: read
200 CALL_METHOD 0
202 STORE_NAME 25: p
204 LOAD_NAME 6: xor
206 LOAD_NAME 9: e
208 LOAD_NAME 19: stream
210 CALL_FUNCTION 2
212 STORE_NAME 9: e
214 LOAD_NAME 6: xor
216 LOAD_NAME 25: p
218 LOAD_NAME 19: stream
220 CALL_FUNCTION 2
222 STORE_NAME 26: c
224 LOAD_NAME 2: sha256
226 LOAD_NAME 26: c
228 CALL_FUNCTION 1
230 LOAD_METHOD 27: digest
232 CALL_METHOD 0
234 LOAD_NAME 11: s
236 COMPARE_OP 2 (==)
238 POP_JUMP_IF_FALSE 131
240 LOAD_NAME 20: print
242 LOAD_NAME 6: xor
244 LOAD_NAME 12: t
246 LOAD_NAME 19: stream
248 CALL_FUNCTION 2
250 LOAD_METHOD 21: decode
252 CALL_METHOD 0
254 CALL_FUNCTION 1
256 POP_TOP
258 LOAD_CONST 1: None
260 RETURN_VALUE
262 LOAD_NAME 20: print
264 LOAD_NAME 9: e
266 LOAD_METHOD 21: decode
268 CALL_METHOD 0
270 CALL_FUNCTION 1
272 POP_TOP
274 LOAD_CONST 1: None
276 RETURN_VALUE
278 LOAD_CONST 1: None
280 RETURN_VALUE

看到行数不算太多,而且有很多都是赋值操作,比较容易逆,干脆直接手逆出源码

逆出来的main.py

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
import sys
from hashlib import sha256

def KSA(key):
keylength=len(key)
S=list(range(256))
j=0
for i in range(256):
j=(S[i]+j+key[i%keylength])%256
S[i],S[j]=S[j],S[i]
return S

def PRGA(S):
i=0
j=0
while True:
i=(i+1)%256
j=(j+S[i])%256
S[j],S[i]=S[i],S[j]
K=S[(S[i]+S[j])%256]
yield K

def RC4(key):
S=KSA(key)
return PRGA(S)

def xor(p,stream):
return bytes(map(lambda x:x^stream.__next__(),p))

if __name__=='__main__':
w=b'\xf6\xef\x10H\xa9\x0f\x9f\xb5\x80\xc1xd\xae\xd3\x03\xb2\x84\xc2\xb4\x0e\xc8\xf3<\x151\x19\n\x8f'
e=b'$\r9\xa3\x18\xddW\xc9\x97\xf3\xa7\xa8R~'
b=b'geo'
s=b'}\xce`\xbej\xa2\x120\xb5\x8a\x94\x14{\xa3\x86\xc8\xc7\x01\x98\xa3_\x91\xd8\x82T*V\xab\xe0\xa1\x141'
t=b"Q_\xe2\xf8\x8c\x11M}'<@\xceT\xf6?_m\xa4\xf8\xb4\xea\xca\xc7:\xb9\xe6\x06\x8b\xeb\xfabH\x85xJ3$\xdd\xde\xb6\xdc\xa0\xb8b\x961\xb7\x13=\x17\x13\xb1"
m={2:115,8:97,11:117,10:114}
n={3:119,7:116,9:124,12:127}
m|={x:x^n[x] for x in n}
m|=((i.bit_count(),i) for i in b)
stream=RC4(list(map(lambda x:x[1],sorted(m.items()))))
print(xor(w,stream).decode())
p=sys.stdin.buffer.read()
e=xor(e,stream)
c=xor(p,stream)
if sha256(c).digest()==s:
print(xor(t,stream).decode())
else:
print(e.decode())

逆的过程中发现bit_count()是python 3.10的新方法,一查才发现6f 0d 0d 0a是3.10版本的魔数(还以为第一字节被魔改了x

这里python文件的逻辑是用m、n、b生成了一个key,给rc4初始化密钥流,然后xor加密数据(其实就是把rc4加密拆开了),最后验证密文的sha256值是否等于所给的值即可。

由于yield的特性,我们可以爆破出p是26位(其他长度的输入都不能正常输出xor(t,stream).decode()

pyc隐写

爆破sha256显然是不可行的,于是猜测到有pyc隐写,把key打印出来也能看到有提示:

stegosaurus是一个pyc隐写工具(AngelKitty/stegosaurus: A steganography tool for embedding payloads within Python bytecode.),拿工具来用发现又报错,查了一下发现是工具不兼容高版本的问题。

研究了一下这个工具的隐写原理,然后直接手抠隐写payload(

用自己逆出来的main.py生成pyc文件,来跟题目提出来的pyc文件在010里做对比,题目中有值且自己的pyc文件中为00的部分很大可能是隐写的字节,比如这种:(其他以此类推)

拿到一串乱序的payload:b'\xf0\xb4\x55\x16\x36\xc5\x6f\xdb\xc9\xea\x64\x04\x15\x62\x11\xa2\xb0\xcd\xf0\x7d\x49\x32\xd6\x22\xe5\x0a'

因为顺序问题无从下手,只好用stegosaurus工具自己生成了一个隐写版本的两个pyc做对比,为了方便看顺序,payload设置成”ABCDEFGHIJKLMNOPQRSTUVWXYZ“

拿到两个pyc文件,很容易就对比出这26个字母的顺序,比如:

最后拿到顺序

按顺序排列payload填入p中,由rc4的特性加密和解密流程相同,所以可以直接拿源码改出exp:

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
import sys
from hashlib import sha256
def KSA(key):
keylength=len(key)
S=list(range(256))
j=0
for i in range(256):
j=(S[i]+j+key[i%keylength])%256
S[i],S[j]=S[j],S[i]
return S
def PRGA(S):
i=0
j=0
while True:
i=(i+1)%256
j=(j+S[i])%256
S[j],S[i]=S[i],S[j]
K=S[(S[i]+S[j])%256]
yield K
def RC4(key):
S=KSA(key)
return PRGA(S)
def xor(p,stream):
return bytes(map(lambda x:x^stream.__next__(),p))
if __name__=='__main__':
w=b'\xf6\xef\x10H\xa9\x0f\x9f\xb5\x80\xc1xd\xae\xd3\x03\xb2\x84\xc2\xb4\x0e\xc8\xf3<\x151\x19\n\x8f'
e=b'$\r9\xa3\x18\xddW\xc9\x97\xf3\xa7\xa8R~'
b=b'geo'
s=b'}\xce`\xbej\xa2\x120\xb5\x8a\x94\x14{\xa3\x86\xc8\xc7\x01\x98\xa3_\x91\xd8\x82T*V\xab\xe0\xa1\x141'
t=b"Q_\xe2\xf8\x8c\x11M}'<@\xceT\xf6?_m\xa4\xf8\xb4\xea\xca\xc7:\xb9\xe6\x06\x8b\xeb\xfabH\x85xJ3$\xdd\xde\xb6\xdc\xa0\xb8b\x961\xb7\x13=\x17\x13\xb1"
m={2:115,8:97,11:117,10:114}
n={3:119,7:116,9:124,12:127}
m|={x:x^n[x] for x in n}
m|=((i.bit_count(),i) for i in b)
stream=RC4(list(map(lambda x:x[1],sorted(m.items()))))
print(xor(w,stream).decode())
# p=sys.stdin.buffer.read()
p=b'\xe5\x0a\x32\xd6\x22\xf0\x7d\x49\xb0\xcd\xa2\x11\xf0\xb4\x55\x16\x36\xc5\x6f\xdb\xc9\xea\x64\x04\x15\x62'
e=xor(e,stream)
c=xor(p,stream)
print(c)
if sha256(c).digest()==s:
print(xor(t,stream).decode())
else:
print(e.decode())

拿到flag:flag{P0w5rFu1_0pEn_50urcE}

赛后改进

用python提取pcapng

比赛的时候是直接硬提取的流量包里的纯文本的,写脚本比较麻烦XD

后来看到Nu1l的wp调用了一个库python-pcapng,感觉可行,来学着写一下脚本试试。

再后来报错搞不定,找了另一个包pyshark写脚本有:

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

cap=pyshark.FileCapture('./ExtremelySlow.pcapng',display_filter="data")
data=[]
for packet in cap:
content=packet['HTTP'].get_field_by_showname('content-range').showname_value
key=int(re.findall(r"\d+",content)[0])
try:
val=packet['DATA'].data.binary_value
except AttributeError:
val=bytes(chr(packet['DATA'].tcp_reassembled_data.binary_value[-1]),encoding='utf-8')
finally:
data.append((key,val))

data=sorted(data,key=lambda t:t[0])
with open('./res','wb') as f:
for t in data:
f.write(t[1])

反汇编pyc文件

反汇编pyc还可以用以下的小脚本(来自python - How can I understand a .pyc file content - Stack Overflow):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import dis, marshal, sys

header_sizes = [
# (size, first version this applies to)
# pyc files were introduced in 0.9.2 way, way back in June 1991.
(8, (0, 9, 2)), # 2 bytes magic number, \r\n, 4 bytes UNIX timestamp
(12, (3, 6)), # added 4 bytes file size
# bytes 4-8 are flags, meaning of 9-16 depends on what flags are set
# bit 0 not set: 9-12 timestamp, 13-16 file size
# bit 0 set: 9-16 file hash (SipHash-2-4, k0 = 4 bytes of the file, k1 = 0)
(16, (3, 7)), # inserted 4 bytes bit flag field at 4-8
# future version may add more bytes still, at which point we can extend
# this table. It is correct for Python versions up to 3.9
]
header_size = next(s for s, v in reversed(header_sizes) if sys.version_info >= v)

with open(pycfile, "rb") as f:
metadata = f.read(header_size) # first header_size bytes are metadata
code = marshal.load(f) # rest is a marshalled code object

dis.dis(code)

魔改stegosaurus

反编译器魔改估计得写很长,有空专门开一篇文章(咕咕咕)。

这里魔改一下stegosaurus,让它支持3.10版本。

(仓库指路:c10udlnk/stegosaurus: 添加python3.10支持。A steganography tool for embedding payloads within Python bytecode.

首先执行python3.10 stegosaurus.py res.pyc -x,报错:

stegosaurus.py定位到出错代码:

从这里(Python代码保护 | pyc 混淆从入门到工具实现 - 知乎)可以知道,3.5和3.6的header有12字节,而3.7开始支持校验hash值,又多了4字节出来,所以header这里要加一行版本的判断。

然后继续报错:

这里可以知道是编码的问题

而payload里有不可见字符,所以干脆就直接把转码去掉就好。

魔改get √

文章作者: c10udlnk
文章链接: https://c10udlnk.top/p/wpFor-2021qwb/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 c10udlnk_Log