COMPFEST CTF 2022 Writeup

この大会は2022/11/13 2:00(JST)~2022/11/14 2:00(JST)に開催されました。
今回もチームで参戦。結果は1022点で205チーム中17位でした。
自分で解けた問題をWriteupとして書いておきます。

Sanity Check (Misc)

問題にフラグが書いてあった。

COMPFEST14{Go0d_LucK_4nd_H4vE_FuN_8e53852062}

c0rR3ct10n (Forensics)

PNGフォーマットを逆転し、PNGシグネチャの逆転部分をJPGのヘッダの逆転部分にしたもののようなので、元に戻す。

#!/usr/bin/env python3
with open('lemaoo.png', 'rb') as f:
    data = f.read()

data = data[::-1]
data = b'\x89PNG\x0d' + data[5:]

with open('lemaoo_fix.png', 'wb') as f:
    f.write(data)


画像は白黒のものになっていて、8ビットで1つの文字になりそうなので、0, 1に置き換え、デコードする。

#!/usr/bin/env python3
from PIL import Image

img = Image.open('lemaoo_fix.png').convert('L')
w, h = img.size

b = ''
for y in range(h):
    for x in range(w):
        white = img.getpixel((x, y))
        if white == 0:
            b += '0'
        else:
            b += '1'

msg = ''
for i in range(0, len(b), 8):
    msg += chr(int(b[i:i+8], 2))
print(msg)

デコード結果は以下の通り。

$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7$#!7https://tinyurl.com/m00nlander$#!7$#!7$#!7$#!7

この文字列の最後の方に以下のURLが埋め込まれている。

https://tinyurl.com/m00nlander

このURLにアクセスすると、m00n.jpgがダウンロードできる。ファイルフォーマットはpngが壊れている状態。シグネチャシグネチャとIENDチャンクを修復し、画像の幅と高さをブルートフォースで修復する。

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

with open('m00n.jpg', 'rb') as f:
    data = f.read()

data = b'\x89PNG\x0d\x0a' + data[6:-4] + b'\xae\x42\x60\x82'

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

found = False
for h in range(1, 2048):
    for w in range(1, 2048):
        width = struct.pack('>I', w)
        height = struct.pack('>I', h)
        ihdr = data[12:16] + width + height + data[24:29]
        crc = struct.pack('!I', binascii.crc32(ihdr))
        if crc == data[29:33]:
            found = True
            out = head + ihdr + tail
            with open('m00n_fix.png', 'wb') as f:
                f.write(out)
            break
    if found:
        break

修復した画像をStegSolveで開き、Red plane 4を見ると、フラグが書いてあった。

COMPFEST14{hHhH_th0u_4re_c0Rr3ct!_634af16261}

Seems Familiar (Cryptography)

$ nc 13.212.50.63 11688
1. Get encrypted flag
2. Encrypt a message
3. Decrypt a message
4. Exit
> 1
Sorry, the get_flag function is currently broken. Please try something else.
1. Get encrypted flag
2. Encrypt a message
3. Decrypt a message
4. Exit
> 3
Sorry, the decrypt function is currently broken. Please try something else.
1. Get encrypted flag
2. Encrypt a message
3. Decrypt a message
4. Exit
> 2
message (in hex) = 01 
ciphertext (in hex): 442a1735d39a53305034ba74eb55e0afe21e409460204df2de0a27cd35f4267db2a827ccc972fc5e44e55fdb7c413e306aa543695b8bf9549b7d9c8a80f3c457ed853e642bdd50432eb9fa3eca74b6678cf2ba2683f3b2df8c6386daa2d413421f982cbb9b1d7711509ac86efb60b383
1. Get encrypted flag
2. Encrypt a message
3. Decrypt a message
4. Exit
> 2
message (in hex) = 01234567
ciphertext (in hex): 36b784caea6e47cda9789dcc4ea2ca2ed3833905172743e725463b44f619d6b49803afe8c2259c7e48a89e026b1fbf8084c35b182479744c533d2c5f83fba464f1216cc1545697cec4177e7364afa0dc7d78ad27d355b5c5f816d089fe4ee6613ef406130989ac3fcddcda5d020f657b
1. Get encrypted flag
2. Encrypt a message
3. Decrypt a message
4. Exit
> 2
message (in hex) = 0123456789ab
ciphertext (in hex): 8da318a802fbfe408038290f7c0044a111df296ee4a9b98fc1bcd55398c1296d7c7a68eb0c82443db079efd8068dd937b4a8dccff6bb1429280310dcd19ba5050d1d6bb373d9e55599b034c1fa8a50c0ad589430c420707ceb69cd2df856b48f2d4410a310e0e22724eca54407b4a842
1. Get encrypted flag
2. Encrypt a message
3. Decrypt a message
4. Exit
> 2
message (in hex) = 0123456789abcdef
ciphertext (in hex): 5fae2af342b436f5960c491aee2bce1f271beea4ec5a11008e99636a79dfcfc03df73c327244438dcd37a6de8afb26fd568df9b09ffad97821a2f744b8f9f865c8b8aa9a03f76aa860623cb12f71457fdcc6d3428471da4f715ed939d9632f1da973d0184612add316f2ac6ffede6299
1. Get encrypted flag
2. Encrypt a message
3. Decrypt a message
4. Exit
> 2
message (in hex) = 0123456789abcdef0123
ciphertext (in hex): bbb38b54ef7f87872ecd3918efd4e550de4d6c9de2dd85b6a3f29dbea516b0a3b8f7f19f3aefde378a70b07b386c9ab0803918ce18eb90f81657ada4394ba195fceff5c1ef61da75564fcd7de2b9aa2adadd5496d09dfce3dc410440b167eb8992e6d0d1a6e8e4f53bd589712fc074bc
1. Get encrypted flag
2. Encrypt a message
3. Decrypt a message
4. Exit
> 2
message (in hex) = 0123456789abcdef01234567
ciphertext (in hex): 7ed453a2f1dbe838c54b6cdf437bb2f3cb8ee75fbe2ac4ff6c25b734350abe4e92271bb74e90f3e1fecb27226e8435f21d7183c871d75949a4fdcecdbb0d804ff7d6b3d4f2eae8e141c7ff46df63c975d726f7008407fe831b7021ec9c50c68d31ec29e52d7e1d7430d8a3856602e099
1. Get encrypted flag
2. Encrypt a message
3. Decrypt a message
4. Exit
> 2
message (in hex) = 0123456789abcdef0123456789ab
ciphertext (in hex): 5ccd969f2e70cbd0f8136874ec7c299d88f500f79976b1bf0d559d80ff3d63818f16d8027144485ac0048ed7e2d9845b06974b2332dfa59c86e68d648fdf3831b938c16fcb1191537be5a30672cdb0d9143430e69eb988302d5b6e02cead5ea497a1394817f38b7b37bc124ba0fa28a7
1. Get encrypted flag
2. Encrypt a message
3. Decrypt a message
4. Exit
> 2
message (in hex) = 0123456789abcdef0123456789abcd
ciphertext (in hex): d08d6b104b9c6d8b39de04ad39d7d4753760b21d332aa554f193247ce8b80803e412243526544715a4178b7be31490b6f7937ca2404626b3e1cc903051210a96275353032c7c9f5887460f6ea1c89b0ed5b8b407db5d5e3e9ad20266939348cd0c812f0d51827a447479dfb1d1693fe2
1. Get encrypted flag
2. Encrypt a message
3. Decrypt a message
4. Exit
> 2
message (in hex) = 0123456789abcdef0123456789abcdef
ciphertext (in hex): eeba4020a92f51ccb08f40230098e8c5066c6fdf5cc92ac89a988d55c087bbaeea46f1572482ba05ca7df9e37b0f51a89e20f8a65bce827356b4742c36d3a65fc2fd9a41f5b37df1a551d61051659f22c0f86f2315a772aa7b0d4b0765ce5069684bba6974f816298fb9a44f40579696
1. Get encrypted flag
2. Encrypt a message
3. Decrypt a message
4. Exit
> 2
message (in hex) = 0123456789abcdef0123456789abcdef01
ciphertext (in hex): eeba4020a92f51ccb08f40230098e8c5442a1735d39a53305034ba74eb55e0afe21e409460204df2de0a27cd35f4267db2a827ccc972fc5e44e55fdb7c413e306aa543695b8bf9549b7d9c8a80f3c457ed853e642bdd50432eb9fa3eca74b6678cf2ba2683f3b2df8c6386daa2d413421f982cbb9b1d7711509ac86efb60b383
1. Get encrypted flag
2. Encrypt a message
3. Decrypt a message
4. Exit

$ nc 13.212.50.63 11688
1. Get encrypted flag
2. Encrypt a message
3. Decrypt a message
4. Exit
> 2
message (in hex) = 1111111111111111111111111111111111111111111111111111111111111111
ciphertext (in hex): 660995f5cbd73d0172f762fb6e53ab93660995f5cbd73d0172f762fb6e53ab93170dc1d2c55ff93f927ee5c27856acb793d6ec9cbcf87fe06773e72ee849748455cfb2892c070b19f0d812c7b7218691fd3761aa5d84f844df1bca34f96a5a38a0fb000c40b54c5f55fe00906ee8ef98bde103065d72c722afc0121d0b60c176
1. Get encrypted flag
2. Encrypt a message
3. Decrypt a message
4. Exit

AES暗号で暗号ブロックの構成は以下のようになると考えられる。

0123456789abcdef
IIIIIIIIIIIIIIII
FFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFF
PPPPPPPPPPPPPPPP

AES暗号のECBモードと推測し、1文字ずつはみ出させて、同じ暗号となるかを確認しながら、フラグを割り出す。

0123456789abcdef
XXXXXXXXXXXXXXXF
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXF
FFFFFFFFFFFFFFFF
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(('13.212.50.63', 11688))

flag = ''
for i in range(96):
    for code in range(32, 127):
        print('[+] flag :', flag)
        try_pt = ('X' * 15 + flag)[-15:] + chr(code) + 'X' * (95 - i)
        try_pt_hex = try_pt.encode().hex()

        data = recvuntil(s, b'> ')
        print(data + '2')
        s.sendall(b'2\n')
        data = recvuntil(s, b'= ')
        print(data + try_pt_hex)
        s.sendall(try_pt_hex.encode() + b'\n')
        data = recvuntil(s, b'\n').rstrip()
        print(data)

        ct = bytes.fromhex(data.split(' ')[-1])
        block0 = ct[:16]
        block6 = ct[96:112]
        if block0 == block6:
            flag += chr(code)
            break
    if flag[-1] == '}':
        break

print('[*] flag:', flag)

実行結果は以下の通り。

        :
1. Get encrypted flag
2. Encrypt a message
3. Decrypt a message
4. Exit
> 2
message (in hex) = 61634c455f663937616533613034377c58
ciphertext (in hex): 65ddb452545d43e30f6eea797b9fe25a828c7d5af5ff09a31a41f1514c89b57a375ac2a114358aa7b0805375e4c76ed098d40a5f13cc2db219e5227ccf753e666638eca199133385fcfeab15659785900e1919c1a346ef44220ad67b5a4aa1878332f7775dbc529bc7a9deff5ddada4354bd510b4278397c62cff94a61157493
[+] flag : COMPFEST14{1nDeP3ndenT_bLoCK_ENCryPt10n_w1Th_fl4G_ApP3nDED_0f_course_iTS_eCB_oracLE_f97ae3a047
1. Get encrypted flag
2. Encrypt a message
3. Decrypt a message
4. Exit
> 2
message (in hex) = 61634c455f663937616533613034377d58
ciphertext (in hex): 8332f7775dbc529bc7a9deff5ddada43828c7d5af5ff09a31a41f1514c89b57a375ac2a114358aa7b0805375e4c76ed098d40a5f13cc2db219e5227ccf753e666638eca199133385fcfeab15659785900e1919c1a346ef44220ad67b5a4aa1878332f7775dbc529bc7a9deff5ddada4354bd510b4278397c62cff94a61157493
[*] flag: COMPFEST14{1nDeP3ndenT_bLoCK_ENCryPt10n_w1Th_fl4G_ApP3nDED_0f_course_iTS_eCB_oracLE_f97ae3a047}
COMPFEST14{1nDeP3ndenT_bLoCK_ENCryPt10n_w1Th_fl4G_ApP3nDED_0f_course_iTS_eCB_oracLE_f97ae3a047}