みむらです。 SECCON CTF 横浜大会に行ってきました。
http://www.seccon.jp/p/201212yokohama.html
昨年の12月22日に行われ、会場は実にクリスマス+リア充 の空間で満ちあふれており、
私のような者が居ていいのだろうかと思いましたが結果は上記のとおり。
・・にしても、この記事が12月に書き始めて、2月まで下書き状態で保存されていたとか言えない。
チーム名は前回のつくば大会同様 「wasamusume」としました。
前回と違う点としては、
1.チームリーダーがなぜか私になってる。
2.ておくれ要因を排除した。
というところでしょうか。ネタを前回よりも、さらに取り除きました。
今回のチームメンバは、
私、かーみさん、あたがわさん、Decさん、cookies さん、ゆったんさんの計6人。
今回はメンバが大変すばらしい方々が集まったことに加えて、
問題の特性がチームの特性と合っていたようにもおもえます。
また、初回参加時にはチームメンバだった「わさお」が、
知らないうちに他のチームで参戦することになっていて、我々が 「wasamusume」なんて名前でいいのかな
とか思いましたが、やっぱり結果オーライでしょう。ここは。
解いていて、いままでと違うなと思ったのは、
やはりスコアの遷移でしょうか。
執筆時点ではスコアに関する情報が出ていませんので難しいところですが、
過去2回の大会では「最初の出だしはいいが、後で追い抜かれて追い越せない」という感じでした。
今回はバイナリ系問題に時間がかかり(ごめんなさい)前半は Web な皆さんに助けられました。
そのような中で、過去と同じ「最初はいい、途中で抜かれる」という形ができ、
やばい!と思ったとき、バイナリ系の問題が解け、その時同時に呪縛からも抜け出せたような気がします。
CTF 会場としては、すでに述べてはおりますがクリスマスクリスマスしており、
非リア充を寄せ付けない、まさに 「こんなところでCTFといてるばあじゃない」 というような。
BGM にはクリスマスソング、もしくはそれに関連したクリスマス中止ソング。
あとは “うなぎのきもち” になることを強要(?)されたりと、前回2回の大会とは趣が違うBGM で、
私にとってはむしろ作業しやすい感じだったようにもおもいます。
記事が長くなりそうですのでいったんここで切りますね。
まじめな感想はこの辺で (なお、 出題問題については記事の最後に記述してあります。)
大会前日は、大学から秋葉原に直行して、ゆいなさん と おきなわさんと合流。
秋葉原をぐるぐる巡り、GPU が安いぞ!! とか ここで全部買える・・! とかとか。
そんなこんなで、かーみーさんとも合流。
一緒に東京駅のプロジェクションマッピングの上映を見て、
秋葉原に戻ってきてつけ麺を食べました。
「えいっ、あーっ」 って言っているんでしょうか、詳細はわからないんですが、
あの湯切りの際のかけ声がすごかったです。
めりーくりすまーす(しろめ
大会前夜、かーみーさんと横浜の赤レンガ倉庫まで散歩してきました。
思ったこととして素直な感想は、男二人で来る場所じゃない と。
このツリーの両脇に鐘がありまして、これを男性の方と女性の方がペアになり、
時に手をつなぎ、時に肩を寄せ合った状態で鐘を鳴らすわけです。
なんというか、こう、表現しにくい圧力といいますか、
何かを間違えてしまったような、そんな感じを強烈に受けました。
あれはいったい、なんだったんでしょうねぇ。。
前泊の宿はアパホテル。
大会中、水分不足が起きるといけないと思いまして、ホテルのフロントにて水を購入し、
プランに付属のカレーも頂いて。
もちろん、かの有名なあの方の顔がしっかり印刷されておりました。
ホテルでは、寝床に関してインシデントが発生していたような気がしますが
気にしないことにしておきます。
大会後、
wasamusume チームと九工大メンバとで 横浜ということで中華料理を。
そのような中で
第一回大会優勝の Ubel Panzer の話が。 やはり、さすがなーがさん。
いろいろと頼みまして、テーブルをぐるぐる回して食しました。
辛さとか熱さとか、いろいろと相まってとてもおいしかったです。
そして帰り際。
今回の大会のうちのチームの MVP ということで、
高校生の2人に商品として「アパ社長カレー」を贈呈し、それぞれ帰宅の途につきました。
最後に。今回出題された問題について。
パスワードを答えよ。
ファイルを展開しますと、 tlscb-iwa.exe がぽつん。
実行しても、
Invalid Key – (null)
というダイアログが出るだけ。
ってことで中を見ていきます。
毎度毎度お世話になっている IDA せんせーに投げ込み。
main 関数は普通に見つかるのでそこを潜る。
何かおかしい。ということでいろいろと見てみる。
Names Window 内に “TlsCallback” で始まるいくつかの関数が。
ということで、たぶんこいつが先行して動いているのかなと予想。
TlsCallback?
TLS (Thread Local Storage) と呼ばれるスレッドごとに固有な記憶領域を使用した場合に、スレッド起動時と終了時に呼び出されるコールバック関数のこと
TLS Callbacks – ネットエージェント株式会社 オフィシャルブログ
http://www.netagent-blog.jp/archives/51735398.html
というものなのだそうです。(投げやり
その他このエントリを書くに当たり、下記の資料も参考にしました。気になる方はどうぞ。。
Microsoft PE and COFF Specification – MSDN
この仕掛けを言葉で説明するとどういう物かわかりにくいですので、サンプルコードを書いてみました。
#include <cstdio> #include <windows.h> // // 参考資料: // TLS Callbacks - Netagent Official Blog // http://www.netagent-blog.jp/archives/51735398.html // // GB_2.3_IS01 - Android 2.3 (Gingerbread) for SHARP IS01 // http://sourceforge.jp/projects/gb-231r1-is01/scm/git/GB_2.3_IS01/blobs/b3dcab6a8e4a6e9b2dae18f34b25e5713d0675ef/external/chromium/base/thread_local_storage_win.cc // #ifdef _WIN64 #pragma comment(linker, "/INCLUDE:_tls_used") #else #pragma comment(linker, "/INCLUDE:__tls_used") #endif // // 参照: http://msdn.microsoft.com/en-us/library/windows/desktop/ms682583(v=vs.85).aspx // void tls_callback(HINSTANCE hInst, DWORD fdwReason, LPVOID lpvReserved) { switch(fdwReason) { case DLL_PROCESS_ATTACH: puts("ATTACH"); break; case DLL_PROCESS_DETACH: puts("DETACH"); break; } } // // この辺の書き方については下記参照: // http://msdn.microsoft.com/en-us/library/1dc22465.aspx // #pragma const_seg(".CRT$XLB") extern const PIMAGE_TLS_CALLBACK callback = (PIMAGE_TLS_CALLBACK)tls_callback; int main() { puts("HELLO"); return 0; }
だいたいこのような感じです。
コールバック関数の引数が、ちょうど DLLMain の取り方と同じであることがおわかりいただけるかと思います。
おおざっぱには、上記のコードでスレッドの開始・終了時にコールバック関数が呼ばれるという挙動になります。
(当方の環境ではプロセス開始時はキャッチできませんでした。
仕掛けも細かく書くといろいろとおもしろいのですが、おおざっぱに書きますと、
・本来この機能はローダが持っている機能。
( ローダ –> CRT –> ユーザ関数 みたいな流れだと思われる。
・CRT がIMAGE_TLS_DIRECTORY 型の __tls_used という変数を用意する。
( x86 環境以外では _tls_used になる。
・”.CRT$XL?” ( “.CRT$XLB” から “.CRT$XLZ” まで使える。 ) という名前のセクションに、
PIMAGE_TLS_CALLBACK (コールバック関数へのポインタ)を記録する。
・リンカが rdata$T セクションに _tls_used を配置する。
・CRT が用意した仕掛けを使って、ローダから(結果として) TLS Callback として登録してある関数が呼ばれる。
というような流れになります。(きっと
また記述する際は リンカに対して “/INCLUDE:__tls_used” としておかないと、
rdata セクションにおいてくれないのか、うまく動かない模様。
というわけで、プログラムコードを見ていきます。
(ここまで、12月中に書いてあったんですよね・・汗。
TlsCallback_0
まず、 TlsCallback_0 を見ていきます。
大まかに解説すると次のようになります。
Win32API では、 文字列を扱う API 名の最後には “A”, “W” という文字がつき、
それぞれ、 マルチバイト版 (日本語環境なら Shift_JIS) ワイド文字列版 (Unicode) を返すようになっており、
今回の場合は、 GetCommandLine の マルチバイト版を呼び出していることになります。
LPTSTR WINAPI GetCommandLine(void);
呼び出し規則 (Syntax) は上記のようになっていますので、
eax に文字列へのポインタが入っており、その値を [ebp+var_8] にコピー。
( C 言語で言うところの、 *((char**)(ebp + var_8)) = eax; ってなところでしょうか・・?
もう一つ [ebp+var_4] の指し示す領域を、この処理では カウンタ として使用しており、
そこに 0 を書き込み、カウンタを初期化しています。
注:
大括弧 [ ] で括っていない物は そのままの値であり、 ( Cで言うところの i , j )[ ] で括ってあるものは、それが指し示す値 ( C で言うところの *i, *j ) を指します。
以降の文章ではこの解説は省きます。
次の処理で文字列のコピーを行います。
まずはじめの2ブロックは、文字列コピーの際の長さの判定。
指定された長さ ( 0xC ) 分だけをコピーしているかや、
NULL が来るかどうか ( 来たらループを抜ける ) をしています。
残りの2つのブロックで、実際のコピーと、カウンタ変数のカウントアップをしています。
TlsCallback_3:
先述の TlsCallback_0 で文字列のコピーは完了していますので、あとはいじっていきます。
上記の解説中での データと 文字の関係 ( 65 が A という文字を示すというような関係 ) については、
http://e-words.jp/p/r-ascii.html ( ASCII文字コード : IT用語辞典 ) を見ますと、詳しく書かれています。
また、変換する際に使用していた表については、
このようになっており、 仮に 0 という値が edx に入っていた場合、
0x50 が帰る、というような流れとなっています。
TlsCallback_4 :
まずはじめに3つの値をメモリ上に展開します。
次にここの部分で、 xor を繰り返します。
なんか、いつぞやか流行った、俺俺暗号みたいな(ry
TlsCallback_5:
まず関数の頭で TlsCallback_3, TlsCallback_4 をもう一度呼んで、再実行させています。
その後、 今まで処理してきたバイナリデータ ( 0x4098E0 ~ 0x4098EC ) と、
プログラム内部にあらかじめ用意してあったバイト列 ( 0x408100 ~ 0x40810C ) を比較して、
正しいかどうかを判定しています。
全体像としてはこのような形になります。
また、あらかじめ持っているデータは
このようなデータになります。
ではどうやって解くか:
変換テーブルを通して変換して (TlsCallback_3) 0xAB, 0xCD, 0xEF と xor して (TlsCallback_4)
変換テーブルを通して変換して (TlsCallback_3) 0xAB, 0xCD, 0xEF と xor して (TlsCallback_4)
できあがった物が、 最後に示したバイト列と一緒になればいいのです。
・・・頭のいいチームは、逆算するプログラムを書くとおもうのですが、
まぁ・・。量も小さいし、1バイト毎の処理だし、力任せでいいよね。ってことで。
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace tlscb_iwa_solve { class Program { static byte[] conv = {0x50,0x33,0x79,0xD1,0xED,0x5B,0x30,0x11,0x46,0xB0,0x8B,0xCA,0xDF,0x94,0x52,0xE7,0x65,0x90,0xCD,0x40,0xD2,0x67,0x3F,0x37,0xFD,0x78,0xBE,0x0C,0x6F,0x7E,0x14,0xCB,0x45,0x4A,0x48,0x22,0x92,0x61,0xF0,0xEF,0x44,0x56,0x82,0x3B,0x9B,0xB1,0x91,0x3A,0xA5,0x72,0xAB,0x0A,0x5E,0xB2,0x75,0x51,0x8F,0xA6,0x6D,0xEA,0xBD,0xA8,0x42,0xAA,0x69,0x38,0xA9,0xA0,0xD9,0x29,0x2D,0x34,0x6E,0xF2,0x3E,0x76,0x07,0x2E,0xEC,0x0E,0xBF,0x43,0x5D,0x5A,0xE6,0xE3,0x4F,0xFB,0x4D,0x97,0x87,0x86,0x25,0x84,0x71,0x60,0x4C,0xE4,0x1C,0x53,0x9E,0xC6,0x04,0xF1,0x4E,0xF6,0xD4,0x5C,0x1B,0x62,0xC0,0x93,0x2F,0x0F,0xE5,0x7C,0x59,0xC3,0xC5,0x09,0xEE,0x70,0x8A,0x64,0xF3,0xCE,0xE0,0xC4,0x2C,0xFE,0x7D,0xC9,0xC8,0x3D,0x19,0x98,0xD0,0xA3,0x54,0x49,0x2A,0x88,0xFC,0xB4,0x10,0x36,0x2B,0xAC,0xC2,0x21,0x00,0x8D,0x68,0xFF,0x4B,0x7B,0x24,0xFA,0xF9,0x05,0x6C,0x73,0xC1,0x0B,0x35,0x9A,0x18,0xEB,0x81,0x7F,0xCF,0xB5,0x7A,0x74,0xB9,0x63,0xDD,0x32,0x16,0xDA,0xCC,0x27,0xA7,0x23,0x08,0xAE,0xD7,0x26,0x13,0x85,0xF5,0xD8,0x8C,0xA4,0xBC,0x6A,0xE1,0x12,0x17,0x99,0x1F,0x47,0xC7,0x31,0x57,0x89,0xBA,0x58,0x77,0xAD,0xB6,0x9F,0xD5,0x9D,0xB3,0x06,0xB7,0x1D,0xF8,0x15,0x9C,0x96,0xDE,0xA2,0xDB,0x3C,0xAF,0x39,0xF4,0xE8,0xF7,0x83,0xA1,0x8E,0x28,0x66,0x5F,0xE9,0x6B,0x03,0xB8,0x95,0xE2,0xDC,0xD6,0x41,0x1A,0xD3,0xBB,0x55,0x02,0x20,0x01,0x1E,0x0D,0x80,0xD4,0x46,0xF0,0x4F,0xB6,0x03,0x06,0xAA,0xFE,0x31,0xAE,0x8A,0x01,0x00,0x00,0x00}; static byte[] correct = {0xD4,0x46,0xF0,0x4F,0xB6,0x03,0x06,0xAA,0xFE,0x31,0xAE,0x8A}; static byte[] Func3(byte[] b) { return b.Select(x => conv[x]).ToArray(); } static byte[] Func4(byte[] bs) { byte[] xb = { 0xAB, 0xCD, 0xEF }; for (int i = 0; i < bs.Length; i++) { bs[i] = (byte)(bs[i] ^ xb[i%3]); } return bs; } static void Main(string[] args) { byte[] ans = new byte[correct.Length]; byte[] bs = new byte[correct.Length]; for (int i = 0; i < ans.Length; i++) { ans[i] = 0; while(bs[i] != correct[i]) { ans[i]++; bs = Func3(ans); bs = Func4(bs); bs = Func3(bs); bs = Func4(bs); bs = Func3(bs); bs = Func4(bs); } } Console.WriteLine(Encoding.ASCII.GetString(ans)); } } }
ぺたっと。
大会中にがりがり書いたコードをそのまま貼り付けるので、
もっとちゃんと書けばこんな変なコードにはならんのですが。。。うーむ。まぁ、いいよね。
というわけで、答えはこれ。
Find the key !?
これは Windows 8 の XAML の問題。
渡されたファイルの中身はこんな感じ。 某フェローさんならいっぱつですよね!!!()
.appx も中身は zip ファイル。展開します。
Visual Studio を立ち上げて、 FindTheKey という名前のアプリケーションを作ります。
MainPage.xaml と Common フォルダを問題のファイルと差し替え。
MainPage.xaml を開いてみると、 PathStyle を見に行っている部分が。
StandardStyles.xaml を見に行くとこれまた怪しい表記が。
というわけで、データを組み合わせて、Path 要素にカーソルを合わせると、
こんな感じでこんにちは。
というわけで、キーは “hello xaml!”
・・たぶん、問題作成者は Blend あたりで文字を入力して作った後、
パスに変換したんでしょうね。。きっと。。きっと。。。
動かした気になろう
中身は PowerPC の ELF ファイルでした。
大会中、私は解けなかったので、 ならばということで、 「動かして」みました。
取り出しましたるは、この PowerBook G4 さん。 PowerPC プロセッサです。
というわけで、インストールメディアの作成のために jigdo さんをインストールして。。
jigdo ファイルをダウンロード。
ダウンロードしていく。
でけた!!!!
インストールなーう。
でけた!!!!
というわけで、問題のファイルを転送。
んでもって、実行。
うごいたった!!
ってことで、たぶんキーは “Hello Kyutech”
(そこ、ておくれとかいわない
というわけで、横浜大会楽しかったです。
P.S.
この記事書いている今時点で、全国大会まで1週間切っているとか信じられない()