RCTF 2019 Writeup

この大会は2019/5/18 10:00(JST)~2019/5/20 10:00(JST)に開催されました。
今回もチームで参戦。結果は1111点で405チーム中46位でした。
自分で解けた問題をWriteupとして書いておきます。

welcome (Misc)

Telegramに入ったら、メッセージにフラグが書いてあった。

RCTF{Welcome_To_RCTF2019}

baby_crypto (Crypto)

サーバ処理は以下のようになっている。

key: ランダム16バイト
iv : ランダム16バイト
salt: key

username: a-zで5文字以上10文字以下で指定
password: a-zで5文字以上10文字以下で指定

cookie: admin:0;username:[username];password:[password]
hv : salt + cookie のsha1
hv_hex : salt + cookie のsha1(hex)

cookie_padded: cookieのpadding

iv + cookie_padded暗号 + hvのhexを表示

cookie入力
復号後、以下の処理
・salt + cookieのsha1が一致しているかをチェック
・;区切りで各値を取り、adminの値が1の場合、フラグを表示

cookieのデータの末尾に";admin:1"が入っているデータでハッシュがわかるものをHash Length Extension Attackからハッシュと、対応するデータを求める。その際saltはkeyなので、長さは16バイトと分かっている。さらにそのデータに対する暗号文をCBC Oracle Padding Attackで生成する。

import socket
import hashpumpy
import binascii

def recvuntil(s, tail):
    data = ''
    while True:
        if tail in data:
            return data
        data += s.recv(1)

def str_xor(s1, s2):
    return ''.join(chr(ord(a) ^ ord(b)) for a, b in zip(s1, s2))

def pad(s):
    l = 16 - len(s) % 16
    return s + chr(l) * l

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('45.76.208.70', 20000))

data = recvuntil(s, ':\n').rstrip()
print data
username = 'admin'
print username
s.sendall(username + '\n')

data = recvuntil(s, ':\n').rstrip()
print data
password = 'admin'
print password
s.sendall(password + '\n')

data = recvuntil(s, '\n').rstrip()
print data
data = recvuntil(s, '\n').rstrip()
print data
mac = data[-40:]

base_str = 'admin:0;username:' + username + ';password:' + password
h, d = hashpumpy.hashpump(mac, base_str, ';admin:1', 16)
pad_d = pad(d)
plain_blocks = [pad_d[i:i+16] for i in range(0, len(pad_d), 16)]

c = ['\x00'*16] * 5
for i in range(4, 0, -1):
    key = ''
    for j in range(16):
        for code in range(256):
            try_iv = 'X' * (15 - len(key)) + chr(code) + str_xor(key, chr(j+1)*j)
            cookie = try_iv + c[i] + 'h' * 20
            cookie = binascii.hexlify(cookie)
            data = recvuntil(s, ':\n').rstrip()
            print data
            print cookie
            s.sendall(cookie + '\n')
            data = recvuntil(s, '\n').rstrip()
            print data
            if data not in 'Invalid padding':
                key = chr(code ^ (j+1)) + key
                break
    c[i-1] = str_xor(plain_blocks[i-1], key)

cookie = binascii.hexlify(''.join(c)) + h
data = recvuntil(s, ':\n').rstrip()
print data
print cookie
s.sendall(cookie + '\n')
data = recvuntil(s, '\n').rstrip()
print data

実行結果は以下の通り。

Input username:
admin
Input password:
admin
Your cookie:
5633290bdd244501f1f689a3bae19fb112b83db0334df13c985d0bba608e6c72862632b10bbd4f2139c57e4b9830079d3f26d77ae8b9e8659e109fc5ed08d42ac06c663a4e1ad2282392ea588611eb610150cc2d
Input your cookie:
58585858585858585858585858585800000000000000000000000000000000006868686868686868686868686868686868686868
Invalid padding
        :
Input your cookie:
e965ebc63e74c43fd695d41737eaaf6a8d5f4d8e991c614853cfd3a37ebe04cb6868686868686868686868686868686868686868
Invalid padding
Input your cookie:
ea65ebc63e74c43fd695d41737eaaf6a8d5f4d8e991c614853cfd3a37ebe04cb6868686868686868686868686868686868686868
Invalid hash
Input your cookie:
9b1196bf405ee414b3f6a175499bd21f8d5f4d8e991c614853cfd3a37ebe04cb6cba5321c2052667775d12198cb964e4932b6e3035a82c1e7b587659aceacff000000000000000000000000000000000803eb104472c0600959f2aaccc645871fabbdbf8
Your flag: RCTF{f2c519ea-567b-41d1-9db8-033f058b4e3e}
RCTF{f2c519ea-567b-41d1-9db8-033f058b4e3e}