DEFCON CTFの過去問を解いていきます。
binariesが欲しい方は前回の記事を参照してください。
最近サボリ気味だったので、少し気を引き締めるためにShellcode500を解きました。
と言っても、あまり良い問題では無いので、正直Shellcode400の方が面白くて難しかったのですが。
とりあえず、ひとつ言えることとしては、他のShellcodeの問題に比べて使用しているアルゴリズムが複雑で、C++のクラスを多用してることもあり、かなりサイズがでかいバイナリになっています。
したがって、本来ならちゃんとすべての処理を読むべきではあるんですが、C++のクラスやメソッドの名前から大体どのような処理であるかを予想して、読む手間を省きたいと思います。
今回はかなり雑にCのコードに直しています。
しかし、それでもbmpの画像データを直接recvし、8pix × 8pixに区切ってそれぞれに離散コサイン変換を掛けて、得られたデータをリードソロモン符号による符号誤り訂正を行っているのだろうということはメソッドの名前から明らかになります(初め、ECCが楕円曲線暗号だと思っていて意味不明でした。楕円曲線暗号は鍵の交換の仕方だけが要点なので、全く今回のデータには関係ないですね。頭がついていない。)。
さて、離散コサイン変換もリードソロモン符号も中途半端に理解する程度が私には限界なので、なんとか実装しなくても解けるような方法を考えましょう。
まず、DCTCoefficient::DecodeDCT(this, double *)によって返ってくる値が0か1であることに気づきます。
何故なら、AddDataBitの引数になっており、AddDataBit内では完全に1か0を期待して行われている計算が存在しているからです。
テスト用にlenaへ送信した真っ白い画像や、真っ黒い画像では、全て戻り値は0であり、8×8マス全ての色を乱数で決定した画像では、戻り値が1でした。
おそらく、これはエントロピーを計算して、ある閾値以上であれば1を返すのでしょうが、全く処理を読んでないので確信はありません。
とは言え、処理の外線をなぞれば、DCTCoefficient::DecodeDCTの戻り値が引数のdouble*の値のみに依存するのかどうかぐらいは分かるので、double*のみにしか依存しないのであれば、0を返すような画像と1を返すような画像をそれぞれ1つずつ用意してやれば、自由に値を操作出来るようになります。
次にリードソロモン符号の方ですが、Wikipediaの説明のみから推測するに、16byteの実データに対して、16byteの冗長データを付け加えることで、エラーの訂正が出来るようです。
この時、シンドロームの値が全て0であればエラーはないと判断されるようなので、
今回の場合、送りたいShellcodeをあまり訂正してほしくはないので計算されたシンドロームの値が全て0になるようにしたいわけです。
RS32_16::rs_syndrome内では、0ではなく、0xffがエラーがないと判断される値のようです。
イマイチどうなっているのか分かりませんが、行ってる処理としては同じようなので、深く考えないことにします。
さて、リードソロモン符号の実装は、少なくとも競技時間中に出来るようなものではないと思われるので、ライブラリなどでいいのが落ちてないか探しましょう。
Pythonのreedsoloというモジュールがかなり使えそうだったので、そちらを使うことにしました。
何故か冗長データが16byteだとdecodeが失敗するので、冗長データを17byteにして、Shellcodeで冗長データの1byte分をjmpすることでどうにかしました。
The key is: We're rather impressed at your performints
所要時間4時間28分。
やっぱりゲームしながらだと時間かかりますね。