33C3CTF WriteUp try [150] web

ページを開くとHaskellのコードが書かれたテキストボックスとRunボタンが出てきます。
とりあえずRunボタンを押してみることにします。
f:id:megumish:20170103211050p:plain
Log in firstと言われたので適当な名前でログインしてみることにします。
ログインした後に再度試してみると今度はコードを実行することができました。
ソースコードを見ると

<form action="/run" method="post" accept-charset="utf-8">

    <input id="run_file" name="run_file" value="fib.hs" type="hidden"/>
    <input type="submit" value="Run!"/>
</form>

となっておりfib.hsの部分を変えれば任意のプログラムを実行できそうです。
ただし、fib.hsは/static/fib.hsにあるようなので何らかの方法で/static下にファイルを置く必要がありそうです。
ログイン後はさらに上部のメニューにUplodeとProfileが増えています。
UplodeからHaskellのコードがアップロードできるのであればとてつもなく簡単ですが、残念ながら使えないようです。

次にProfileを確認してみると、プロフィール画像としてGIF画像がアップロードできるようです。
試しにGIF画像をアップロードすると、/static/33c3_(UUID)/pic.gifにGIF画像が配置されるようです。
ここで/static下のファイルはURLを書きかえることで実行できることを思い出します。
試しに先ほどのソースのvalueの値をfib.hsから/33c3_(UUID)/pic.gifに変えてみると
GIF画像がコードとして認識されて実行されることがわかります。もちろん、実際はコードではなくGIF画像のバイナリであるためエラーが出ます。
ここまでのことから、偽装したHaskellのコードをGIF画像としてアップロードし、それを実行させることでフラッグを得ることができるのだろうと推測しました。

そこで問題になるのは、どういう形式のファイルをサーバー側がGIF画像として認識するかということです。
GIF画像の構造*1を調べると、GIF画像バイナリの特定のアドレスには固定値があることが分かるのでそこをしらみつぶしに消してはアップロードを繰り返し試してみました。
その結果、バイナリの最初の"GIF89a"と番地0x15の0x2Cの二つの固定値*2以外を変えてアップロードしてもGIF画像として判定されることがわかりました。

次に問題となるのは、この偽装GIFバイナリにHaskellのコードを埋めることです。
最初に考えたのはGIF89aという文字を関数として認識させることで、Haskellのコードの一部として使うというものでしたが、Haskellでは関数や変数の最初を大文字でおくことはできないのですぐに断念することになりました。
少しHaskellについて調べてみるとどうやら型や型クラスは最初の文字を大文字でおくことができるようでした。
しかし自分はHaskellのことをよく知らないので、slackに投げて聞いてみるとGIF89aを型(もしくは型クラス?)として後置宣言したコード*3をくれたのでそれをバイナリに埋め込みアップロードしたところ見事フラッグを手に入れることができました。

GIF89a i = GIF89a "aaaaaa"
newtype GIF89a = GIF89a String
main = putStrLn "Hello, World!"

slackでもらったHaskellのコード
f:id:megumish:20170103210811p:plain
GIF画像にHaskellのコードを埋め込んだバイナリ

以下フラッグ

33C3_n3xt_T1me_idri5_m4ybe

*1:GIFフォーマットの詳細

*2:これ以外に最後の番地に0x3Bを配置することも条件の一つかと考えていたが、間違えて最後の0x3Bを消してアップロードした際に必要な条件ではないことが分かった。

*3:ちなみにリテレートコメント形式はアップロードした後の拡張子が強制的にgifになってしまうため使うことはできませんでした。