InterKosenCTF Write-Up

概要

2019年1月18~20日にかけて開催された、InterKosenCTFのWriteUpです。

所属しているチーム「生活習慣崩壊ズ(seikatsukowareru)」は1850点を獲得し、5位となりました。そのうち、自分は5問通して900点を獲得しました。

自分の通せた5問について解法を記したいと思います。

Gimme Chocolate(Web 100pts)

brainfuckで特定の文字列を出力できたら勝ち。ただし、ソースに文字制限(100文字)があるので、どうやっても正当ソース(400文字)を捩じ込めない模様です。

//However, looking into the details, there are some differences. Be careful!

インタプリタのところに上記の記述があったので、最初にそこを見ました。メモリがリングになっているだけで、脆弱性はまったくありませんでした。

投稿されたソースはすべての人に見えるようになっているため、投稿によるものでないと考えます。恐らく外部からソースを注入する、または出力せずに何らかの方法でフラグにアクセスするのだろうと思いました。

ざっと見ていると、

$code file_get_contents($_GET['file']);

とありました。公式レファレンスを見ると、file_get_contentsはURLからも取得できるようです。以下のGistのrawデータを投げ、通しました。

gist.github.com

lights out(Cheat 100pts)

exeで(たぶん)クリア不可能なゲームが落ちてくるので、クリアしてくださいという問題。(ゲーム内容:クリックしたら周り3*3マスの色が反転するので、全て点灯させたら勝ち。)

f:id:keymoon:20190121003630p:plain

ゲーム画面

exeで落ちてくるのは相当珍しいので、恐らくC#的なそれなのかなあと想像。とりあえずILSpyに入れてみると、いい感じで開けました。

ただし、変数名がハングルに置き換わっている、文字列が動的生成になっている等の難読化が施されています。

そこまで酷い難読化ではなさそうなので、適当にフラグを表示する部分を探していました。すると、以下の関数が怪しそうだと感じました。恐らく全てのマスについて点灯していることを確認したらメッセージボックスを出しているのでしょう。

f:id:keymoon:20190121003445p:plain

怪しい関数

メッセージボックスの引数にあるFlagと思われる文字列は謎の関数の返り値となっています。なので、この関数を見てみます…

謎の関数

なんだこれ。stringを返しているのかと思いきやValueTuple('(int,int,int)'の部分)を返している…?(何も分からない。)

しかし、実はこれが関数呼び出しであるということがわかります。

適当に関数名をつけたりして実行できる形にしたものが以下です。

gist.github.com

これを普通に実行し、str2()の戻り値を表示させてあげると、フラグが出てきます。

Secure Session(Web 200pts)

PHPのSessionHandlerを継承した自前セッションハンドラのデモが与えられます。flagがどこにあるかはわかりません。

このハンドラはAESを用いてセッションに入る文字列を暗号化しています。暗号利用モードはCBC、まあまあ弱い奴です。

なので、これの脆弱性をつく問題なのかな、と考えました。しかし、どうやら違うようです(偽装できない上、これでセッションを偽装できたところで何の利益もないので。セッションに保存されている情報はアクセスカウンタのみです。)

しかし、セッションハンドラのセットアップを省略するため、クライアントにシリアライズされたセッションハンドラを渡し、二回目以降はそれを利用してセットアップを行っています。

ダメそうなコード

つまり、クライアント側で$handlerに入るオブジェクトをある程度自由に変更できるというわけです。シリアライズされているhandlerは以下のように作られています。

シリアライズされるデータはSecureSessionに紐付いている全てのオブジェクトなので、ここで宣言されている変数全てが変更可能となります。

handlerのセットアップ部分。

set_cryptoでhandlerが用いる暗号化/復号化関数を設定しています。これはSECRET_KEY,messageを引数として取り、暗号化/復号化した結果を返すものです。

アクセス時に呼ばれる関数なので、この関数を改造して上手いことできれば嬉しそうだとわかります。

先程も言ったとおり、setした関数にはkeyと文が渡されます。つまり、第一引数であるkeyは自由に設定ができるということです。よって、encryptにコマンドを自由に実行できるsystemを指定し、SECRET_KEYをls(ディレクトリにあるファイルを一覧表示するコマンド)としました。

 ローカルでシリアライズしたものを送りつけると、いい感じで出てきてくれました。

f:id:keymoon:20190121011901p:plain

/hOI_the_flag_is_hereにアクセスし、おわりです。

Login(Web 250pts)

adminとしてログインさせる問題です。

ソースを読むと、サニタイジングをせずにSQLに突っ込んでいるところが存在しました。明らかにSQL Injectionが刺さります。

ここで、2つの方針が浮かびます。adminのパスワードを当てる方法、passwordに返した値とパスワード自体が同じ、または同じMD5になるような値を探す方法があります。

大体は後者を取っているようですが、私は前者を取りました。

0からfまでをパスワードとして持つアカウントを取り、そのアカウントのパスワードよりadminのパスワードのn文字目が大きければログイン成功、そうでなければ失敗というSQL文になるようにユーザー名を設定しました。その成否で二分探索を行い、パスワードを特定しました。

ソースは以下です。

gist.github.com

anti cheat(Cheat 250pts)

Canvasで実行されるゲームです。JSがこれでもかと言うほど難読化されています。高スコアを取れば勝ちなようです。

まず、どこから手を付けてよいかわかりませんでした。ということで、ステップバイステップ実行をし、描画が切り替わる際に実行されている関数を発見しました。

そこにブレークポイントを打ち、フレーム毎の実行を可能にしました。現在の目標は高スコアを取ることなので、スコアの改竄を考えます。

スコアが変わった際に変わった変数を探したいと考えたので、スコアが変わる前と変わった後のグローバルの内容をJSONでダンプ、整形してDiffを取りました。(整形後のjsonは30MBくらいあり、整形にも1分ほど掛かりました…。画像は10万行ほどあったnullの配列を削ぎ落とした後のものです。)

f:id:keymoon:20190121013453p:plain

Diffの様子

ぐっと睨むと怪しい変数を2個見つけることができます。現在のスコアが保存されている変数と、それの二乗が保存されている変数です。

その2つをとても大きく変更してあげると、フラグを見つけることができます。

雑感

始まる前にお風呂に入ろうと思ったらむちゃくちゃ寝てしまいました。

 スタートダッシュが切れなかったので、自明問の得点ブーストがかからないのが厳しいなあと思ったんですが、割と良い成績を残せたようで良かったです。

問題も自分に合った難易度のものが多くあり、解いたものは全て楽しめました。

あと土日がCTFだと完全に休めませんね。これを書いている今は日付が変わって月曜日なのですが、正直信じられないです……(無茶苦茶疲れている)。