Pwn2Win CTF 2020 Writeup

この大会は2020/5/30 1:37(JST)~2020/6/1 1:37(JST)に開催されました。
今回もチームで参戦。結果は166点で401チーム中104位でした。
自分で解けた問題をWriteupとして書いておきます。

Transcendence Rebellion (Bonus)

Rulesのページの最下部に小さくこう書いてあった。

For the first time, these tiny letters on the bottom of the screen are not a prank. \o/ if you got to this point, means that you probably read all our informations and instructions. And for that, we will award your team with extra points in the competition, after all, reading is FUNDAMENTAL for a competition like this. Use the flag "CTF-BR{RTFM-1s-4-g00d-3xpr3ss10n-v6.0}" on the challenge "Bonus" during the day of the event and guarantee your extra score! ;)
CTF-BR{RTFM-1s-4-g00d-3xpr3ss10n-v6.0}

Androids Encryption (Crypto)

$ nc encryption.pwn2.win 1337
Let's see if you are good enough in symmetric cryptography!

MENU
1 - Encrypt your secret
2 - Encrypt my secret
3 - Exit
Choice: 2
VMYeRX0ORnE4fcQCrw3Y6nP2kRIW2EkAeca9N0Tm+gV9Hhrs4D7jjxzgvKgwvshwZpuwE1TmRR4YVBkvfyBH+A==
MENU
1 - Encrypt your secret
2 - Encrypt my secret
3 - Exit
Choice: 1
Plaintext: YWFhYWFhYWFhYWFhYWFhYQ==
TMHCIRYGvZYpDgpY+/DVgCCsqCa9u3c+EdPBIwRDbUc=
MENU
1 - Encrypt your secret
2 - Encrypt my secret
3 - Exit
Choice:

$ nc encryption.pwn2.win 1337
Let's see if you are good enough in symmetric cryptography!

MENU
1 - Encrypt your secret
2 - Encrypt my secret
3 - Exit
Choice: 1
Plaintext: YQ==
ERROR: Invalid plaintext size

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

■1 - Encrypt your secret
・入力文字をbase64デコードする。
・key1, iv1で暗号化→表示

■2 - Encrypt my secret
・key2, iv2でフラグを暗号化→表示

□暗号化
・blocks: 16バイトごとのブロックの配列
・以下のように暗号化する。

pt0 ^ iv          --(AES-ECB)--> ct0
pt1 ^ (ct0 ^ pt0) --(AES-ECB)--> ct1
                :

・iv + 暗号を返す。

以下次回に使う。
・iv2をkey2で復号したものをiv2にする。
・上記で暗号化したものを16バイトごとのブロックの配列にし、全部ブロックのXORをとったものをkey2とする。

このことから以下のことが言える。

1.を実行する。
・key2は暗号化データをブロックごとにわけたXORになっている。
2.を実行する。
・暗号化されたフラグが表示される。
 ・keyは上記のkey2
 ・ivはこの時表示された先頭16バイトのiv

以上のことからフラグを復号する。

import socket
import base64
from Crypto.Cipher import AES

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

def to_blocks(txt):
    return [txt[i*BLOCK_SIZE:(i+1)*BLOCK_SIZE] for i in range(len(txt)//BLOCK_SIZE)]

def xor(s1, s2):
    return ''.join(chr(ord(a) ^ ord(b)) for a, b in zip(s1, s2))

def decrypt(txt, key, iv):
    bs = len(key)
    blocks = to_blocks(txt)
    ptxt = ''
    aes = AES.new(key, AES.MODE_ECB)
    curr = iv
    for block in blocks:
        ptxt += xor(aes.decrypt(block), curr)
        curr = xor(ptxt[-bs:], block)
    return ptxt

BLOCK_SIZE = 16

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('encryption.pwn2.win', 1337))

data = recvuntil(s, ': ')
print data + '1'
s.sendall('1\n')

try_pt = base64.b64encode('a' * BLOCK_SIZE)
data = recvuntil(s, ': ')
print data + try_pt
s.sendall(try_pt + '\n')
data = recvuntil(s, '\n').rstrip()
print data
key = base64.b64decode(data)[BLOCK_SIZE:]

data = recvuntil(s, ': ')
print data + '2'
s.sendall('2\n')
data = recvuntil(s, '\n').rstrip()
print data

iv = base64.b64decode(data)[:BLOCK_SIZE]
ct = base64.b64decode(data)[BLOCK_SIZE:]

flag = decrypt(ct, key, iv)
print flag

実行結果は以下の通り。

Let's see if you are good enough in symmetric cryptography!

MENU
1 - Encrypt your secret
2 - Encrypt my secret
3 - Exit
Choice: 1
Plaintext: YWFhYWFhYWFhYWFhYWFhYQ==
QpSPVTiHHdlKkLnD6UU1qnoLMwCiCXHn7CNfB/JXOtU=
MENU
1 - Encrypt your secret
2 - Encrypt my secret
3 - Exit
Choice: 2
5Cn0M3UFBWnaLepOTro39KDnvDs+mLyh8XXkWzjKLWWBP7+fxpUAzFRcnjx41Y3FXPHVCbcbXlMIqcbBTu7cMw==
CTF-BR{kn3W_7h4T_7hEr3_4r3_Pc8C_r3pe471ti0ns?!?}
CTF-BR{kn3W_7h4T_7hEr3_4r3_Pc8C_r3pe471ti0ns?!?}