きなこもち。

(´・ω・`)

InterKosenCTF 2020解けたやつだけwriteup

InterKosenCTF2020https://score.kosenctf.com/index.html#/ にjohn_the_ripperで1人で参加し、webとpwnのwarmupを解いて64位でした(´;ω;`)

公式リポジトリはこちら↓

github.com

warmup以外は解けませんでしたが、ブログのネタとしてwriteup投下します😂

matsushima 2[web, warmup]

f:id:kinako_mochimochi:20200912212619p:plain
matsushima 2

問題ページにアクセスするとブラックジャックが楽しめます。

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させることができるので、ブラックジャックに必ず勝つことができます。

f:id:kinako_mochimochi:20200912211849p:plain
自分の手は21に、相手は絶対Burstさせる図

とまあ、相手をBurstさせれば自分の手札に関係なく勝ちなのでSTANDを連打させて相手が自動的に負けるようにすればOKです。 これを14回繰り返せばflagのボタンが押せました。

f:id:kinako_mochimochi:20200912212403p:plain
力技で取ったflag

KosenCTF{r3m3mb3r_m475u5him4}

なお、他の方のwriteupを見るとJWTを分析してpythonでsolverを書いている方がほとんどで、私のBurpでリクエストゴリ押し必勝法は見当たりませんでした笑

solver書ける人本当に凄い...

babysort[pwn, warmup]

f:id:kinako_mochimochi:20200912214009p:plain
babysort

問題バイナリを実行すると、入力した数値を昇順か降順でソートしてくれるプログラムが動きます。

ソースコードを読むとシェルを起動してくれるwin関数があるため、win関数のアドレスに飛ばすことがゴールになりそうです。

考えたこと

  • BOF?
    • 配られたソースを見てもBOFできそうな箇所はない
  • FSA?
    • %pを入力しても、数値以外の文字を検知したらexitしてしまうため不可

解法

問題のソースファイルで次のように構造体が定義されています。

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関数が起動してしまう、ということみたい。

f:id:kinako_mochimochi:20200912215619p:plain
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を解いたので、総合的には落ちていますが...)

今年も面白い問題をありがとうございました!