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