CakeCTF 2023 Writeup
Table of Contents
はじめに
2023/11/11-12にかけて開催されたCakeCTF 2023にチームBunkyoWesternsのメンバーとして参加しました。Internationalでは3/729位でした。
例によってチームメンバーが強すぎて僕はほぼ何もしていないのですが、名ばかりのWriteupを残しておきます。
proたちのWriteupは以下の通りです。
- cakectf 2023 - kanonさん
- CakeCTF2023 - チョコラスクさん
- CakeCTF 2023: Gaming VM Writeup - rand0mさん
- CakeCTF 2023 writeup - st98さん
- Word Tower:cheat:214pts - Satokiさん
Rev
Cake Puzzle(56 solved/128pts)
Someone cut a cake and scrambled. nc others.2023.cakectf.com 14001
チームメンバーのpr0xyさんが次のメモを残してくれていたので続きを巻き取りました。
0x4080のMの配列を0x11b5関数のチェックを満たすように(降順にソート)できれば勝ち
M = [0x445856db,0x4c230304,0x0022449f,0x671a96b7,0x6c5644f7,0x7ff46287,0x6ee9c829,0x5cda2e72,0x00000000,0x698e88c9,0x33e65a4f,0x50cc5c54,0x1349831a,0x53c88f74,0x25858ab9,0x72f976d8]
def check():
for i in range(3):
for j in range(3):
if M[i*4 + j] >= M[i*4 + j+1]:
return 1
if M[i*4 + j] >= M[(i+1)*4 + j]:
return 1
return 0
def exchange(idx1, idx2):
M[idx1], M[idx2] = M[idx2], M[idx1]
def search0():
for i in range(3):
for j in range(3):
if M[i*4 + j] == 0:
return (i,j)
def win():
print("OK")
def chall():
while True:
if check() == 0:
win()
break
c = input()[0]
idx1, idx2 = search0()
if c == 'U':
exchange(idx1*4 + idx2, (idx1-1)*4 + idx2)
elif c == 'R':
exchange(idx1*4 + idx2, (idx1-1)*4 + idx2+1 )
elif c == 'D':
exchange(idx1*4 + idx2, (idx1+1)*4 + idx2)
elif c == 'L':
exchange(idx1*4 + idx2, (idx1-1)*4 + idx2-1)
chall()
はい、スライドパズルですね。
上記のスクリプトのバグっている部分を修正して、手動で操作したログを同時に出力してくれるスクリプトは以下の通りです。
# M = [
# 0x445856DB,
# 0x4C230304,
# 0x0022449F,
# 0x671A96B7,
# 0x6C5644F7,
# 0x7FF46287,
# 0x6EE9C829,
# 0x5CDA2E72,
# 0x00000000,
# 0x698E88C9,
# 0x33E65A4F,
# 0x50CC5C54,
# 0x1349831A,
# 0x53C88F74,
# 0x25858AB9,
# 0x72F976D8,
# ]
# Mの値をスライドパズルの0〜15に置き換える
# sorted_indices = sorted(range(len(M)), key=lambda i: M[i])
# sorted_M = [M[i] for i in sorted_indices]
# ans = [0 for i in range(len(M))]
# for i in range(len(M)):
# for j in range(len(M)):
# if sorted_M[i] == M[j]:
# ans[j] = i
# break
# Mの値をスライドパズルの0〜15に置き換えたもの
M = [5, 6, 1, 10, 12, 15, 13, 9, 0, 11, 4, 7, 2, 8, 3, 14]
inp = []
def printM(m=M):
for i in range(4):
for j in range(4):
print(f"{m[i*4+j]:11d}", end="")
print()
print("".join(inp))
print("-" * 43)
def check():
for i in range(4):
for j in range(4):
if M[i * 4 + j] >= M[i * 4 + j + 1]:
return 1
if M[i * 4 + j] >= M[((i + 1) * 4 + j) % 16]:
return 1
return 0
def exchange(idx1, idx2):
M[idx1], M[idx2] = M[idx2], M[idx1]
def search0():
for i in range(4):
for j in range(4):
if M[i * 4 + j] == 0:
return (i, j)
def win():
print("OK")
def chall():
while True:
printM()
if check() == 0:
win()
break
c = input()[0]
inp.append(c)
idx1, idx2 = search0()
if c == "U":
exchange(idx1 * 4 + idx2, (idx1 - 1) * 4 + idx2)
elif c == "R":
exchange(idx1 * 4 + idx2, idx1 * 4 + idx2 + 1)
elif c == "D":
exchange(idx1 * 4 + idx2, (idx1 + 1) * 4 + idx2)
elif c == "L":
exchange(idx1 * 4 + idx2, idx1 * 4 + idx2 - 1)
chall()
print(inp)
一応ソルバを書こうかと思ったのですが、ググるとめぼしいソルバが見つからなかったので、手動でガチャガチャして解きました。スライドパズル コツなどでググると、解き方を履修できるので30分くらい履修して解きました。
手元でガチャガチャすると、UURRRDLLDDLURRRULDDRUULDDLRRULLDRULLDRRRULDRULLDRRULDLLURRRUULDLDRRUULDDRUULDRULLLDDRRRULLURDRULLDRURDLULDRULDRRULLRDLLURRRDLLLURRDLLURRDLLURDRULDLURDRRULLLDRRRLLURRDLULDUDLUDRRRULLDLU
というペイロードが得られるので、それを投げるとflagが得られます。
(*'-') < solve.py
s = "UURRRDLLDDLURRRULDDRUULDDLRRULLDRULLDRRRULDRULLDRRULDLLURRRUULDLDRRUULDDRUULDRULLLDDRRRULLURDRULLDRURDLULDRULDRRULLRDLLURRRDLLLURRDLLURRDLLURDRULDLURDRRULLLDRRRLLURRDLULDUDLUDRRRULLDLU"
from pwn import *
io = remote("others.2023.cakectf.com", 14001)
for ch in s:
io.recvuntil(b"> ")
io.sendline(ch.encode())
print(io.recvuntil(b"\n"))
(*'-') < python solve.py
[+] Opening connection to others.2023.cakectf.com on port 14001: Done
b'CakeCTF{wh0_at3_a_missing_pi3c3_0f_a_cak3}\n'
[*] Closed connection to others.2023.cakectf.com port 14001
絶対にもっと綺麗な解き方がありますが、綺麗な解き方にこだわってflagが取れなければ本末転倒だと思うので、今回はガチャガチャしました。
これ、Rev問題ですけど実質Miscという気がしました。