TFC CTF 2023 Writeup

この大会は2023/7/28 19:00(JST)~2023/7/30 19:00(JST)に開催されました。
今回もチームで参戦。結果は2447点で970チーム中29位でした。
自分で解けた問題をWriteupとして書いておきます。

RULES (MISC, WARMUP)

ルールのページを見るとフラグのサンプルが書いてあった。

TFCCTF{Th15_3d1t10n_15_rus7y!}

DOWN BAD (FORENSICS, WARMUP)

IHDRチャンクのCRCがおかしい。画像の高さが間違っていると推測できるので、CRCが合うよう高さを変更する。

#!/usr/bin/env python3
import struct
import binascii

with open('down_bad.png', 'rb') as f:
    data = f.read()

head = data[:12]
tail = data[29:]

for h in range(1168, 2049):
    height = struct.pack('>I', h)
    ihdr = data[12:20] + height + data[24:29]
    crc = struct.pack('!I', binascii.crc32(ihdr))
    if crc == data[29:33]:
        out = head + ihdr + tail
        with open('down.png', 'wb') as f:
            f.write(out)
        break


下の方にフラグが現れた。

TFCCTF{28ae25c96850245ffdd70a880158f9f3}

MAYDAY! (CRYPTO, WARMUP)

NATO phonetic alphabetのようだ。https://en.wikipedia.org/wiki/NATO_phonetic_alphabetを参考に復号する。

WH4T-AR3-YOU-S1NKING-4B0U7
TFCCTF{WH4T-AR3-YOU-S1NKING-4B0U7}

DIZZY (CRYPTO, WARMUP)

文字と位置のペアがスペース区切りで書いてある。該当する位置に文字を書いていく。

          111111111122222222223333333333
0123456789012345678901234567890123456789
TFCCTF{th15_ch4ll3ng3_m4k3s_m3_d1zzy_;d}
TFCCTF{th15_ch4ll3ng3_m4k3s_m3_d1zzy_;d}

ALIEN MUSIC (CRYPTO, EASY)

音階のアルファベット表記のような暗号。

>>> hex(ord('T'))[2:]
'54'
>>> hex(ord('F'))[2:]
'46'
>>> hex(ord('C'))[2:]
'43'
>>> hex(ord('{'))[2:]
'7b'
>>> hex(ord('}'))[2:]
'7d'

以上から推測しながら、対応づける。

A  -> 0
A# -> 1
B  -> 2
C  -> 3
C# -> 4
D  -> 5
D# -> 6
E  -> 7
F  -> 8
F# -> 9
1  -> a
2  -> b
3  -> c
4  -> d
5  -> e
6  -> f
#!/usr/bin/env python3
enc = 'DC# C#D# C#C C#C DC# C#D# E2 C#5 CA EC# CC DE CA EB EC# D#F EF# D6 D#4 CC EC EC CC# D#E CC E4'
enc = enc.replace('1', 'a')
enc = enc.replace('2', 'b')
enc = enc.replace('3', 'c')
enc = enc.replace('4', 'd')
enc = enc.replace('5', 'e')
enc = enc.replace('6', 'f')
enc = enc.replace('A#', '1')
enc = enc.replace('A', '0')
enc = enc.replace('B', '2')
enc = enc.replace('C#', '4')
enc = enc.replace('C', '3')
enc = enc.replace('D#', '6')
enc = enc.replace('D', '5')
enc = enc.replace('E', '7')
enc = enc.replace('F#', '9')
enc = enc.replace('F', '8')
enc = enc.split(' ')

flag = ''
for c in enc:
    flag += chr(int(c, 16))
print(flag)
TFCCTF{N0t3W0rthy_m3ss4g3}

RABID (CRYPTO, EASY)

base64文字列に余計な文字が含まれている。CyberChefでbase64結果を確認しながら、文字を削っていく。

VEZDQ1RGe13kwdV9yNGIxZF9kMGc/IT8hPyE/IT8hPi8+Pz4/PjEyMzkwamNhcHNrZGowOTFyYW5kb21sZXR0ZXJzYW5kbnVtYmVyc3JlZWVlMmozfQ==
TFCCTF{]äÁÕ}Èшő}Áœü„ü„ü„ü„ü„ø¼øüøüøÄÈÌäÁ©…ÁÍ­‘¨ÀäÅÉ…¹‘½µ±•ÑÑ•ÉÍ…¹‘¹Õµ‰•ÉÍÉ•••”ɨÍô
VEZDQ1RGe3kwdV9yNGIxZF9kMGc/IT8hPyE/IT8hPi8+Pz4/PjEyMzkwamNhcHNrZGowOTFyYW5kb21sZXR0ZXJzYW5kbnVtYmVyc3JlZWVlMmozfQ==
TFCCTF{y0u_r4b1d_d0g?!?!?!?!?!>/>?>?>12390jcapskdj091randomlettersandnumbersreeee2j3}
TFCCTF{y0u_r4b1d_d0g?!?!?!?!?!>/>?>?>12390jcapskdj091randomlettersandnumbersreeee2j3}

CYPHEREHPYC (CRYPTO, MEDIUM)

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

・KEY: ある文字列の2回繰り返し
・initial_cipher: 16進数文字列→hexデコード
・cipher: initial_cipherをパディングし、AES-ECB暗号化したもの
・cipherの16進数表記を表示
・cipher: cipherをパディングし、AES-ECB暗号化したもの
・cipherの16進数表記を表示
・cipher: cipherをパディングし、AES-ECB暗号化したもの
・result: 16進数文字列→hexデコード
・cipherとresultが一致する場合、フラグを表示

KEYは固定なので、2回アクセスすると、1回目のresultがわかる。

$ nc challs.tfcctf.com 31797
Initial HEX: 10101010101010101010101010101010
45163e33272d0b15cb72f8a9ad51d58445163e33272d0b15cb72f8a9ad51d584
68afa11f3c8c1d4632e8b7faebfeb45668afa11f3c8c1d4632e8b7faebfeb45645163e33272d0b15cb72f8a9ad51d584
Result HEX: 

$ nc challs.tfcctf.com 31797
Initial HEX: 45163e33272d0b15cb72f8a9ad51d58445163e33272d0b15cb72f8a9ad51d584
68afa11f3c8c1d4632e8b7faebfeb45668afa11f3c8c1d4632e8b7faebfeb45645163e33272d0b15cb72f8a9ad51d584
60b9b8d9446bb248e86bcedd1292537d60b9b8d9446bb248e86bcedd1292537d68afa11f3c8c1d4632e8b7faebfeb45645163e33272d0b15cb72f8a9ad51d584
Result HEX: 

$ nc challs.tfcctf.com 31797
Initial HEX: 10101010101010101010101010101010
45163e33272d0b15cb72f8a9ad51d58445163e33272d0b15cb72f8a9ad51d584
68afa11f3c8c1d4632e8b7faebfeb45668afa11f3c8c1d4632e8b7faebfeb45645163e33272d0b15cb72f8a9ad51d584
Result HEX: 60b9b8d9446bb248e86bcedd1292537d60b9b8d9446bb248e86bcedd1292537d68afa11f3c8c1d4632e8b7faebfeb45645163e33272d0b15cb72f8a9ad51d584
TFCCTF{3v3ryth1ng_1s_r3v3rs3d_1n_th3_3nd}
TFCCTF{3v3ryth1ng_1s_r3v3rs3d_1n_th3_3nd}

AES CTF TOOL V1 (CRYPTO, MEDIUM)

$ nc challs.tfcctf.com 30796

    1. Encrypt Data
    q. Quit
> 1
Data (HEX): 11
85002691171df16280981249612d09379390287d25ca1975aa8b6173044513373dfa81168619115150b32e5b49f9e230051f1a65a2a879b02dfbd3aad9d4a5fcda9b6f6504f626c40d2a8c1d8b3628cf

    1. Encrypt Data
    q. Quit
> 1
Data (HEX): 1111
35f699049a7a32cc6d0c66ad5605c299386f6df4ad17887a32e2b210c55e645f90e9f19d88bdf08bb7475432ff4989de683a26922e3934b6cc7b24541d6118258ff392c88c920355af4950f3bead013d

    1. Encrypt Data
    q. Quit
> 1
Data (HEX): 111111
ea42625c60d0f17023354279abb9282fa0351c3e8aec2bf3d26b2ef9d123a43310a458a22431862ae3030d47d6092ab4e976927b64abc93e8f5625a1181837de445509ce391ab51535737ba8af2bbda7

    1. Encrypt Data
    q. Quit
> 1
Data (HEX): 11111111
8413ba6c4088575f07217d22e5cd8c167b10d44f4774f821159f758ee5fc7e026bcdd7a7746faa51be74cffb93caa01062b649117b0378cfdd6036e56733c0cc557527ab44e966690f52a262a2ccf79d

    1. Encrypt Data
    q. Quit
> 1
Data (HEX): 1111111111
6bb8d79dbf7979038c32443e24849949f49448acb6c51566653658264c3e02be864b7e975f22ba73d84a3a17343015e84b3a8dc43072898f64e5f8f5e760457aea672c9b8da2d0fe9161abed835d6fa1

    1. Encrypt Data
    q. Quit
> 1
Data (HEX): 111111111111
636c8da4d1573a3d9c83b62f6454a08cbe7b5bd8d3b9397530d687aa2d40548df9df1a4f0788881227a2a38a49306daf8513662e21dfcde24b5054fc8f90cd91f72893a09af0ef372551d63b9007cad8

    1. Encrypt Data
    q. Quit
> 1
Data (HEX): 11111111111111
a2b27fbfa9408da5a6fd4fd874709622779e3c1a623027cd2b76d99678c3b9c7a96f30dae0f6bdc64c42b74977a3d418c8f490decb20d864d630f7fb881cb4805c0279a39d48020848cb9d5a52e85d79

    1. Encrypt Data
    q. Quit
> 1
Data (HEX): 1111111111111111
cf632b307e2d852a6040a2c265372133cca1d9224b47912af1d7f2f75fc49485c20247c148f1f7c964e01ee336eaec25f5a425c7756c8fa1a4561388044cd93f66c5a5ea6b1f69404865edf7f15dda50

    1. Encrypt Data
    q. Quit
> 1
Data (HEX): 111111111111111111
cf632b307e2d852a6040a2c265372133ff329119ddc70f9ac08b523d63f11ce1ab72b36d501dd59af587e39c484189a574d4264c7ce2461034d43000c662232773bb45d526085f617aa9d95c712f576d

    1. Encrypt Data
    q. Quit
> 1
Data (HEX): 11111111111111111111
cf632b307e2d852a6040a2c265372133b8cc6915d04275246d8bf3bfe1ef51f5adf7a00091e4b90b5f0450994f448b3f80069efc633d7ba28a927e7781c5251f4e947a0c6d59c5755756b9b2b094a66b

    1. Encrypt Data
    q. Quit
> 1
Data (HEX): 1111111111111111111111
cf632b307e2d852a6040a2c2653721335366a5b55cd91d13629a76f239c1d3a6633922a69e40adb04a96d1e53a9777c29762a90ee0b00dbe89d56ad69c6cb4465f893157b0ad77a901acb5c464368fcb

    1. Encrypt Data
    q. Quit
> 1
Data (HEX): 111111111111111111111111
cf632b307e2d852a6040a2c265372133a4675c1c5799f1be38b8122bfe241b2c61cf61c2b7274f2769ca8dea85fdcdc21aba12746c4a4f10ef7a9461c270bfd8ce5d534393c97d909bce95c1629887a2

    1. Encrypt Data
    q. Quit
> 1
Data (HEX): 11111111111111111111111111
cf632b307e2d852a6040a2c2653721337c8ad10490cd369b96c2f62717a5fb9f86a82256f37a76296317b2dccd3052d5db22efa93b66c946e05269f98a9d4ce0ad4b3e83e2b2fd12d30152cb6fd498da

    1. Encrypt Data
    q. Quit
> 1
Data (HEX): 1111111111111111111111111111
cf632b307e2d852a6040a2c265372133d1372126a091c2c0f5f0b1348763dbe86fbcd6e210e63ccb2e95c295661add06c37bc3b16702927aaf759a25f13029b0b9b7e0e49e503bb5668582f4ff34d66c

    1. Encrypt Data
    q. Quit
> 1
Data (HEX): 111111111111111111111111111111
cf632b307e2d852a6040a2c265372133c0ca5cd5307d24a0b6d9a037a03cbe9ff37864afafc70a669f8f67a49face404859714360d0cb79a56f7870760074b8a94493b17ad11210cd1e6b4ecca18551a

    1. Encrypt Data
    q. Quit
> 1
Data (HEX): 11111111111111111111111111111111
cf632b307e2d852a6040a2c2653721339d6399eaf717efa28b086755a0745ba588040d348a6cf7e92acfacbe3733745f3e88ec44488bb611ee84857d435cf555f31b9bbd820d397eaa38af12a61e3652

    1. Encrypt Data
    q. Quit
> 1
Data (HEX): 1111111111111111111111111111111111
cf632b307e2d852a6040a2c265372133b5a11da5774b87b51f418d6f59bdf1a79390287d25ca1975aa8b6173044513373dfa81168619115150b32e5b49f9e230051f1a65a2a879b02dfbd3aad9d4a5fcda9b6f6504f626c40d2a8c1d8b3628cf

    1. Encrypt Data
    q. Quit
> 1
Data (HEX): 1111111111111111111111111111111111111111111111111111111111111111
cf632b307e2d852a6040a2c26537213395dec1915bf16e17255083e484eba6b19d6399eaf717efa28b086755a0745ba588040d348a6cf7e92acfacbe3733745f3e88ec44488bb611ee84857d435cf555f31b9bbd820d397eaa38af12a61e3652

    1. Encrypt Data
    q. Quit
Data (HEX): 111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
cf632b307e2d852a6040a2c26537213395dec1915bf16e17255083e484eba6b195dec1915bf16e17255083e484eba6b19d6399eaf717efa28b086755a0745ba588040d348a6cf7e92acfacbe3733745f3e88ec44488bb611ee84857d435cf555f31b9bbd820d397eaa38af12a61e3652

    1. Encrypt Data
    q. Quit

おそらくAES暗号のCBCモードで、先頭8バイトに固定文字列が入っている。

0123456789abcdef
SSSSSSSSXXXXXXXX
XXXXXXXXXFFFFFFF
FFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFF
PPPPPPPPPPPPPPPP

フラグの長さは55であると思われる。
ECBモードより面倒だが、1バイトずつはみ出させ、ブルートフォースでフラグを求めることができる。1回目に(1)のように指定し、5ブロック目の暗号を取得する。次に(2)のように?をブルートフォースし、5ブロック目の暗号が同じになるものを探す。これを繰り返す。

(1)フラグはみ出し
0123456789abcdef
SSSSSSSSXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXF
FFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFPP

(2)ブルートフォース
0123456789abcdef
SSSSSSSSXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXX?
FFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFP
#!/usr/bin/env python3
import socket

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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('challs.tfcctf.com', 31706))

flag = ''
for i in range(55):
    payload = (b'X' * (71 - i)).hex()
    data = recvuntil(s, b'> ')
    print(data + '1')
    s.sendall(b'1\n')
    data = recvuntil(s, b': ')
    print(data + payload)
    s.sendall(payload.encode() + b'\n')
    data = recvuntil(s, b'\n').rstrip()
    print(data)
    block5 = data[32*4:32*5]

    for code in range(32, 127):
        payload = ((b'X' * (71 - i)) + flag.encode() + bytes([code])).hex()
        data = recvuntil(s, b'> ')
        print(data + '1')
        s.sendall(b'1\n')
        data = recvuntil(s, b': ')
        print(data + payload)
        s.sendall(payload.encode() + b'\n')
        data = recvuntil(s, b'\n').rstrip()
        print(data)
        if data[32*4:32*5] == block5:
            flag += chr(code)
            break

print(flag)

実行結果は以下の通り。

        :

    1. Encrypt Data
    q. Quit
> 1
Data (HEX): 58585858585858585858585858585858585446434354467b67753335355f746834745f7734735f333435792e2e2e576834542d41626f55745f5468335f30746833725f306e333f7c
5e117e6df84abfd4ec4278a6eeb4dcc2d5878b3b645e5938b898eccb5f3a5cadb124b89e4c5319882f4a8700ce908abed25369139323e530cb38b75c9b56aefb5883af3bb74e2574c007d465db396dfd468060dc4e9e1f11458e58597f2171be99c593829911de9ec9294bc51352766b9779759ce5b40dfae8c92ceab954f6d861d27d66c74e11825b01afc8f5faf8a8

    1. Encrypt Data
    q. Quit
> 1
Data (HEX): 58585858585858585858585858585858585446434354467b67753335355f746834745f7734735f333435792e2e2e576834542d41626f55745f5468335f30746833725f306e333f7d
5e117e6df84abfd4ec4278a6eeb4dcc2d5878b3b645e5938b898eccb5f3a5cadb124b89e4c5319882f4a8700ce908abed25369139323e530cb38b75c9b56aefbe6cd366f4d67e30bfc9b6487ce10f25d468060dc4e9e1f11458e58597f2171be99c593829911de9ec9294bc51352766b9779759ce5b40dfae8c92ceab954f6d861d27d66c74e11825b01afc8f5faf8a8
TFCCTF{gu355_th4t_w4s_345y...Wh4T-AboUt_Th3_0th3r_0n3?}
TFCCTF{gu355_th4t_w4s_345y...Wh4T-AboUt_Th3_0th3r_0n3?}

AES CTF TOOL V2 (CRYPTO, MEDIUM)

$ nc challs.tfcctf.com 30853
Flag is:  fd083c910e4322d97054a742d6e6cdbc7aeed3cab5b04f539bca7a64bf8a4db76eaeb5a579429bbe848fd57fb9136e1f54ef4630c6c7a0fe4b7a7497247daa15

    1. Encrypt Data
    2. Decrypt Data
    q. Quit
> 1
Data (HEX): 12
fd083c910e4322d97054a742d6e6cdbc26af21a95f580cf077aa6149935652e1

    1. Encrypt Data
    2. Decrypt Data
    q. Quit
> 2
Data (HEX): fd083c910e4322d97054a742d6e6cdbc26af21a95f580cf077aa6149935652e1

    1. Encrypt Data
    2. Decrypt Data
    q. Quit
> 2
Data (HEX): fd083c910e4322d97054a742d6e6cdbc7aeed3cab5b04f539bca7a64bf8a4db76eaeb5a579429bbe848fd57fb9136e1f54ef4630c6c7a0fe4b7a7497247daa15

    1. Encrypt Data
    2. Decrypt Data
    q. Quit
> 2
Data (HEX): fd083c910e4322d97054a742d6e6cdbc7aeed3cab5b04f539bca7a64bf8a4db76eaeb5a579429bbe848fd57fb9136e1f54ef4630c6c7a0fe4b7a7497247daa14
Padding is incorrect.

    1. Encrypt Data
    2. Decrypt Data
    q. Quit

paddinのチェック結果だけがわかる。AES CBC Padding Oracle Attackで復号する。

#!/usr/bin/env python3
import socket
from Crypto.Util.strxor import strxor
from Crypto.Util.Padding import unpad

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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('challs.tfcctf.com', 31485))

data = recvuntil(s, b'\n').rstrip()
print(data)
enc_flag = bytes.fromhex(data.split(' ')[-1])

blocks = [enc_flag[i:i+16] for i in range(0, len(enc_flag), 16)]

flag = b''
for i in range(1, len(blocks)):
    xor_block = b''
    for j in range(16):
        for code in range(256):
            print('[+] flag: ', flag)
            if j != 0:
                print('[+] %d - %d - %d: %s' % (i, j, code, strxor(xor_block, blocks[i - 1][-j:])))
            try_pre_block = b'\x00' * (16 - j - 1) + bytes([code]) + strxor(xor_block, bytes([j + 1]) * j)
            try_cipher = (try_pre_block + blocks[i]).hex()
            data = recvuntil(s, b'> ')
            print(data + '2')
            s.sendall(b'2\n')
            data = recvuntil(s, b': ')
            print(data + try_cipher)
            s.sendall(try_cipher.encode() + b'\n')
            data = recvuntil(s, b'\n').rstrip()
            print(data)
            if 'Padding is incorrect' not in data:
                xor_code = (j + 1) ^ code
                xor_block = bytes([xor_code]) + xor_block
                break
    flag += strxor(blocks[i - 1], xor_block)

flag = unpad(flag, 16).decode()
print('[*] flag:', flag)

実行結果は以下の通り。

                :
                :
[+] flag:  b'TFCCTF{W3ll..._th15_0n3_w4s_4ls0'
[+] 3 - 15 - 199: b'easy!}\t\t\t\t\t\t\t\t\t'

    1. Encrypt Data
    2. Decrypt Data
    q. Quit
> 2
Data (HEX): c787562125d6f9763a4fa2cc757bdbba5da5d91b41d51a8e85a6e2ebaf36565d
Padding is incorrect.
[+] flag:  b'TFCCTF{W3ll..._th15_0n3_w4s_4ls0'
[+] 3 - 15 - 200: b'easy!}\t\t\t\t\t\t\t\t\t'

    1. Encrypt Data
    2. Decrypt Data
    q. Quit
> 2
Data (HEX): c887562125d6f9763a4fa2cc757bdbba5da5d91b41d51a8e85a6e2ebaf36565d

[*] flag: TFCCTF{W3ll..._th15_0n3_w4s_4ls0_easy!}
TFCCTF{W3ll..._th15_0n3_w4s_4ls0_easy!}