继续填坑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 jsonimport rewith 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) 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 ])) result = [int (d[1 ],16 ) for d in data] 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 sysfrom hashlib import sha256def 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 sysfrom hashlib import sha256def 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=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 pysharkimport recap=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, sysheader_sizes = [ (8 , (0 , 9 , 2 )), (12 , (3 , 6 )), (16 , (3 , 7 )), ] 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) code = marshal.load(f) 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 √