WMCTF 2020 | game

CBCモード

IV固定のCBCモードでECB のChosen Plaintext Attackのようなものをやれという問題。リクエストの回数に制限がなく、時間も20minと十分にとってあったので、適切なサーバを使って高速ネットワークに頼ればなんとかなる

IVがわかっているので最初のblockがAAAA... になるように調節して、あとはChosen Plaintext Attackの要領をやるだけで良い。なかなかおもしろかった

from ptrlib import Socket, xor
import sys
from binascii import hexlify
import proofofwork

# sock = Socket("localhost", 10000)
sock = Socket("170.106.35.18", 16442)
# sock = Socket("81.68.174.63",  16442)

postfix, hash = sock.recvregex(r"sha256\(XXXX\+([0-9a-zA-Z]+)\) == ([0-9a-f]+)")
s = proofofwork.sha256(hash.decode(), text=b'?' * 4 + postfix)
print("[+] PoW: {}".format(s[:4]))
sock.sendline(s[:4])



ivhex = sock.recvregex("IV is: ([0-9a-f]+)")[0]
iv = bytes.fromhex(ivhex.decode())


def encrypt(m):
    global iv
    sock.sendlineafter("> ", "1")
    sock.sendlineafter("(in hex): ", hexlify(m))
    result = bytes.fromhex(sock.recvline().decode())
    iv = result[-16:]
    return result


def calc_prefix():
    return xor(b"A" * 16, iv)


def calc_block(known):
    if len(known) < 16:
        return b"A" * (15 - len(known))

    l = len(known) % 16
    if l == 0:
        return b"A" * 15
    else:
        return b"A" * (15 - l)


known = b""
for x in range(1, 4):
    for i in range(16):

        block = calc_block(known)
        result = encrypt(calc_prefix() + block)
        wannaget = result[16*x:16*(x+1)]

        for b in range(256):
            sys.stdout.write("{},".format(b))
            sys.stdout.flush()
            prefix = calc_prefix()
            block = calc_block(known) + known + bytes([b])

            result = encrypt(prefix + block)
            result = result[16*x:16*(x+1)]
            if result == wannaget:
                known += bytes([b])
                break
        else:
            raise Exception(":(")

        sys.stdout.write("\n")
        print([b for b in known])

sock.sendlineafter("> ", "2")
sock.sendlineafter("(in hex): ", hexlify(known))
print(sock.recvline())