Midnight Sun CTF 2020 Quals Writeup

この大会は2020/4/1 9:00(JST)~2020/4/2 9:00(JST)に開催されました。
今回もチームで参戦。結果は126点で527チーム中276位でした。
自分で解けた問題をWriteupとして書いておきます。

Sanity (misc)

freenodeで#midnightsunチャネルに入ると、メッセージにフラグがあった。

midnight{n0_c0ugh1ng_0n_1rc}

Pybonhash (crypto, re)

pycをデコンパイルする。

$ uncompyle6 pybonhash.cpython-36.pyc 
# uncompyle6 version 3.6.5
# Python bytecode 3.6 (3379)
# Decompiled from: Python 2.7.12 (default, Oct  8 2019, 14:14:10) 
# [GCC 5.4.0 20160609]
# Warning: this version has problems handling the Python 3 byte type in contants properly.

# Embedded file name: pybonhash.py
# Compiled at: 2020-03-28 22:11:38
# Size of source mod 2**32: 1017 bytes
import string, sys, hashlib, binascii
from Crypto.Cipher import AES
from flag import key
if not len(key) == 42:
    raise AssertionError
else:
    data = open(sys.argv[1], 'rb').read()
    if not len(data) >= 191:
        raise AssertionError
FIBOFFSET = 4919
MAXFIBSIZE = len(key) + len(data) + FIBOFFSET

def fibseq(n):
    out = [
     0, 1]
    for i in range(2, n):
        out += [out[(i - 1)] + out[(i - 2)]]

    return out


FIB = fibseq(MAXFIBSIZE)
i = 0
output = ''
while i < len(data):
    data1 = data[(FIB[i] % len(data))]
    key1 = key[((i + FIB[(FIBOFFSET + i)]) % len(key))]
    i += 1
    data2 = data[(FIB[i] % len(data))]
    key2 = key[((i + FIB[(FIBOFFSET + i)]) % len(key))]
    i += 1
    tohash = bytes([data1, data2])
    toencrypt = hashlib.md5(tohash).hexdigest()
    thiskey = bytes([key1, key2]) * 16
    cipher = AES.new(thiskey, AES.MODE_ECB)
    enc = cipher.encrypt(toencrypt)
    output += binascii.hexlify(enc).decode('ascii')

print(output)
# okay decompiling pybonhash.cpython-36.pyc

これから以下のことが読み取れる。

・keyの長さは42
・dataの長さは191以上
・FIB: フィボナッチ数列

総当たりでAES復号して、md5の32バイトになるものを探す。そこからさらに2バイト文字でそのmd5になるものを探す。そこから鍵を割り出すことができれば、フラグが取得できる。

from Crypto.Cipher import AES
import hashlib

def fibseq(n):
    out = [0, 1]
    for i in range(2, n):
        out += [out[(i - 1)] + out[(i - 2)]]

    return out

def is_hash(s):
    chars = '0123456789abcdef'
    for c in s:
        if c not in chars:
            return False
    return True

def rev_hash(h):
    for code1 in range(256):
        for code2 in range(256):
            text = chr(code1) + chr(code2)
            if hashlib.md5(text).hexdigest() == h:
                return text
    return 'Error'

FIBOFFSET = 4919
LEN_KEY = 42
LEN_DATA = 191
MAXFIBSIZE = LEN_KEY + LEN_DATA + FIBOFFSET
FIB = fibseq(MAXFIBSIZE)

with open('hash.txt', 'r') as f:
    output = f.read().rstrip()

msg = ''
j = 0
key = [0] * LEN_KEY
for i in range(0, len(output), 64):
    enc = output[i:i+64].decode('hex')
    found = False
    for key1 in range(256):
        for key2 in range(256):
            thiskey = (chr(key1) + chr(key2)) * 16
            cipher = AES.new(thiskey, AES.MODE_ECB)
            dec = cipher.decrypt(enc)
            if is_hash(dec):
                found = True
                h = dec
                if j < LEN_DATA:
                    key[(j + FIB[(FIBOFFSET + j)]) % LEN_KEY] = key1
                    j += 1
                    key[(j + FIB[(FIBOFFSET + j)]) % LEN_KEY] = key2
                    j += 1
                break
        if found:
            break
    msg += rev_hash(h)

print msg

flag = ''
for k in key:
    flag += chr(k)

print flag

実行結果は以下の通り。

Wee'r  n  Intkneg
nrm o ms
ihkw
 n u' tsuwnii'kn eorenAwWwwnoree nt'ti wgsk ouenm
Akhies  e yrI
ue kfnn  ne n'
eWee'r  n  Intkneg
nrm o ms
ihkw
 n u' tsuwnii'kn eorenAwWwwnoree nt'ti wgsk ouenm
Akhies  e
midnight{xwJjPw4Vp0Zl19xIdaNuz6zTeMQ1wlNP}
midnight{xwJjPw4Vp0Zl19xIdaNuz6zTeMQ1wlNP}