SekaiCTF 2022 Writeup

この大会は2022/10/1 1:00(JST)~2022/10/3 1:00(JST)に開催されました。
今回もチームで参戦。結果は378点で852チーム中238位でした。
自分で解けた問題をWriteupとして書いておきます。

Sanity Check (Misc)

Discordに入り、#announcementチャネルを見ると、トピックにフラグが書いてあった。

https://ctf.sekai.team/ | SEKAI{w31c0m3_t0_th3_w0r1d!}
SEKAI{w31c0m3_t0_th3_w0r1d!}

Let’s Play Osu!Mania (PPC)

"-"と縦に"-"+"#"*X+"-"となっているものの数を答える。縦に順に確認し、カウントする。
以下のスクリプトをSubmitしたら、フラグが表示された。

N = int(input())

lines = []
for i in range(N):
    line = input()
    lines.append(list(line[1:-1]))

count = 0
hold = False
for i in range(4):
    for j in range(N):
        if hold == False and lines[j][i] == '-':
            count += 1
        elif hold == False and lines[j][i] == '#' and lines[j-1][i] == '-':
            hold = True
        elif hold == True and lines[j][i] == '-':
            hold = False

print(count)
SEKAI{wysi_Wh3n_y0u_fuxx1ng_C_727727}

Time Capsule (Crypto)

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

・rand_nums = []
・rand_numsの要素が8個になるまで、以下繰り返し
 ・tmp: ランダム8ビット整数
 ・rand_numsになければ、rand_numsにtmpを追加
・42回以下繰り返し
 ・flag = encrypt_stage_one(flag, rand_nums)
  ・u: (rand_numsの要素, インデックス)のソート配列
  ・res: インデックスから8バイト飛びで連結
・res = encrypt_stage_two(flag.encode('utf-8'))
 ・now:現在時刻の文字列にパディング
 ・random.seed(now)
 ・key: flagの長さ分ランダム8ビット整数の配列
 ・flag + now とkey + [0x42] * len(now)のXOR
・resをflag.encに保存

暗号の末尾18バイトと0x42のXORでnowを割り出す。あとはそれをseedとしてkeyを割り出せば、XORによりencrypt_stage_two関数実行前のflagを割り出せる。あとは8!の総当たりで、"SEKAI{"から始まるものを探す。

#!/usr/bin/env python3
import random
import itertools
import string

def transfer_pos(message, rand_index):
    res = ''
    for i in rand_index:
        for j in range(i, len(message), len(rand_index)):
            res += message[j]
    return res

def decrypt_stage_one(message, rand_index):
    chars = string.ascii_letters[:len(message)]
    tmp_chars = chars
    for _ in range(42):
        tmp_chars = transfer_pos(tmp_chars, rand_index)
    dec = ''
    for i in range(len(message)):
        dec += message[tmp_chars.index(chars[i])]
    return dec

with open('flag.enc', 'rb') as f:
    res = f.read()

now = ''.join([chr(c ^ 0x42) for c in res[-18:]])
random.seed(now)

key = [random.randrange(256) for _ in range(len(res[:-18]))]
tmp_flag = ''.join([chr(c ^ k) for (c, k) in zip(res[:-18], key)])

for c in list(itertools.permutations(range(8))):
    flag = decrypt_stage_one(tmp_flag, list(c))
    if flag.startswith('SEKAI{'):
        print(flag)
        break
SEKAI{T1m3_15_pr3C10u5_s0_Enj0y_ur_L1F5!!!}

Survey (Misc)

アンケートの最後にフラグが書いてあった。

SEKAI{thx_for_playing_SekaiCTF_2022}