InterKosenCTF 2020解けたやつだけwriteup
InterKosenCTF2020https://score.kosenctf.com/index.html#/ にjohn_the_ripperで1人で参加し、webとpwnのwarmupを解いて64位でした(´;ω;`)
公式リポジトリはこちら↓
warmup以外は解けませんでしたが、ブログのネタとしてwriteup投下します😂
matsushima 2[web, warmup]
問題ページにアクセスするとブラックジャックが楽しめます。
999999chipsを獲得すればflagを獲得できます。
1プレイごとに勝てばchipが2倍になり、負けたらその時点でゲームオーバーで、最初のchipは100なので、14回連続でゲームに勝てばflag獲得です。
ちなみに愚直にプレイしてみましたが、相手が21でブラックジャックを揃えると、こちらが21であっても負けるので道中で相手に21を引かせないことが絶対条件です。
しかし結構な確率で相手が21を揃えてくるので通常プレイで勝つのは諦めました笑
考えたこと
- JWTでセッション管理している
- warmupなのでJWT none attackとかだと思った→違いました
- Flask製なのでSSTI?
- 唯一注入できそうな箇所に色々送り込んでみるもコレジャナイ感→SSTIではない
- 通常プレイで勝つ
- 上にも書いた通り無理でした笑
解法
と、色々試してみましたがどれも不発に終わり、キレてHITのボタンを連打したところ、カードが一瞬だけスロットのように数回変わった後、こちらの最終的な手が確定しました。
また、STANDも同様に連打したところ同じ現象が起きました。 ここで初めてHITとSTANDの実装が甘いのではないかと思い、Burp suiteを使ってHITとSTANDのHTTPリクエストをキャプチャしました。
Proxyを作動させてHITやSTANDを連打すると、押した分だけHTTPリクエストがキューに追加されます。
すなわち、連打した分だけ異なったJWTが入ったHTTPリクエストがproxyに溜まるので、自分の好きな手が揃ったリクエストだけを通し(burpではFORWARD)し、残りのリクエストを捨て(DROP)れば好きな手(21など)を揃えることができちゃいます。
また、同じことをSTANDで行えば確実に相手をBurstさせることができるので、ブラックジャックに必ず勝つことができます。
とまあ、相手をBurstさせれば自分の手札に関係なく勝ちなのでSTANDを連打させて相手が自動的に負けるようにすればOKです。 これを14回繰り返せばflagのボタンが押せました。
KosenCTF{r3m3mb3r_m475u5him4}
なお、他の方のwriteupを見るとJWTを分析してpythonでsolverを書いている方がほとんどで、私のBurpでリクエストゴリ押し必勝法は見当たりませんでした笑
solver書ける人本当に凄い...
babysort[pwn, warmup]
問題バイナリを実行すると、入力した数値を昇順か降順でソートしてくれるプログラムが動きます。
ソースコードを読むとシェルを起動してくれるwin関数があるため、win関数のアドレスに飛ばすことがゴールになりそうです。
考えたこと
解法
問題のソースファイルで次のように構造体が定義されています。
typedef struct { long elm[5]; SORTFUNC cmp[2]; } SortExperiment;
そして実際にソートする部分はこちら
qsort(se.elm, 5, sizeof(long), se.cmp[i]);
elmはソートするために入力する数値のための配列、cmpは昇順か降順かを格納するSORTFUNC型の配列です。
あまり詳しくないのですが、構造体はメモリに確保される際、メンバの変数が隣り合って確保されると仮定したら、cmp[i]のiに負の値を入れたとき、SORTFUNCではなくelmの値を見に行き、そのアドレスがwin関数のものならそこに飛んじゃったりしないかな〜なんて思いながら実行したら実際にwin関数に飛んでflagが取れました笑
詳しくは作問陣営の解説を見た方がよさそう→InterKosenCTF 2020を開催しました - CTFするぞ
- man 3 qsortで確認すると、確かに最後の引数は関数ポインタを受け取るのでwin関数のアドレスを渡せばwin関数が起動してしまう、ということみたい。
KosenCTF{f4k3_p01nt3r_l34ds_u_2_win}
ちなみに0x400787
をelmに入れようとするとxが数値でないのでexitするので、10進数4196231
を入れる必要があります。
まぐれとはいえ、Pwnが解けたのでよし。
まとめ
社会人になってなかなかCTFに参戦できず、約2~3ヶ月ぶりのCTFでしたが、warmupの問題が解けて安心しました💦
warmupとはいえsolve数も40~50だったのでそれなりに満足しています。
(とはいえ、去年はwarmupの上のeasyを解いたので、総合的には落ちていますが...)
今年も面白い問題をありがとうございました!