TSGCTF 3 で作問・作問チェックをした問題について

作問

今年はPwnを6問作った。ちなみに接頭語がcだらけなのは気づきましたか?(特に意味はないです)

Beginner's Pwn (beginner) 283 solves

Writeup: https://hackmd.io/@moratorium08/SJiWXXvNF

もともとはCoffeeをBeginner向けに作りかけていたが、想定よりテクい感じになってしまったので、もっとテクい感じではなく、博多市でも解いてくれるような問題でかつpwn感ある問題を目指した(ちなみに忙しくて解いてくれなかった)

本当はBeginner枠はPwnわかる人間でも考えないと分からないし、わからない人間も考えればわかるみたいな問題を出せると理想だが、残念ながらアイデア不足で前者が満たせなかった。

Coffee (easy) 48 solves

Writeup: https://hackmd.io/@moratorium08/ryMcaePVY

ソースコードがとても短いのでもう一回載せちゃお

#include <stdio.h>

int x = 0xc0ffee;
int main(void) {
    char buf[160];
    scanf("%159s", buf);
    if (x == 0xc0ffee) {
        printf(buf);
        x = 0;
    }
    puts("bye");
}

特に話せる話は、無いな

cHeap (easy) 39 solves

Writeup: https://hackmd.io/@moratorium08/BJ3j8-wNK

かなり典型的なheap風水問。zer0ptsの解く速度があまりに速くてさすがに怖かった。

Cling (medium) 5 solves

Writeup: https://hackmd.io/@moratorium08/S1ig4xwVF

Clingで遊んでいたら気づいた事実を元に作った問題。個人的には露骨にclingのevalを呼び出す部分があんまり気に入ってなくて、clingがJITをlazyな感じで、関数を呼び出すときに初めてコンパイルする感じだったらなおよかったなあという感じ。

ところでClingってよく動いてますよね、すごい。

Chat (medium-hard) 1 solve with azaika

Writeup: https://hackmd.io/@moratorium08/Sk7puL84Y

難しめの問題枠。もともとは、std::variantのemplaceを使っていて、クソデカ数を入れると、一個前の選択肢に依らずに例外で落とすことができていたが、azaikaという人間が現れてC++を教えてくれたのでよりバグが込み入った。

そもそものアイデアは、例外吐いて落ちるときstack unwinding起きないんだという驚きから生まれた問題(実際には実装依存だがclang/gccは起きないっぽい)。この挙動あんま知らなかったけど結構怖いと思うんだよね。やはりちゃんと例外はキャッチしないといけないね

Catastrophe (hard) 1 solve

Writeup: https://hackmd.io/@moratorium08/Sk7puL84Y

OCamlのunsound問。ただ、SECCONのときと違いocamloptでネイティブのマシンコードにするのではなくocamlcでバイトコードを吐くようにした。なので最終的にはバイトコードを実行するVMのpwn。

実は2018年頃に一度この形での問題(Obj.magicさえあれば、OCamlインタプリタがPwn可能なのか)というのを作ろうとしてできるだろうけどよくわからないねと思ってやめた経験があるので、実はちょっとエモという話も。

まあもう二度とOCamlを題材にすることはないでしょう。

作問チェック

Rev/Pwn・言語処理系の問題は僕の担当という気持ちで眺めてた。今回は過去2回と比べてもかなりレビューを互いにしたと思う。

lkgit by smallkirby

中間発表に悩まされる中作ったかーびーかーねる問。userfaultfdを"うまく"起こさないといけない問題。 あんまカーネル問ができないので、https://github.com/smallkirby/kernelpwn と https://ptr-yudai.hatenablog.com/ を見ながらスクリプトキディをして解いた。なのであんまり読ませられるコードにはなっていない。

https://gist.github.com/moratorium08/6f62359389d45cab971dbad562380327#file-lkgit-c

smallkirby.hatenablog.com

Beginner's Rev by mikit

最初作問チェックをしたときに比較的想定解通りに解いたので、かえって紛らわしかったかもしれない。gdb謎機能とか使わずに、バイナリパッチして、forkした場合に/dev/nullに吐き出す部分を取り除いた。そうすると、OK/NGの数の変化で前からbrute forceができる。想定はstraceで同様にOK/NGの数を数える方針で、ゴリゴリrevをしなくても綺麗に解けるBeginner's Revにふさわしい問題

https://gist.github.com/moratorium08/6f62359389d45cab971dbad562380327#file-beginners_rev-py

Natural Flag Processing by dai

もともともう少しオートマトンの構造が簡単だったときに一度解いて、その後ネタを知った状態で解き直した。

基本的には、可能なケースをトラックするコードを書けばいい。Colabって便利なんですね

https://gist.github.com/moratorium08/6f62359389d45cab971dbad562380327#file-natural_flag_processing-py

optimized by ishitatsuyuki

ある意味TSGCTF唯一のrev問らしいrev問。やや典型に近かったかもしれない分だけソルバーが出ていた気がする。UPX + 難読化つきのバイナリなので、gdb使ってバイナリを取り出して、IDAで開くときれいに解析してくれた。

あとは、当該処理を元に可能なケースを全探索

https://gist.github.com/moratorium08/6f62359389d45cab971dbad562380327#file-optimized-cpp

Beginner's Web by hakatashi

分からんくて泣いてた。普通にraceだと思っちゃうよね。よくこういう問題が作れるなあ。

Beginner's Crypto by hakatashi

"三つ子素数"についてわかったとき、そっかーという気持ちに

UB sharp by JP3BGY

これは、作問チェック実は結構していたんですが、結局非想定解がいっぱいあった... 申し訳ない。言語問、かなり不可能。出す前にホワイトボックスにするかブラックボックスにするか議論で盛り上がったが、やはり(出すなら)ホワイトボックスしか勝たんなあという気持ちに。

僕が見つけた非想定解は、sanitizerのバグと旧バージョンで可能だった変数の上書き解。実はこの後者に対応するために、当該変数をインスタンス変数にしたのが運のつきだった。直したところに目がいかなくなっていた。

しかし、うまいことGetTypeをしてAssemblyを取り出してReflection頑張る解はレビュー中普通に見つけられておらず、これは普通に駄目だった。

感想

CTF開始前は割と胃が痛かったし、そもそも準備がとてもしんどかった。ただ、よくわからんけど結局開催後は楽しかったという気持ちだけが残るんですよね。

来年、、あると、いいですね?