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よりはチームの力になれた気がします。もっと力をつけたい。