参加レポートはこちら。
- [web/funny factorials]
- [crypto/Compact XORs]
- [misc/Censorship]
- [misc/Censorship Lite]
- [web/waiting-an-eternity]
[web/funny factorials]
def filter_path(path): # print(path) path = path.replace("../", "") try: return filter_path(path) except RecursionError: # remove root / from path if it exists if path[0] == "/": path = path[1:] print(path) return path @app.route('/') def index(): safe_theme = filter_path(request.args.get("theme", "themes/theme1.css")) f = open(safe_theme, "r") theme = f.read() f.close() return render_template('index.html', css=theme)
別で与えられたDockerfileで、サーバのルートディレクトリにflag.txtがあることがわかっているので、リクエストパラメータのtheme
を書き換えてやれば良さそう。
filter_path()関数はひたすら自分を呼び出し、最後にRecursionErrorの処理で先頭のスラッシュを削除するので、スラッシュを2個つけて?theme=//flag.txtというリクエストを送ると、無事レスポンスのHTMLにフラグが埋め込まれていた。
[crypto/Compact XORs]
与えられるのはflegというファイルのみ。中身は以下のような感じ。
610c6115651072014317463d....
本CTFのフラグ形式はamateursCTF{...}という形。先頭が「610c6115...」となっており、0x61=97で小文字のaなので、1文字ずつ正解が混じっていそうと判断。
Pythonのインタプリタを起動して、先頭の0x61と0x0cのXORを取ると以下のようになった。
>>> 0x61 ^ 0x0c 109
109は小文字のmなので、ama...と解読できそう。スクリプトを書くのが面倒だったので、ChatGPTに解かせることに。
自分: Pythonで610c61156510のような16進数の文字列が与えられたとします。 この文字列を、61, 0c, 61, 15, 65, 10のように 2つずつの数字に分解し、 先頭から奇数番目の数字はそのままASCII文字列に変換、 偶数番目の数字は直前の数字と排他的論理和をとって ASCII文字列にするようなコードを出力してください ChatGPT: このようなタスクはPythonの組み込み関数と、 特に`hex`と`ord`を使用することで解決できます。 以下のコードは、指定された仕様に従ってこのタスクを実行します。 ```python hex_str = "610c61156510" # 2文字ごとに分解 chunks = [hex_str[i:i+2] for i in range(0, len(hex_str), 2)] output = "" for i in range(len(chunks)): # 奇数番目の数字はそのままASCII文字列に変換 if i % 2 == 0: output += chr(int(chunks[i], 16)) # 偶数番目の数字は直前の数字と排他的論理和をとってASCII文字列にする else: xor_res = int(chunks[i-1], 16) ^ int(chunks[i], 16) output += chr(xor_res) print(output) ```
あとは、hex_str変数を実際の文字列に置き換えてコードを実行するとフラグが出力された。ChatGPT賢いなあ。
[misc/Censorship]
こんな感じでソースが与えられるので、サーバに接続してコードを入力してフラグを取得しに行く。
#!/usr/local/bin/python from flag import flag for _ in [flag]: while True: try: code = ascii(input("Give code: ")) if "flag" in code or "e" in code or "t" in code or "\\" in code: raise ValueError("invalid input") exec(eval(code)) except Exception as err: print(err)
ポイントになりそうなif文では、入力値にflagまたはeまたはtまたはバックスラッシュが入っているかをチェックしている。試しにサーバに接続してaと入力しエンターキーを押すと、以下のようになった。
Give code: a name 'a' is not defined
exec(eval(code))
で、入力された文字列をそのままPythonのコードとして実行してくれているらしい。flagは_という変数に格納されているので、ここにアクセスして頑張れば良さそう。直接print(_)
というコードを実行しようとしたが、これはtが含まれているためinvalid inputの判定に。
なんとかして例外を発生させて、print(err)でそれを検知できればフラグを確認できそう。しばらく考えたのち、ゼロ除算のエラーを利用することを思いついた。フラグ文字列を1文字ずつ順番に取得し、想定されるアスキーコード値で引き算して、その結果が0になっているか確認すれば良い。フラグ形式はamateursCTF{...}なので、1文字目はaだろうとあたりをつけて、以下のように試してみると、ゼロ除算のエラーが表示され、1文字目がaであることが確認できた。
Give code: 1/(ord(_[0])-ord('a')) division by zero
あとはこれをひたすら繰り返して、2番目の文字、3番目の文字...と進めていくとフラグが取得できる。小文字のeなどは、if文のチェックで弾かれてしまうので、直接アスキーコード値を入力して回避した。
きっともっと賢いやり方があるんだろうけど、頑張って1文字ずつ推測していく方法しか思いつかなかった...orz
[misc/Censorship Lite]
前作Censorshipの改良版。今回はif文のチェックが以下のように変更になっている。
if any([i in code for i in "\lite0123456789"]): raise ValueError("invalid input")
入力したコードを実行させて、例外を発生させて内容を確認できるところは前作と同じ。数値が直接使えなくなったので、フラグの何文字目かを指定するord(_[0])
の部分の改善が必要。アスキーコード値を利用して、ord(_[ord('a')-ord('a')])
のようにすると、目的の文字にアクセスできる。あとは力技で。
ちなみに、これの改良版のCensorship Lite++という問題もあった。コードは以下のように変更になっている。文字列にアクセスするための[
や]
といった記号は使えるものの、ord()
などの関数呼び出しが封じられたためフラグに辿り着けず。無念...。
#!/usr/local/bin/python from flag import flag for _ in [flag]: while True: try: code = ascii(input("Give code: ")) if any([i in code for i in "lite0123456789 :< :( ): :{ }: :*\ ,-."]): print("invalid input") continue exec(eval(code)) except Exception as err: print("zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz", err)
他の人のwriteupを読むと勉強になる。
[web/waiting-an-eternity]
ソースコードは無し。サイトを開くと、「just wait an eternity」とテキストが表示される。
開発者ツールを開いて、HTTPレスポンスを確認すると、以下のヘッダが設定されていた。
refresh: 1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000; url=/secret-site?secretcode=5770011ff65738feaf0c1d009caffb035651bb8a7e16799a433a301c0756003a
調べてみると、メタタグと同じで、指定時間待って指定されたURLにリダイレクトしてくれるヘッダっぽい。
そんなに待てないので、urlを直接入力して開くと「welcome. please wait another eternity.」というテキストが表示された。適当にリロードしてみると、表示が変わって、「you have not waited an eternity. you have only waited 15.026334047317505 seconds」と表示される。HTTPリクエストヘッダをみると「cookie: time=1690307411.834997」という意味ありげなクッキーが設定されていた。こいつをいじればフラグが取得できそう。
cookie: time=9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
を設定してリクエストを送ったところ、「you have not waited an eternity. you have only waited -inf seconds」と表示された。マイナス無限大になってしまっているので、クッキーの値を「-9999...」に変更してリクエストを送ると、無事フラグが出力された。