Loading...
5/4/2025
チーム「卍最強卍」でOptimisticと2人で参加して62位でした。問題をそれなりに残しておくつもりだったけど結構解いちゃってごめん。久々の割には善戦したんじゃなかろうか?
熱海とポルトガルで検索すると姉妹都市らしい。ほぼ決め打ちでやったらあたってウケた。記念碑をつけて適当にググると写真が貼られたサイトが見つかる。
TsukuCTF25{2014/06/06}
曲がってるエレベーターは維持の難しさなどからあまりないことは一般に知られているのはその通り。検索すると、さすがの三菱なんですね(サイト)。
写真を見る感じ、美術館系っぽい?きがする。と思ったら、全設置箇所まとめのページを発見。目grepすると、神奈川県のランドマークプラザっぽい。TsukuCTF25{tokyo-esca.com}
を投げるがincorrect。あれ?1
調べ直して、TsukuCTF25{yokohama-landmark.jp}
を投げてcorrect。
情報がなさすぎる... ので、頑張って点字を解読する。
とーきょーと きゅーせき まさかどづか
東京都旧跡将門塚!!!!!ということで、あとは調べるだけ。
TsukuCTF25{35.6873_139.7627}
写真右端にOTi... Fried Chikenという看板が見えるのでググる。一発で見つかっていい感じ(link)。ジャカルタのチェーン店らしい。
長めの道で、奥が多分T字路になっているっぽいので、それを念頭に探す。日本人のCTFなので、おそらく都市部かつ大通りだろうという予想。
めちゃくちゃ頑張って探すと、見つかる2。TsukuCTF25{-7.3189_110.4971}
。
わからないのでほぼ丸投げ。ある程度調べてもらったやつのリンクを眺めながらそれっぽそうなのを適当に投げていたらあたった(あの?)
あー、これなんだっけ。と言いながらAI丸投げ3。
... しかし、これは機能しません。実際には、JSONパーサーを使用して配列を作成する場合、配列の
length
は常に非負になります。 実際の解法は、配列のプロトタイプを操作してlength
プロパティをオーバーライドすることですが、JSONパースだけではこれは不可能です。 あ、待って!JSONで有効ではない構文を使用してJSON.parse
を失敗させ、ソースコードを確認できるエラーを発生させる必要があります。 いや、実際の解法はJSON.parse
が配列以外のものを返すようにすることです! ...
勝手に自分で解決してた。
❯ curl -X POST -d 'array={"length":-1}' http://challs.tsukuctf.org:28888
TsukuCTF25{l4n_l1n_lun_l4n_l0n}%
完全に専門外なので予定はなかったけど、MMRZの作問分があるはずなので一個ぐらいは解きたいなぁのきもちで取り組む。
ヴィジュネル暗号の実装らしい。tsukuctfの部分が変わらないということは、その部分がおなじになるように設定されている、ということ。とを26で割ったあまりが等しくないといけないので、に注意するとしかありえない。つまり、暗号のtsukuctf
の部分はa
が連続しているはず。あと、keyの文字数が足りなくなった時点で暗号文をkeyとして再利用している。これにa
が連続する部分があるから、その部分は平文が残っているはず。
...ん?tsukuctf
も、a
が連続する部分も8文字なんだよなぁ。これはつまり、周期が8であることを示唆している?じゃないと暗号文が...aaaaaa aa tsukuctf...
にはならないはず。実際に前から8文字で区切っていくとそこが境目になるし、これで決め打って良さそう。はじめの8文字を除いてkeyと暗号文がわかったことになるので、デコーダを書く。
ciphertext="ayb wpg uujmz pwom jaaaaaa aa tsukuctf, hj vynj? mml ogyt re ozbiymvrosf bfq nvjwsum mbmm ef ntq gudwy fxdzyqyc, yeh sfypf usyv nl imy kcxbyl ecxvboap, epa 'avb' wxxw unyfnpzklrq." import string cipher_only_acsii=[] for c in ciphertext: if c in string.ascii_lowercase: cipher_only_acsii.append(c) key = cipher_only_acsii[0:8] def f(c, k): base = ord('a') ci = ord(c) - base ki = ord(k) - base p = (ci-ki+26)%26 return chr((base+p)) idx = 0 ans=[] for c in ciphertext[10:]: if c not in string.ascii_lowercase: ans.append(c) continue p = f(c, key[idx]) ans.append(p) key[idx] = c idx+=1 idx%=8 print(''.join(ans)) """ ❯ python3 dec.py joy this problem or tsukuctf, or both? the flag is concatenate the seventh word in the first sentence, the third word in the second sentence, and 'fun' with underscores. """
ということで、flagはTsukuCTF25{tsukuctf_is_fun}
。
tsukuctf
の部分が変わらないなぁじゃないよ。絶対作るの大変だったでしょこれ。
Xor shiftの部分だけ取り出して適当なseedで試してみると、どうも280回で一周回るっぽい?で、300回の試行があるので
をすればよい。 久々にpwntoolsを使ったので仕様に頭を悩ませつつ、AIに助けてもらいつつ、なんとか書き上げる。
trace = [] idx = 0 WIN = b"Tsukushi: You win! " DRAW = b"Tsukushi: Draw! ...But If you wanna get the flag, you have to win 294 rounds in a row. " ENDGAME = b"Tsukushi: You won 294 times in a row?! That's incredible! " def janken_gen() -> int: global idx if len(trace) < 280: return 0 else: res = (trace[idx]+1)%3 idx += 1 idx %= 280 return res from pwn import * #io = process(['uv', 'run', 'server.py'], env={'FLAG':'STATUS_OK'}) io = remote('challs.tsukuctf.org', 30057) while True: isEnd = False while True: print(io.recvuntil(b"Rock, Paper, Scissors... Go! (Rock: 0, Paper: 1, Scissors: 2): ")) p = janken_gen() io.send(str(p)+' ') if len(trace) < 280: # 結果をもとに相手の手を復元 result = io.recvline() if result == WIN: trace.append(2) elif result == DRAW: trace.append(0) break else: trace.append(1) break else: io.recvline() result = io.recvline() if result == ENDGAME: isEnd = True break if isEnd: break print(io.recvall())
❯ uv run exploit.py
...
[+] Receiving all data: Done (95B)
[*] Closed connection to challs.tsukuctf.org port 30057
b'Tsukushi: So, here is the flag. TsukuCTF25{4_xor5h1ft_15_only_45_good_45_1t5_5h1ft_p4r4m3t3r5}
'
(xortsukushift)