バランスを取りたい

よくCTFの記事を書きます

SECCON 2017 国際決勝 Writeup

遅くなってほとんど忘れてるけど毎年書いてるし今回もやります。

12月に開催されたオンライン予選で11位だったので国際決勝にチームdodododoで出場してきました。

結果は3位とまずまずでしたが、国内決勝と違ってスポンサー賞がなかったので残念でした。 点数的には4位と12ポイント差しかなかったので何か1つでもミスっていたら逆転されてたでしょう。

2017.seccon.jp

自分は主にサーバ1とサーバ5のhangulを解いたのと、問題以外ではNIRVANAを見まくって他のチームの得点状況の把握をしました。

SECCONでは問題に集中してようがなんだろうがNIRVANAをちゃんと見るというのが超重要です。 これをしないとそもそもDefense pointが加算されたのかどうかすらわかりません。

また、Defense pointの獲得状況はログとしてどこかで見られるわけではないので、「問題hogeで時々Defenseに失敗している」といった状況では対処が遅れる可能性があります。

King of the Hillでは状況が刻一刻と変化していくため、得点状況が把握できたのは戦略を立てる上でかなり有効でした。

サーバ1: brainhack

Webページにアクセスし、Brainf*ckで "Hello, World!" を出力しろという問題。バイナリが配布されていてなんかヤバそうな空気。

Brainf*ckの実行結果は確認することができ、これを使っていくつかのAttack flagを入手することになります。

フラグ1

"Hello, World!" と出力するだけ。

Flag: SECCON{Congraturations! Also find other attack flags.}

フラグ2

配布されたバイナリをstringsすると出るらしいです。

バイナリでマップされる領域外?か何かにフラグが置いてあったらしく、ディスアセンブラで読んでたので一切気づいてませんでした。 チームメンバーが拾ってくれていて非常に助かりました。

Flag: SECCON{This must be  too easy for you.}

It was too hard.

フラグ3

配布されたバイナリをよく読むと通常の8つの命令の他に、
', ", %, {, }, :, ;
とかの謎の命令が登場します。

そのうち'の実装を読むと、attack3.txtからポインタ位置に応じて1文字読み出せる実装になっています。 これを使って以下のようにするとattack3.txtの内容を読み出せます。

'.>'.>'.>'.>'.>'.>'.>'.>'.>'.>'.>'.>'.>'.>'.>'.>'.>'.>'.>'.>'.>'.>...
Flag: SECCON{Don't forget to update your score with updated your team keyword.}

フラグ4

フラグ3同様にサーバ上のファイルが読み込まれているタイプの問題です。 読み込んでいるファイルはYXR0YWNrNC50eHQ=という名前だったんですが、これはattack4.txtのbase64でした。こういう謎の小細工は正直要らなかった。

attack4.txtはBrainf*ckでアクセスできる範囲外のメモリに読み込まれているため、普通ではない方法で読み出す必要があります。

ここで謎の命令の実装をよく読んでいくと、{, } を使うと境界チェックをせずにポインターの位置を動かすことができるようになっていました。

というわけで、

{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{...(snip)...{{{{{{{.}.}.}.}.}.}.}.}

みたいな感じでattack4.txtの内容を読み出せます。

ファイル内にフラグがあったので終了。

Flag: SECCON{If you find all 4 flags in this tower, please challenge the other towers.}

Defense

ページ内に文字数と提出者の名前が書かれたランキングが置いてあっていかにも怪しい感じ。 ランキングには短い文字数順で並んでいたので、短いコードを提出したチームにDefense pointが与えられるということらしいです。

Brainf*ck単体での最短コードはググると78bytesのものが見つかるものの、最初から存在するSECCONユーザ(78bytes)を超えることができず、Defense pointを獲得できません。

codegolf.stackexchange.com

ここで、配布されているバイナリの機能を使って78bytesより短くHello Worldを表現する必要がありそうだということに気づく。 ……が時すでに遅しという状態で、他のチームが12bytes解を提出していてかなり絶望的です。 とはいえDefense pointで差を開けられないようにする必要があったので気合でいろいろ探していると、なんと9bytesで通る解を発見します。

驚異の9bytes解はこれ。もはやHello, World!より短い。

{[{]{[.{]

Attackのフラグ4で登場したファイル内に

[NUL]!dlroW ,olleH[NUL].....(snip)

みたいな内容が偶然にも埋まっていたため、この部分のHello, World!が出力されます。

「ここにHello, World!を置くからには簡単に出力できるんだろう」という予想を持ってコードを書いてはいましたが、 実際に[NUL]を確認していたわけではないので試しに入れたやつが通ってしまったときは悲鳴を上げました。

コードの説明をすると、{[{] の部分でHの右隣の[NUL]に到達して{で1文字左に移動したあと、[.{]で次の[NUL]まで文字を出力しています。

この9bytes解でdodododoはしばらく単独Defenseを続けていましたが、すこし後にbinjaも9bytes解を発見してポイントが山分けになります。

さらに2日目になると、いくつかのチームがBrainf*ckの実行バイナリ自体をPwnしてランキングサーバに対して直接攻撃し始めたためランキングが荒れました。

ちなみにランキングが荒れすぎたせいでサーバがダウンするという事件が発生したのでPwnできてなかったdodododo的には非常においしかったです。

サーバ5

サーバ1が思ってたより長くなったのでまた今度。説明するのしんどいし書かないかも。