VolgaCTF 2023 Qualifier Writeup

この大会は2023/5/14 0:00(JST)~2023/5/15 0:00(JST)に開催されました。
今回もチームで参戦。結果は223点で140チーム中83位でした。
自分で解けた問題をWriteupとして書いておきます。

Recurrent (crypto)

暗号の処理概要は以下の通り。

・p: 2**511以上2**512-1未満のランダム素数
・F = GF(p)
・a, b, s0, n_a, n_b: 1以上p未満のランダム整数
・p, a, b, s0を出力
・A = recurrent(s0, a, b, n_a)
 ・s = s0
 ・以下iが1からn_aまでについて繰り返し実行
  ・s = s + a * i + b
 ・sを返却
・B = recurrent(s0, a, b, n_b)
・A, Bを出力
・master_secret = int(recurrent(A, a, b, n_b))
・key = sha256(master_secret.to_bytes(64, 'big')).digest()[:16]
・iv = sha256(key).digest()[:16]
・flagをパディングして、key, ivを使ってAES-CBC暗号化し、ivの後ろに結合して出力

Aの値はどのように計算されるかを見てみる。

s1 = s0 + a * 1 + b
s2 = s1 + a * 2 + b = s0 + a * 1 + b + a * 2 + b = s0 + a * (1 + 2) + b * 2
s3 = s2 + a * 3 + b = s0 + a * (1 + 2 + 3) + b * 3
    :
sn_a = s0 + a * (1 + 2 + ... + n_a) + b * n_a = A

同様に以下の式になる。

sn_b = s0 + a * (1 + 2 + ... + n_b) + b * n_b = B

以上から以下の式が成り立つ。

master_secret = A + a * (1 + 2 + ... + n_b) + b * n_b = A + B - s0

これでAES暗号(CBCモード)のkey, ivがわかるので、暗号文を復号し、フラグを得る。

#!/usr/bin/env python3
from hashlib import sha256
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

with open('params', 'r') as f:
    params = f.read().splitlines()

p = int(params[0].split(' ')[-1])
s0 = int(params[3].split(' ')[-1])
A = int(params[4].split(' ')[-1])
B = int(params[5].split(' ')[-1])
enc = eval(params[6].split(' ')[-1])

master_secret = (A + B - s0) % p
key = sha256(master_secret.to_bytes(64, 'big')).digest()[:16]
iv = sha256(key).digest()[:16]
ct = enc[16:]
assert iv == enc[:16]

cipher = AES.new(key, AES.MODE_CBC, iv=iv)
flag = unpad(cipher.decrypt(ct), 16).decode()
print(flag)
VolgaCTF{y0u_r3_s0_g00d_1_s33_n0w}

Feedback (misc)

アンケートに答えたら、フラグが表示された。

VolgaCTF{c3aa7ef4a5ff6cd9cf0b94e0}