読者です 読者をやめる 読者になる 読者になる

BCTF 2017 Writeup

この大会は2017/4/15 11:00(JST)~2017/4/17 11:00(JST)に開催されました。
今回もチームで参戦。結果は1444点で303チーム中35位でした。
自分で解けた問題をWriteupとして書いておきます。

Hulk (Crypto)

CBCモードのブロック暗号の問題。処理の概要は以下の通り。

request1: 入力
・96バイト以下
・16進数文字

plaintext1 = 入力16進数を文字に変換+フラグ
ciphertext1 = plaintext1の暗号
plaintext1_str = 入力16進数+'|flag'
ciphertext1_str = plaintext1の暗号の16進数表記

request2: 入力
・96バイト以下
・16進数文字

plaintext2 = 入力16進数を文字に変換
ciphertext2 = plaintext2の暗号(iv: ciphertext1の末尾16バイト)
plaintext2_str = 入力16進数
ciphertext2_str = plaintext2の暗号の16進数表記

CBCモードなので、以下のようになる。

[平文1ブロック目] ^ IV                  --(暗号化)--> [暗号文1ブロック目]
[平文2ブロック目] ^ [暗号文1ブロック目] --(暗号化)--> [暗号文2ブロック目]
[平文3ブロック目] ^ [暗号文2ブロック目] --(暗号化)--> [暗号文3ブロック目]

3ブロック目に1バイトずつフラグの先頭が入るように暗号化する。
平文3ブロック目の最終バイトを総当たりで暗号化し、暗号文2ブロック目とIVとのXORを2回目の暗号化に使う。
この結果2回目の暗号文1ブロック目が1回目の暗号文3ブロック目と同じになるものをブルートフォースで探す。

#!/usr/bin/env python
import socket
import re

pattern = 'ciphertext: (.+)'

flag = ''
l_flag = len(flag)
while True:
    for code in range(36, 127):
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect(('202.112.51.217', 9999))
        print 'try %s' % chr(code)
        print flag

        plain1 = '58' * (47 - l_flag)
        data = s.recv(1024)
        data += plain1
        s.sendall(plain1 + '\n')

        data = s.recv(1024)
        m = re.search(pattern, data)
        cipher1_str = m.group(1)
        iv = cipher1_str[2:].strip().decode('hex')[-16:]
        data = s.recv(1024)

        if l_flag < 16:
            try_str = ('58' * (15 - l_flag)).decode('hex') + flag + chr(code)
        else:
            try_str = flag[l_flag-15:l_flag] + chr(code)

        cipher1_2 = cipher1_str[2:].strip().decode('hex')[16:32]
        plain2 = ''
        for i in range(16):
            plain2 += chr(ord(try_str[i]) ^ ord(iv[i]) ^ ord(cipher1_2[i]))
        plain2 = plain2.encode('hex')
        data += plain2
        s.sendall(plain2 + '\n')

        data = s.recv(1024)
        m = re.search(pattern, data)
        cipher2_str = m.group(1)
        if cipher1_str[66:98] == cipher2_str[2:34]:
            flag += chr(code)
            l_flag += 1
            break
    if flag[-1:] == '}':
        break

print flag
bctf{3c1fffb76f147d420f984ac651505905}