Crypto CTF 2021 | KeyBase

#cryptoctf2021

#!/usr/bin/env python3

from Crypto.Util import number
from Crypto.Cipher import AES
import os
import sys
import random
from flag import flag

def keygen():
    iv, key = [os.urandom(16) for _ in '01']
    return iv, key

def encrypt(msg, iv, key):
    aes = AES.new(key, AES.MODE_CBC, iv)
    return aes.encrypt(msg)

def decrypt(enc, iv, key):
    aes = AES.new(key, AES.MODE_CBC, iv)
    return aes.decrypt(enc)

def die(*args):
    pr(*args)
    quit()

def pr(*args):
    s = " ".join(map(str, args))
    sys.stdout.write(s + "\n")
    sys.stdout.flush()

def sc():
    return sys.stdin.readline().strip()

def main():
    border = "+"
    pr(border*72)
    pr(border, " hi all, welcome to the simple KEYBASE cryptography task, try to    ", border)
    pr(border, " decrypt the encrypted message and get the flag as a nice prize!    ", border)
    pr(border*72)

    iv, key = keygen()
    flag_enc = encrypt(flag, iv, key).hex()

    while True:
        pr("| Options: \n|\t[G]et the encrypted flag \n|\t[T]est the encryption \n|\t[Q]uit")
        ans = sc().lower()
        if ans == 'g':
            pr("| encrypt(flag) =", flag_enc)
        elif ans == 't':
            pr("| Please send your 32 bytes message to encrypt: ")
            msg_inp = sc()
            if len(msg_inp) == 32:
                enc = encrypt(msg_inp, iv, key).hex()
                r = random.randint(0, 4)
                s = 4 - r
                mask_key = key[:-2].hex() + '*' * 4
                mask_enc = enc[:r] + '*' * 28 + enc[32-s:]
                pr("| enc =", mask_enc)
                pr("| key =", mask_key)
            else:
                die("| SEND 32 BYTES MESSAGE :X")
        elif ans == 'q':
            die("Quitting ...")
        else:
            die("Bye ...")

if __name__ == '__main__':
    main()

鍵全探索してiv みつけるだけっぽい。

flagの後ろのパートCBCモードで復号してみて成功したら鍵で、その鍵をつかってCBCモードの性質をつかって、後ろのブロックの平文と暗号文のペアから前のブロックの暗号文を順に復元していける

from itertools import product
from Crypto.Cipher import AES
from ptrlib import Socket, xor

def cbc_decrypt(key, iv, ciphertext):
    return AES.new(key, mode=AES.MODE_CBC, iv=iv).decrypt(ciphertext)

def ecb_encrypt(key, plaintext):
    return AES.new(key, mode=AES.MODE_ECB).encrypt(plaintext)

def ecb_decrypt(key, ciphertext):
    return AES.new(key, mode=AES.MODE_ECB).decrypt(ciphertext)


sock = Socket("nc 01.cr.yp.toc.tf 17010")
sock.sendlineafter("[Q]uit\n", "G")
flag = bytes.fromhex(sock.recvlineafter("(flag) = ").decode())

payload = b"A" * 32
sock.sendlineafter("[Q]uit\n", "T")
sock.sendlineafter("encrypt:", payload)

ciphertext = sock.recvlineafter("enc = ").decode()  # iv | AA... | AA...
key_prefix = sock.recvlineafter("key = ").decode()

for post in product("012356789abcdef", repeat=4):
    key = bytes.fromhex(key_prefix[:-4] + "".join(post))
    plaintext = cbc_decrypt(key, flag[:16], flag[16:])
    if all([0x20 <= b < 0x7f for b in plaintext]):
        break
else:
    assert False


cdar_block = bytes.fromhex(ciphertext[-32:])
car_block = xor(payload[-16:], ecb_decrypt(key, cdar_block))
iv = xor(payload[:16], ecb_decrypt(key, car_block))

print(cbc_decrypt(key, iv, flag))