【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= =)

image-20210712160129393

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

image-20210712160224633

image-20210712160239511

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

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

image-20210712155918386

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

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后缀。

image-20210614152120204

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

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

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打印出来也能看到有提示:

image-20210712161834340

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

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

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

image-20210614153719020

拿到一串乱序的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“

image-20210614154407137

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

image-20210614154535502

最后拿到顺序

img

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

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())

image-20210614154912394

拿到flag:flag{P0w5rFu1_0pEn_50urcE}

赛后改进

用python提取pcapng

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

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

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

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):

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,报错:

image-20210714110022076

stegosaurus.py定位到出错代码:

image-20210714110050500

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

image-20210714111942133

然后继续报错:

image-20210714112041335

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

image-20210714112123741

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

image-20210714112401389

魔改get √


__EOF__

本文作者c10udlnk
本文链接https://www.cnblogs.com/c10udlnk/p/15011714.html
关于博主:欢迎关注我的个人博客-> https://c10udlnk.top/
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   c10udlnk  阅读(356)  评论(0编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示