Crypto CTF 2021 Writeup

この大会は2021/7/31 1:00(JST)~2021/8/1 1:00(JST)に開催されました。
今回もチームで参戦。結果は108点で443チーム中121位でした。
自分で解けた問題をWriteupとして書いておきます。

Mic Check

ルールのページにフラグが書いてあった。

CCTF{W3lc0me_t0_The_0ne_&_0nly_Crypt0_CTF_Mad3ـw1th_L0ve_f0r_Crypt0!}

Farm

暗号化処理は以下の通り。

・ALPHABET = string.printable[:62] + '\\='
・key生成
 ・64=2**6のため、14個の5乗以下の式を生成
 ・積を返す(14個の5乗以下の式) ※64パターンしかない。
・暗号化
 ・m64: flagをbase64エンコード
 ・pkey = key**5 + key**3 + key**2 + 1
 ・m64の各文字について、ALPHABET[F.index(pkey * maptofarm(chr(m)))]を算出し、結合する。

フラグが'CCTF{'から始まることを前提に、64パターンのキーをブルートフォースし、キーを求める。さらにフラグのbase64エンコード文字の各文字をブルートフォースで復号する。

#!/usr/bin/env sage
from sage.all import *
import string, base64, math

def maptofarm(c):
        assert c in ALPHABET
        return F[ALPHABET.index(c)]

ALPHABET = string.printable[:62] + '\\='

F = list(GF(64))

enc = '805c9GMYuD5RefTmabUNfS9N9YrkwbAbdZE0df91uCEytcoy9FDSbZ8Ay8jj'

keys = [F[i] for i in range(64)]
m64_head = base64.b64encode(b'CCT')

for key in keys:
    pkey = key**5 + key**3 + key**2 + 1
    if ALPHABET[F.index(pkey * maptofarm(chr(m64_head[0])))] == enc[0]:
        print('[+] key =', key)
        break

pkey = key**5 + key**3 + key**2 + 1
print('[+] pkey =', pkey)

m64 = ''
for i in range(len(enc)):
    for m in ALPHABET:
        if ALPHABET[F.index(pkey * maptofarm(m))] == enc[i]:
            m64 += m
            break

print('[+] m64 =', m64)

flag = base64.b64decode(m64.encode()).decode()
print('[*] flag =', flag)

実行結果は以下の通り。

[+] key = z6^5 + 1
[+] pkey = z6^5 + z6^3 + z6^2 + z6
[+] m64 = Q0NURntFbkNyWXA3STBuXzRuRF81dThTVGl0VXRJbjlfaU5fRmkzTGQhfQ==
[*] flag = CCTF{EnCrYp7I0n_4nD_5u8STitUtIn9_iN_Fi3Ld!}
CCTF{EnCrYp7I0n_4nD_5u8STitUtIn9_iN_Fi3Ld!}

KeyBase

$ nc 01.cr.yp.toc.tf 17010
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+  hi all, welcome to the simple KEYBASE cryptography task, try to     +
+  decrypt the encrypted message and get the flag as a nice prize!     +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| Options: 
|	[G]et the encrypted flag 
|	[T]est the encryption 
|	[Q]uit
G
| encrypt(flag) = 9e8574ab8707c03ab04a181283d8bf9c8be334e850570739df1d6cb658bac696
| Options: 
|	[G]et the encrypted flag 
|	[T]est the encryption 
|	[Q]uit
T
| Please send your 32 bytes message to encrypt: 
0123456789abcdef0123456789abcdef
| enc = 0****************************c89b26fae8a39a1af1d9a0c9a33b4198caf
| key = a6346a6148918313e0695e4a3301****
| Options: 
|	[G]et the encrypted flag 
|	[T]est the encryption 
|	[Q]uit
Q
Quitting ...

サーバの処理概要は以下の通り。

・iv, key: ランダム16バイト
・flag_enc: AES-CBC暗号化(hex)
・以下繰り返し
 ・メニュー選択(G/T/Q)表示
 ・'g'選択の場合
  ・flag_enc表示
 ・'t'選択の場合
  ・msg_inp: 32バイト入力
  ・enc: msg_inpのAES-CBC暗号化(hex)
  ・r: 0~4ランダム
  ・s = 4 - r
  ・enc先頭rバイト + '*' * 28 + enc32-sバイト目以降を表示
  ・keyの末尾2バイト以外 + '*' * 4を表示

encの2ブロック目は必ず表示される。AES-CBC暗号なので、以下のようになる。

平文1ブロック目 ^ IV               --(AES暗号)--> 暗号1ブロック目
平文2ブロック目 ^ 暗号1ブロック目  --(AES暗号)--> 暗号2ブロック目

暗号2ブロック目をkeyのブルートフォースで復号し、平文2ブロック目とのXORをする。その結果暗号1ブロック目のわかっている文字が一致しているものを探す。keyがわかれば、同様に1ブロック目を復号し、平文1ブロック目とのXORでIVも割り出せる。あとはflagを復号すればよい。

import socket
from Crypto.Cipher import AES
from Crypto.Util.strxor import strxor

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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('01.cr.yp.toc.tf', 17010))

data = recvuntil(s, '[Q]uit\n').rstrip()
print data + 'g'
s.sendall('g\n')
data = recvuntil(s, '\n').rstrip()
print data
enc_flag = data.split(' ')[-1].decode('hex')

try_pt = 'a' * 32
ct0_0 = ''
ct0_1 = ''
while True:
    data = recvuntil(s, '[Q]uit\n').rstrip()
    print data + 't'
    s.sendall('t\n')
    data = recvuntil(s, '\n').rstrip()
    print data + try_pt
    s.sendall(try_pt + '\n')
    data = recvuntil(s, '\n').rstrip()
    print data
    try_ct = data.split(' ')[-1]
    if '*' not in try_ct[:4]:
        ct0_0 = try_ct[:4]
    if '*' not in try_ct[28:32]:
        ct0_1 = try_ct[28:32]
    ct1 = try_ct[32:]
    data = recvuntil(s, '\n').rstrip()
    print data
    key_part = data.split(' ')[-1][:-4]
    if ct0_0 != '' and ct0_1 != '':
        break

found = False
for k1 in range(256):
    for k2 in range(256):
        key = key_part.decode('hex') + chr(k1) + chr(k2)
        aes = AES.new(key, AES.MODE_ECB)
        pt1 = aes.decrypt(ct1.decode('hex'))
        ct0 = strxor('a' * 16, pt1).encode('hex')
        if ct0[:4] == ct0_0 and ct0[-4:] == ct0_1:
            found = True
            break
    if found:
        break

print '[+] key =', key.encode('hex')
pt0 = aes.decrypt(ct0.decode('hex'))
iv = strxor('a' * 16, pt0)
print '[+] iv =', iv.encode('hex')

aes = AES.new(key, AES.MODE_CBC, iv)
flag = aes.decrypt(enc_flag)
print '[*] flag =', flag

実行結果は以下の通り。

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+  hi all, welcome to the simple KEYBASE cryptography task, try to     +
+  decrypt the encrypted message and get the flag as a nice prize!     +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| Options:
|       [G]et the encrypted flag
|       [T]est the encryption
|       [Q]uitg
| encrypt(flag) = 91e5f649d0f5a1be26406b49a6ccbda235d02e3f64a465299399c24e6fcc76e0
| Options:
|       [G]et the encrypted flag
|       [T]est the encryption
|       [Q]uitt
| Please send your 32 bytes message to encrypt:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
| enc = d46****************************52e3717df731872aca698b41a2eb6dd75
| key = 70f0320c155b873cd3bf78fd0ee0****
| Options:
|       [G]et the encrypted flag
|       [T]est the encryption
|       [Q]uitt
| Please send your 32 bytes message to encrypt:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
| enc = d463****************************2e3717df731872aca698b41a2eb6dd75
| key = 70f0320c155b873cd3bf78fd0ee0****
| Options:
|       [G]et the encrypted flag
|       [T]est the encryption
|       [Q]uitt
| Please send your 32 bytes message to encrypt:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
| enc = d****************************1352e3717df731872aca698b41a2eb6dd75
| key = 70f0320c155b873cd3bf78fd0ee0****
| Options:
|       [G]et the encrypted flag
|       [T]est the encryption
|       [Q]uitt
| Please send your 32 bytes message to encrypt:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
| enc = d4****************************352e3717df731872aca698b41a2eb6dd75
| key = 70f0320c155b873cd3bf78fd0ee0****
| Options:
|       [G]et the encrypted flag
|       [T]est the encryption
|       [Q]uitt
| Please send your 32 bytes message to encrypt:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
| enc = d46****************************52e3717df731872aca698b41a2eb6dd75
| key = 70f0320c155b873cd3bf78fd0ee0****
| Options:
|       [G]et the encrypted flag
|       [T]est the encryption
|       [Q]uitt
| Please send your 32 bytes message to encrypt:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
| enc = d46****************************52e3717df731872aca698b41a2eb6dd75
| key = 70f0320c155b873cd3bf78fd0ee0****
| Options:
|       [G]et the encrypted flag
|       [T]est the encryption
|       [Q]uitt
| Please send your 32 bytes message to encrypt:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
| enc = ****************************01352e3717df731872aca698b41a2eb6dd75
| key = 70f0320c155b873cd3bf78fd0ee0****
[+] key = 70f0320c155b873cd3bf78fd0ee01ca4
[+] iv = 002d5f79d5b5aa201c7c792d69a2fb20
[*] flag = CCTF{h0W_R3cOVER_7He_5eCrET_1V?}
CCTF{h0W_R3cOVER_7He_5eCrET_1V?}