TUM CTF 2016 Writeup

この大会は2016/10/1 1:00(JST)~2016/10/3 1:00(JST)に開催されました。
今回もチームで参戦。結果は557点で836チーム中50位でした。
自分で解けた問題をWriteupとして書いておきます。

thejoyofpainting(stego 50)

flacファイルが与えられているので、Audacityで開いてみる。スペクトグラム表示にし、サンプリングを16000Hzにしたら、フラグが出てきた。
f:id:satou-y:20161003232234p:plain

hxp{/!\-1'm-f0ur13r0uZ-/!\}

haggis(crypto 100)

コードが与えられているので、読み解く。
AES暗号で最後のブロックの暗号と、最初の方の平文が与えられているので、その条件を満たせば、フラグが表示される。
4ブロック前提に試すと条件を満たすものがないので、5ブロック前提に考える。

plain1: '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x<16進表記の平文長>'
plain2: 'I solemnly swear'
plain3: ' that I am up to'
plain4: ' no good.\0??????'
plain5: '????????????????'

plain5はできるだけいろんなデータを受け入れたいため、パディングは最後の1文字だけで'\x01'前提。

plain4はできるだけ固定長を長くする。
plain4: ' no good.\0XXXXX?'(最後の1文字だけブルートフォース

ただ、これだと該当する平文が見つからない場合があるので、以下のように考える。
plain4: ' no good.\0XXXX??'(最後の2文字だけブルートフォース

この前提で5ブロック目の暗号を復号したものと、4ブロック目の平文とのXORが5ブロック目の平文になることを利用し、最後の1文字が'\x01'になるものをブルートフォースで探すスクリプトを書く。

#!/usr/bin/env python3
import socket
import binascii
from Crypto.Cipher import AES

pad = lambda m: m + bytes([16 - len(m) % 16] * (16 - len(m) % 16))
def haggis_all(m):
    crypt0r = AES.new(bytes(0x10), AES.MODE_CBC, bytes(0x10))
    return crypt0r.encrypt(len(m).to_bytes(0x10, 'big') + pad(m))

def aes_decrypt(m):
    crypt0r = AES.new(bytes(0x10), AES.MODE_ECB)
    return crypt0r.decrypt(m)

def sxor(x, y):
    s1 = binascii.hexlify(x)
    s2 = binascii.hexlify(y)
    ixor = int(s1, 16) ^ int(s2, 16)
    return  binascii.unhexlify(hex(ixor)[2:].zfill(32))

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('104.198.243.170', 2501))

target = s.recv(256).strip(b'\n')
print(b'target = ' + target)
d_target = aes_decrypt(binascii.unhexlify(target))

msg_head = b'I solemnly swear that I am up to no good.\0'
msg_fix = b'XXXX'
msg_tmp = b'000000000000000'

found = 0
for i in range(256):
    for j in range(256):
        msg = msg_head + msg_fix + chr(i).encode() + chr(j).encode() + msg_tmp
        e = haggis_all(msg)
        c4 = e[48:64]
        p5 = sxor(d_target, c4)
        if p5[-1:] == b'\x01':
            msg = msg_head + msg_fix + chr(i).encode() + chr(j).encode() + p5[:-1]
            found = 1
            break
    if found == 1:
        break

h_msg = binascii.hexlify(msg)
print(h_msg)
s.sendall(h_msg + b'\n')
data = s.recv(256)
print(data)
hxp{PLz_us3_7h3_Ri9h7_PRiM1TiV3z}