amaga38のブログ

twitter: @amaga38

Harekaze mini CTF 2020 Write-up

2020/12/26 10:00 JST - 12/27 10:00 JST (24H) に開催されたCTFのWrite-upです。チーム名: whitecatsで出場して378pt. 全体51位でした。

Easy Flag Checker [rev]

このバイナリに文字列を与えると、フラグであるかどうかチェックしてくれます。 This binary checks if the input is the flag.

フラグチェック処理のGhidraでの逆コンパイル部分。param_1が入力文字列、param_2fakeflag{this_is_not_the_real_flag}という文字列。

funcsは、インデックス0~2でそれぞれaddsubxorの処理をするだけの関数で、fakeflagとtableというデータを順番に処理している。tableの中身は、ソルバーに記載。

undefined8 check(long param_1,long param_2)

{
  char cVar1;
  int index;
  
  index = 0;
  while( true ) {
    if (0x22 < index) {
      return 0;
    }
    cVar1 = (**(code **)(funcs + (long)(index % 3) * 8))
                      ((int)*(char *)(param_2 + index),(int)(char)table[index]);
    if (cVar1 < *(char *)(param_1 + index)) break;
    if (*(char *)(param_1 + index) < cVar1) {
      return 0xffffffff;
    }
    index = index + 1;
  }
  return 1;
}

ソルバーは、fakeflagとtableのデータを順番に処理するだけ。xorしたデータだけprintableの範囲を超えるので、最終的に128で割った余りにしている。

# -*- coding: utf-8
funcs = ['\x96\x11\x40\x00\x00\x00\x00\x00', # return a + b
         '\xb4\x11\x40\x00\x00\x00\x00\x00', # return a - b
         '\xd4\x11\x40\x00\x00\x00\x00\x00'] # return a ^ b
# -> 0: add, 1: sub(), 3: xor()


def add(i, j):
    return ord(i) + ord(j)

def sub(i, j):
    return ord(i) - ord(j)

def xor(i, j):
    return ord(i) ^ ord(j)

functions = [add, sub, xor]

'''
                             table                                           XREF[3]:     Entry Point(*), 
                                                                                          check:0040124f(*), 
                                                                                          check:00401256(R)  
        00404060 e2 00 19        undefine
                 00 fb 0d 
                 19 02 38 
           00404060 e2              undefined1E2h                     [0]                               XREF[3]:     Entry Point(*), 
                                                                                                                     check:0040124f(*), 
                                                                                                                     check:00401256(R)  
           00404061 00              undefined100h                     [1]
           00404062 19              undefined119h                     [2]
           00404063 00              undefined100h                     [3]
           00404064 fb              undefined1FBh                     [4]
           00404065 0d              undefined10Dh                     [5]
           00404066 19              undefined119h                     [6]
           00404067 02              undefined102h                     [7]
           00404068 38              undefined138h                     [8]
           00404069 e0              undefined1E0h                     [9]
           0040406a 22              undefined122h                     [10]
           0040406b 12              undefined112h                     [11]
           0040406c bd              undefined1BDh                     [12]
           0040406d ed              undefined1EDh                     [13]
           0040406e 1d              undefined11Dh                     [14]
           0040406f f5              undefined1F5h                     [15]
           00404070 2f              undefined12Fh                     [16]
           00404071 0a              undefined10Ah                     [17]
           00404072 c1              undefined1C1h                     [18]
           00404073 fc              undefined1FCh                     [19]
           00404074 00              undefined100h                     [20]
           00404075 f2              undefined1F2h                     [21]
           00404076 fc              undefined1FCh                     [22]
           00404077 51              undefined151h                     [23]
           00404078 08              undefined108h                     [24]
           00404079 13              undefined113h                     [25]
           0040407a 06              undefined106h                     [26]
           0040407b 07              undefined107h                     [27]
           0040407c 39              undefined139h                     [28]
           0040407d 3c              undefined13Ch                     [29]
           0040407e 05              undefined105h                     [30]
           0040407f 39              undefined139h                     [31]
           00404080 13              undefined113h                     [32]
           00404081 ba              undefined1BAh                     [33]
           00404082 00              undefined100h                     [34]
'''
table =  '\xe2\x00\x19\x00\xfb\x0d\x19\x02'
table += '\x38\xe0\x22\x12\xbd\xed\x1d\xf5'
table += '\x2f\x0a\xc1\xfc\x00\xf2\xfc\x51'
table += '\x08\x13\x06\x07\x39\x3c\x05\x39'
table += '\x13\xba\x00'

fake_flag = "fakeflag{this_is_not_the_real_flag}"
i = 0
cVal = []
for i in range(len(fake_flag)):
    param0 = fake_flag[i]
    param1 = table[i]
    cVal += [functions[i % 3](param0, param1)]

cVal = list(map(lambda x: x % 128, cVal))
print(''.join(map(chr, cVal)))
'''
$ python3 solve.py
HarekazeCTF{0rth0d0x_fl4g_ch3ck3r!}
'''

Flag: HarekazeCTF{0rth0d0x_fl4g_ch3ck3r!}

Wait [rev]

Please be patient. Brute-force attacks on the score server are prohibited.

実行すると、HINT: ^HarekazeCTF\{ID[A-Z]{4}X\}$ と出力される。求めるのは[A-Z]{4}の4文字分。実行ファイルは、フラグチェック前に3秒のsleepが実行されるので、その処理を潰して実行ファイルに対してブルートフォースするか、チェック処理のところを解析して外部で処理するかの2択ぐらい。

sleep実行処理は、いろいろとチェック処理が入っていて潰すのが面倒くさそうだったので、後者でソルバーを作成。フラグのチェック処理は、HarekazeCTF\{ID[A-Z]{4}X\}$SALT\x00という文字列を結合した文字列のSHA1ハッシュが1fcce7ec44beb72c994e2cd69c462916ca8ec810になるかの確認だけ。(最初\x00部分が抜けていて、ちょっと気づくのに時間がかかった)

# -*- coding: utf-8 -*-
from hashlib import sha1
from itertools import product

def main():
    # HINT: ^HarekazeCTF\{ID[A-Z]{4}X\}$
    '''
    debug
    'HarekazeCTF{IDZZZZX}'
    0x7fffffffe100: 0xe9    0xcc    0x8d    0x45    0xa1    0x44    0x28    0xee
    0x7fffffffe108: 0xd2    0xa2    0x2a    0xa8    0x2b    0xfc    0x36    0x80
    0x7fffffffe110: 0xa9    0x37    0xae    0x7d    0x0     0x0     0x0     0x0
    '''
    
    '''
      local_78[0] = '\x1f';
      local_78[1] = 0xcc;
      local_78[2] = 0xe7;
      local_78[3] = 0xec;
      local_78[4] = 0x44;
      local_78[5] = 0xbe;
      local_78[6] = 0xb7;
      local_78[7] = 0x2c;
      local_78[8] = 0x99;
      local_78[9] = 0x4e;
      local_78[10] = 0x2c;
      local_78[11] = 0xd6;
      local_78[12] = 0x9c;
      local_78[13] = 0x46;
      local_78[14] = 0x29;
      local_78[15] = 0x16;
      local_78[16] = 0xca;
      local_78[17] = 0x8e;
      local_78[18] = 0xc8;
      local_78[19] = 0x10;
    '''
    correct_sha1 = [0x1f, 0xcc, 0xe7, 0xec, 0x44, 0xbe, 0xb7, 0x2c,
                    0x99, 0x4e, 0x2c, 0xd6, 0x9c, 0x46, 0x29, 0x16,
                    0xca, 0x8e, 0xc8, 0x10]
    correct_sha1_md = ''.join(map(lambda x: x[2:], map(hex, correct_sha1)))
    
    seed = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
    cnt = 0
    for s in product(seed, repeat=4):
        FLAG = 'HarekazeCTF{ID' + ''.join(s) + 'X}'
        FLAG += 'SALT\x00'
        flag_sha1 = sha1(FLAG.encode('utf-8'))
        flag_md = flag_sha1.hexdigest()
        print(cnt, FLAG, ':', flag_md, correct_sha1_md)
        cnt += 1
        
        if flag_md == correct_sha1_md:
            print('!!!! GET FLAG !!!!')
            break


if __name__ == '__main__':
    main()

'''
$ python3 solve.py
 :
298850 HarekazeCTF{IDRACGX}SALT : c539a4d9c7352680001a03046ad37c697e609d1b 1fcce7ec44beb72c994e2cd69c462916ca8ec810
298851 HarekazeCTF{IDRACHX}SALT : 917aeeb01e576f5c66bd61f86c96e685b9895abf 1fcce7ec44beb72c994e2cd69c462916ca8ec810
298852 HarekazeCTF{IDRACIX}SALT : 1fcce7ec44beb72c994e2cd69c462916ca8ec810 1fcce7ec44beb72c994e2cd69c462916ca8ec810
!!!! GET FLAG !!!!
'''

Flag: HarekazeCTF{IDRACIX}

以上です。