WhiteHat CTF 2016 Writeup
WhiteHat CTF 2016にTeam:Harekazeの一員として参加していました。順位で18位で800pts取得、そのうちの自分が解いた300ptsのwriteupです
misc200pts(名前忘れた)
イラストロジックの問題SECCON Online CTF 2015に似たような問題あったなー...
イラストロジックのオンラインソルバと強力なQRデコーダを使います。
イラストロジックは複数解がありますが、パズルの数も3つなのでスクリプトを組まずに人力でやりました。
1つめからはN0N
2つめからはOGR
3つめからはAM
ということでWhiteHat{sha1(N0NOGRAM)}が答えになる
flagはWhiteHat{7d2c0e2d951e70c482f98531c82baeb512128adf}
Nem Ran(Reverse Engineering 100pts)
zipを解凍すると.pycファイルが出て来る。pycはpythonのバイナリコード?なのでディスアセンブルする必要がある
ggrといい感じのディスアセンブルコードが出て来る([ももいろテクノロジーさん}(http://inaz2.hatenablog.com/entry/2016/10/10/131358)神)
# http://nedbatchelder.com/blog/200804/the_structure_of_pyc_files.html import dis, marshal, struct, sys, time, types def show_file(fname): f = open(fname, "rb") magic = f.read(4) moddate = f.read(4) modtime = time.asctime(time.localtime(struct.unpack('I', moddate)[0])) print "magic %s" % (magic.encode('hex')) print "moddate %s (%s)" % (moddate.encode('hex'), modtime) code = marshal.load(f) show_code(code) def show_code(code, indent=''): print "%scode" % indent indent += ' ' print "%sargcount %d" % (indent, code.co_argcount) print "%snlocals %d" % (indent, code.co_nlocals) print "%sstacksize %d" % (indent, code.co_stacksize) print "%sflags %04x" % (indent, code.co_flags) show_hex("code", code.co_code, indent=indent) dis.disassemble(code) print "%sconsts" % indent for const in code.co_consts: if type(const) == types.CodeType: show_code(const, indent+' ') else: print " %s%r" % (indent, const) print "%snames %r" % (indent, code.co_names) print "%svarnames %r" % (indent, code.co_varnames) print "%sfreevars %r" % (indent, code.co_freevars) print "%scellvars %r" % (indent, code.co_cellvars) print "%sfilename %r" % (indent, code.co_filename) print "%sname %r" % (indent, code.co_name) print "%sfirstlineno %d" % (indent, code.co_firstlineno) show_hex("lnotab", code.co_lnotab, indent=indent) def show_hex(label, h, indent): h = h.encode('hex') if len(h) < 60: print "%s%s %s" % (indent, label, h) else: print "%s%s" % (indent, label) for i in range(0, len(h), 60): print "%s %s" % (indent, h[i:i+60]) show_file(sys.argv[1])
これを使ってディスアセンブルする。
$ python show_crack.py re100.pyc magic 03f30d0a moddate 26462458 (Thu Nov 10 19:04:22 2016) code argcount 0 nlocals 0 stacksize 5 flags 0040 code 6400008400005a00006401008400005a01006402008400005a0200640300 8400005a03006404008400005a0400798b016505006405008301005a0600 6406005a07006700005a0800784700650900640600650a00650600830100 640700830300445d2d005a0b00650600650b00650b0064070017215a0c00 6508006a0d00650c006a0e00640800830100830100017161005765010065 0f0065080064060019640900830200640a008302005a1000650200650f00 650800640b0019640900830200640c008302005a1100650100650f006508 00640d0019640900830200640e008302005a1200650200650f0065080064 0f00196409008302006407008302005a1300650100650f00650800640700 196409008302006410008302005a1400650200650f00650800640c001964 0900830200640f008302005a1500650300650f0065080064100019640900 830200640d008302005a16006510006411006b020072b201651100641200 6b020072b2016512006413006b020072b2016513006414006b020072b201 6514006415006b020072b2016515006416006b020072b201651600641700 6b020072b20164180047486e05006419004748576e130001010164190047 48651700830000016e010058641a0053 1 0 LOAD_CONST 0 (<code object _ror at 0x101ca7d30, file "C:\Users\AutoBot\Desktop\deWhiteHat\re100.py", line 1>) 3 MAKE_FUNCTION 0 6 STORE_NAME 0 (_ror) 5 9 LOAD_CONST 1 (<code object <lambda> at 0x101ca7830, file "C:\Users\AutoBot\Desktop\deWhiteHat\re100.py", line 5>) 12 MAKE_FUNCTION 0 15 STORE_NAME 1 (__ROR8__) 6 18 LOAD_CONST 2 (<code object <lambda> at 0x101ca79b0, file "C:\Users\AutoBot\Desktop\deWhiteHat\re100.py", line 6>) 21 MAKE_FUNCTION 0 24 STORE_NAME 2 (__ROR4__) 7 27 LOAD_CONST 3 (<code object <lambda> at 0x101ca7ab0, file "C:\Users\AutoBot\Desktop\deWhiteHat\re100.py", line 7>) 30 MAKE_FUNCTION 0 33 STORE_NAME 3 (__ROR2__) 8 36 LOAD_CONST 4 (<code object <lambda> at 0x101ca7b30, file "C:\Users\AutoBot\Desktop\deWhiteHat\re100.py", line 8>) 39 MAKE_FUNCTION 0 42 STORE_NAME 4 (__ROR1__) 9 45 SETUP_EXCEPT 395 (to 443) 10 48 LOAD_NAME 5 (raw_input) 51 LOAD_CONST 5 ('Enter key: ') 54 CALL_FUNCTION 1 57 STORE_NAME 6 (key) 11 60 LOAD_CONST 6 (0) 63 STORE_NAME 7 (count) 12 66 BUILD_LIST 0 69 STORE_NAME 8 (ret) 13 72 SETUP_LOOP 71 (to 146) 75 LOAD_NAME 9 (range) 78 LOAD_CONST 6 (0) 81 LOAD_NAME 10 (len) 84 LOAD_NAME 6 (key) 87 CALL_FUNCTION 1 90 LOAD_CONST 7 (4) 93 CALL_FUNCTION 3 96 GET_ITER >> 97 FOR_ITER 45 (to 145) 100 STORE_NAME 11 (i) 14 103 LOAD_NAME 6 (key) 106 LOAD_NAME 11 (i) 109 LOAD_NAME 11 (i) 112 LOAD_CONST 7 (4) 115 BINARY_ADD 116 SLICE+3 117 STORE_NAME 12 (a) 15 120 LOAD_NAME 8 (ret) 123 LOAD_ATTR 13 (append) 126 LOAD_NAME 12 (a) 129 LOAD_ATTR 14 (encode) 132 LOAD_CONST 8 ('hex') 135 CALL_FUNCTION 1 138 CALL_FUNCTION 1 141 POP_TOP 142 JUMP_ABSOLUTE 97 >> 145 POP_BLOCK 18 >> 146 LOAD_NAME 1 (__ROR8__) 149 LOAD_NAME 15 (int) 152 LOAD_NAME 8 (ret) 155 LOAD_CONST 6 (0) 158 BINARY_SUBSCR 159 LOAD_CONST 9 (16) 162 CALL_FUNCTION 2 165 LOAD_CONST 10 (7) 168 CALL_FUNCTION 2 171 STORE_NAME 16 (b1) 19 174 LOAD_NAME 2 (__ROR4__) 177 LOAD_NAME 15 (int) 180 LOAD_NAME 8 (ret) 183 LOAD_CONST 11 (1) 186 BINARY_SUBSCR 187 LOAD_CONST 9 (16) 190 CALL_FUNCTION 2 193 LOAD_CONST 12 (5) 196 CALL_FUNCTION 2 199 STORE_NAME 17 (b2) (snip) 26 342 LOAD_NAME 16 (b1) 345 LOAD_CONST 17 (16717361816810737874L) 348 COMPARE_OP 2 (==) 351 POP_JUMP_IF_FALSE 434 354 LOAD_NAME 17 (b2) 357 LOAD_CONST 18 (2737455883L) 360 COMPARE_OP 2 (==) 363 POP_JUMP_IF_FALSE 434 366 LOAD_NAME 18 (b3) 369 LOAD_CONST 19 (6845471433611232304L) 372 COMPARE_OP 2 (==) 375 POP_JUMP_IF_FALSE 434 378 LOAD_NAME 19 (b4) 381 LOAD_CONST 20 (4120053589L) 384 COMPARE_OP 2 (==) 387 POP_JUMP_IF_FALSE 434 390 LOAD_NAME 20 (b5) 393 LOAD_CONST 21 (15852670688363919553L) 396 COMPARE_OP 2 (==) 399 POP_JUMP_IF_FALSE 434 402 LOAD_NAME 21 (b6) 405 LOAD_CONST 22 (1273644554) 408 COMPARE_OP 2 (==) 411 POP_JUMP_IF_FALSE 434 414 LOAD_NAME 22 (b7) 417 LOAD_CONST 23 (23327) 420 COMPARE_OP 2 (==) 423 POP_JUMP_IF_FALSE 434
とこんな感じのコードが出て来る。これを見ると入力したkeyを4個ずつに分割して、それに対してROR8等の計算をした結果が16717361816810737874L
等と一緒になればいいらしい。
ROR8等の関数も下の方に記述されており。
consts code argcount 3 nlocals 3 stacksize 5 flags 0043 code 7c00006401007c02001364020018407c01007c0200163f7c00007c02007c 01007c020016183e6401007c02001364020018404253 2 0 LOAD_FAST 0 (val) 3 LOAD_CONST 1 (2) 6 LOAD_FAST 2 (bit_size) 9 BINARY_POWER 10 LOAD_CONST 2 (1) 13 BINARY_SUBTRACT 14 BINARY_AND 15 LOAD_FAST 1 (bits) 18 LOAD_FAST 2 (bit_size) 21 BINARY_MODULO 22 BINARY_RSHIFT 3 23 LOAD_FAST 0 (val) 26 LOAD_FAST 2 (bit_size) 29 LOAD_FAST 1 (bits) 32 LOAD_FAST 2 (bit_size) 35 BINARY_MODULO 36 BINARY_SUBTRACT 37 BINARY_LSHIFT 38 LOAD_CONST 1 (2) 41 LOAD_FAST 2 (bit_size) 44 BINARY_POWER 45 LOAD_CONST 2 (1) 48 BINARY_SUBTRACT 49 BINARY_AND 50 BINARY_OR 51 RETURN_VALUE consts None 2 1 names () varnames ('val', 'bits', 'bit_size') freevars () cellvars () filename 'C:\\Users\\AutoBot\\Desktop\\deWhiteHat\\re100.py' name '_ror' firstlineno 1 lnotab 00011701 code argcount 2 nlocals 2 stacksize 4 flags 0043 code 7400007c00007c010064010083030053 5 0 LOAD_GLOBAL 0 (_ror) 3 LOAD_FAST 0 (val) 6 LOAD_FAST 1 (bits) 9 LOAD_CONST 1 (64) 12 CALL_FUNCTION 3 15 RETURN_VALUE consts None 64 names ('_ror',) varnames ('val', 'bits') freevars () cellvars () filename 'C:\\Users\\AutoBot\\Desktop\\deWhiteHat\\re100.py' name '<lambda>' firstlineno 5 lnotab
2,3のところがROR8やROR4で呼び出している_ror関数、その後5,6,7,8とROR8,ROR4,ROR2,ROR1の関数のディスアセンブルした結果が表示されている。これらをpythonのコードになおしてみるとこうなる。
def _ror(val,bits,bit_size): a = ((2 ** bit_size - 1) & val) >> (bits % bit_size) b = val << (bit_size - (bits % bit_size)) return a | (b & (2 ** bit_size - 1)) def __ror8(val,bits): return _ror(val,bits,64) def __ror4(val,bits): return _ror(val,bits,32) def __ror2(val,bits): return _ror(val,bits,16) def __ror1(val,bits): return _ror(val,bits,8) print('Enter key: ') key = raw_input() count = 0 ret = [] for i in range(0,len(key),4): a = key[i:i+4] ret.append(a.encode('hex')) b1 = __ror8(int(ret[0],16),7) b2 = __ror4(int(ret[1],16),5) b3 = __ror8(int(ret[2],16),8) b4 = __ror4(int(ret[3],16),4) b5 = __ror8(int(ret[4],16),6) b6 = __ror4(int(ret[4],16),3) b7 = __ror2(int(ret[4],16),2) b1a = 16717361816810737874L b2a = 2737455883L b3a = 6845471433611232304L b4a = 4120053589L b5a = 15852670688363919553L b6a = 127364455 b7a = 23327
&や|演算子が使われているので答えの数字から逆算は難しいと判断、しかしkeyを4つの値に分割しているのでbrute forceで解けそうだと思ったのでそのコードを書く。
from itertools import permutations # brute force def _ror(val,bits,bit_size): line_two = ((2 ** bit_size - 1) & val) >> (bits % bit_size) line_three_37 = val << (bit_size - (bits % bit_size)) return line_two | (line_three_37 & (2 ** bit_size - 1)) def __ror8(val,bits): return _ror(val,bits,64) def __ror4(val,bits): return _ror(val,bits,32) def __ror2(val,bits): return _ror(val,bits,16) def __ror1(val,bits): return _ror(val,bits,8) #print('Enter key: ') #key = raw_input() #count = 0 #ret = [] #for i in range(0,len(key),4): # a = key[i:i+4] # ret.append(a.encode('hex')) b1a = 16717361816810737874 b2a = 2737455883 b3a = 6845471433611232304 b4a = 4120053589 b5a = 15852670688363919553 b6a = 127364455 b7a = 23327 text="ABCDEFGHIJKLMNNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz{}_" lis = list(permutations(list(text),4)) print(len(lis)) print(__ror8(int("Whit".encode('hex'),16),7)) for i in range(len(lis)): b1 = __ror8(int("".join(lis[i]).encode('hex'),16),7) # print(b1) if b1 == b1a: print("b1="+"".join(lis[i])) break
最初はこのコードでやっていた、しかしb6だけ復号できずに悩む。原因は同じ文字を使う可能性を考えずに順列を使っていたことだった。forの部分を4重のループを回すように変更したらb6も復号成功。b7に関しては最後なので長さを2から4まで変更して試した。
key=WhiteHat{D0_Y0u_Kn0w_R0Rl}
になるので
WhiteHat{sha1(WhiteHat{D0_Y0u_Kn0w_R0Rl})}がflag
WhiteHat{e3924cda1894cbb7823b3e8405dceafff58dc90e}
終わりに
SECCONよりはチームの力になれた気がします。もっと力をつけたい。