2012年11月12日月曜日

SECCON CTF in NAIST Misc パジャマでおじゃま

問題はパケットチャプチャ形式のファイルで、その中身は以下のようになっています。
ご覧の通り192.168.96.128と192.168.96.129のICMPを利用したpingのパケットが並んでます。
また、データ部も、OSに付属するpingコマンドが付加するシーケンシャルに大きさが増加するバイトが代入されています。
ただ、最初はそうですが、途中からすこし異なってくるようです。
例えばid=0x21dc、seq=26のicmp requestにたいして、同じid、seqとなるicmp replyは3つ返ってくることがわかります。またパケット長も不揃いになってきました。72 ~ 500まで変動しています。さらにパケット長が500のパケットのデータ部は明らかに通常のpingと異なるメッセージになっています。
図の165番のパケットの場合、データ部にLast login Suc Oct 7 15: とか明らかにログイン後に出てくるメッセージなのでicmp replyのデータ部が怪しいと分かります。

各icmp replyのデータ部を眺めているとこんなことに気づきました。

  • パケットの0x44バイト目はシーケンシャルに増加している
  • パケットの0x45バイト目は必ず0x21である
だからid=0x21dcのicmp replyの0x44バイト目を基準にパケットをソートし直して、重複を取り除きました。
すると以下のようになりました。

ログインコマンドが9600,9600なのかは不明ですが、ログイン先のOSがDebian GNU/Linux 5.0でホスト名がsrcみたいです。
ユーザ名はctf、パスワードもctfでログインに成功しました。
若干見づらいですがその後cat key.txtを実行しており、そこでkey.txtの中身が現れ、これが答えとなります。
その後exitしてlogoutに成功しました。
こうした一連のやり取りがpingを通じて行なわれていたことがわかりました。

しかし驚くのはここから、こうしてパケットを縦に並べてみると0x45バイト目の0x21、つまり'!'が縦に並んでいることがわかります。
'!'を境にして左側は通常のpingに付加されるデータ、右側がログイン時の通信というふうにして2つの意味を成していたことがわかりました。

マニアックな通信の仕方ですね。まるでマルウェアみたい笑。




SECCON CTF in NAIST Binary hello-x86-world

またしてもWindowsの実行ファイルからの問題です。実行するとヘルプメッセージを出してくれるとか親切です。
では何を引数に入れたらいいのかを探していきましょう。
下図がmain関数の逆アセンブリです。
call _printfまで実行されたら負けなのでjz short lot_40122Dを追います。loc_40122Dはジャンプ元の直下にあるところで、コマンドライン引数をchar *型として関数sub_4010E0に渡します。では関数sub_4010E0を見てみましょう。
まずeax = _strlen(char *)を求めていることが分かります。
そしてcmp eax, 0xEがあるのでこのコマンドライン引数が14文字であるかどうかを確かめています。14文字の場合、その次のjmpは成立しないので上から順に実行されます。
mov ecx [ebp+ arg_0]
movsx edx, byte ptr [ecx+8]
この2命令でecxにコマンドライン引数であるargv配列の先頭を指すアドレスが入り、edxにargv[8]が入ります。
その次にedx = argv[8] == 0x61 ('a') ? かどうかを比較しているのでargv[8] = 'a'とわかります。
では比較がなりたつ場合、jzは成立せず、また順に実行されます。
上記の手順と同様に
mov eax, [ebp+arg_0]
movsx ecx, byte ptr [eax+7]
mov edx, [ebp+arg_0]
movsx eax, byte ptr [edx+8]
より、ecx, eax = argv[7], argv[8]となりました。
つぎに
sub ecx, eax
cmp ecx, 13h
によりargv[7] = argv[8] + 0x13ということがわかります。
つまりargv[7] = 'a' + 0x13 = 't'となり、argv = "*******ta*****"となります。
以降同様のようなアルゴリズムでargv[0x0] ~ argv[0x10]まで確定しますが、そこで関数が終了しています。(下図)

なのでこれで求まったargv[0:0x10] + "???"の14文字をコマンドライン引数にして代入して実行してあげると同じディレクトリに123というファイルが作成されます。
このファイルの中身からは未だにとくにヒントらしきものが見つからないので
求まったargv[0:0x10]あるいはargv[0:0x10] + "123"がkeyなのではないかと推測しています。

SECCON CTF in NAIST Binary A_Cinderella_protection_for_beginners

問題をダウンロードして解凍するとCinderella.exeというWindows実行ファイルを発見できます。そしてそれを実行すると


 というふうになるので特定の日時にしかイベントが発生しないことが分かります。
では、特定のイベントを発生させるためにプログラムの逆アセンブリを見てみましょう。

main関数は上図のようになります。上図の逆アセンブリからさっきのメッセージボックスはcall MessageBoxAから発生していることが分かります。つまりその前のjnz short loc_401040でイベントが発生するかどうかの分かれ目のようです。
ではloc_401040はどういうコードかというと
こんな感じです。赤色で示されたmov esi, [ebp+arg_0]によってloc_401040にジャンプする前にpushされたeaxがesiに代入されています。さらに前のlea eax [ebp+SystemTime]からSystemTime構造体を指すアドレスがesiとなります。
その後にcmp word ptr [esi], 7DChとかあるので、0x7DC = 2012から考えると
年、月、日、時、分、秒という順に比較されていることが分かります。
つまりこれらの比較条件に合うように[esi+?]の値を操作してあげればイベントが発生します。
なのでこの時刻がキーではないかと思います。
ちなみにイベントはただ時刻をお知らせするメッセージボックスが表れるだけでした。


2012年5月13日日曜日

SECCON CTF 2012/2/18-19 Binary 300 Devide Section

問題:「キーを見つけろ」
まず最初に、説明が長い割に適当なので根気強く見てください(笑)。

問題のzipファイルをダウンロードし、中身を開くと
5つのバイナリファイルが入ってる。
#ファイル名は何を意味するんだろうか。謎だ。
これをバイナリエディタで見ると以下のようになる。
図1

左の2つのファイル(MILEENA.binとPITS.bin)の先頭を見るとMZとPEのマジックがあるので、明らかに Windows PE(Protable Executable)、つまり実行ファイルに関係することがわかる。また右下のREPTILE.binにはKERNEL32.dllの文字列が現れているのでインポートするWindows APIを表していることが伺える。
ここで、問題の名前、Divide Sectionから予想するにPEファイルがSectionごとに分割されていて、おそらくそれを結合すればキーが得られるという仕組みだと疑う。
またPEは
  • MZヘッダ
  • PEヘッダ
  • セクションテーブル
  • セクション1
  • セクション2
  • ...
のフォーマットとなっていることからもこのSecitonを意味している可能性が高い。
早速、この順番に結合してみたが(やはり)実行はできない。
#ここからバイナリエディタとの格闘が始まるのであった。

この分割されたデータを復元するにはSectionのデータをファイル内の正しいアドレスに書き込む必要がある。PEファイルを実行するときはそういったアドレスを参照しながら自分の中にどういうデータがあり、どこに格納されているかを知るのである。
#ちなみにぼくはアナライジング・マルウェアという本を参考にしました。著者の方々、ありがとうございます。

上の図はPEヘッダ(PITS.bin)で実行のヒントとなる部分をマークしたものである。
???の部分はおそらくIAT(Import Address Table)だと思われるが、確信がない。
こう見てみると、大事なところがDE AD BE EFでかき消されていることがわかる。

まず、PEヘッダ内のSection情報をもとに各セクションを復元する。
図中に示されていないNumberOfSectionという場所が0x03であることから残りの3つのファイルがそれに当たると察し、さらに残りのファイルのバイナリから予想して、それらは text(コード領域)、rdata(インポートする関数を並べる領域?)、data(データ領域)だとする。
ここでSection name領域の後のメモリにロードされたときのサイズとアドレス(緑線)と、ディスク上でのサイズとアドレス(青線)が大事となる。
例えば、先頭のtextセクション(file offset: 0xF8)の
  • VirtualAddress : 0x1000, VirtualSize : 0x93A
  • PointerToRawData : 0x400, SizeOfRawData : 0xA00
となります。
※Intelマシンはリトルエンディアンであることに注意してくださいね。
※(00 04 00 00 -> 0x400)

つまり、textセクションとなるデータ(図1の右真ん中TOASTY!.bin)はファイルオフセットが0x400となる場所に来ます。なので0x00でパッディングしなければなりません。
例えばMZヘッダとPEヘッダの間はGapが存在するため0x00を16個パッディングします。
同じようにパッディングを全セクションにすることで最終的には、
PEファイル =
                   MZヘッダ + (0x00 * 16) +
                   PEヘッダ + 0x00 * (16 * 26 + 18) +
                   text + 0x00 * (16 * 12 + 6) +
                   rdata + 0x00 * (16 * 6) +
                   data
となります。
これでヘッダとセクションは正しく配置されました。
 次にImportのアドレスを直します。
Importのアドレスはrdataの間から考えると図1の右下REPTILE.binから指し示す場所を見つけなければなりません。
本当はきちんとrdataのオフセットの差(VirtualAddress - PointerToRawData = 0x1200)を利用しDLLの出現位置から逆算してアドレスを決めるところですが、他の問題(Binary 100、200)のPEファイルから似たパターンを見つけることでimportアドレスを発見しました。
図の2番目のマーカがそのアドレスを示しています。
0x22B4(1番目のマーカ) - 0x1200(オフセット差) = 0x10B4(2番目のマーカ)になりますし、合ってる気がします。
とimportアドレスも分かったのであとはEntryPointです。
これも面倒くさかったのでBinary 100, 200のプログラムからEntryPointのパターン(C6 75 08 33)を探してVirtualAddressに変換して埋めました。

#プログラムが違っても、最初の初期化のルーチンは一緒なんだろうと思います。
つまりオフセット0x5DD + 0x1200 = 0x13DDがEntryPointのはずです。
こうして変換していくと、PEヘッダが下図の左から右に変わります。

試しに、修正したファイルを実行してみるとエラーなく実行できる。
#もうここまでで十分なのだが、問題はまだ続く。
Password:
と聞かれてしまったからである。つなり正しいパスワードが求めていたキーである。
となればデバッガの出番。
#DLLを調べたときにisDebuggerPresentの文字列が出て、まさかアンチデバッガじゃないだろうなと萎えてたけど、そうじゃないみたいで良かった。

逆アセンブルすると、test eax, eaxの結果でCorrectかTry again.に分岐するのでその前の
call loc_401090が怪しいとにらみ、break-pointを埋め込む。
loc_401090でも謎の処理があり、loop loc_4010B9を抜けると怪しいcmp命令がずらずら並ぶ。またesiをベースとしたアドレスが0x4増加するため、文字列を4Byteずつで比較しているのがわかる。それぞれのcmp命令の2番目のオペランドを結合すると答えの文字列が表れる。
以上です。
説明も長く、かけた時間も長く、回答の文字列も長かった。

2012年5月8日火曜日

SECCON CTF 2012/2/18-19 Forensic 300?

問題:「キーを見つけろ」

そして最初に渡されるファイルの先頭はこんな感じです。
しかし、こんなフォーマットのファイルは見たことがありません。普通、ファイルの先頭のヘッダを見てればなんとなく特徴を掴めるのですが、これははっきり言って謎でした。
そのためGoogle先生に頼るしかありませんでした。
そこで、繰り返し出現する「BUDlH0P6b」という文字列を検索すると、わずかなヒットともに、ishフォーマットの可能性を発見しました。
Wikipediaによるとishは1986年9月に発表されたバイナリとテキストを相互変換するための形式で、開発者の石塚さんから命名されたらしい(ishizuka)
#そんな古いフォーマット知らんわ笑
なので、ishフォーマットを展開できるExplzhを用いて展開しました。
するとテキストデータに変換されたのですが、まだ謎です。
 また未知のフォーマットかー。でも先頭がSで固定長のところを見ると何かの命令列のように思えなくもありません。そうしてGoogle先生に聞いてみるとMotorola S-record(SREC, S19)フォーマットにたどり着きました。
このフォーマットはどうやら携帯のメモリに書き込むための命令列のフォーマットのようで、例えば図の一行目の場合、
  • Start code : S
  • Record byte : 1
  • Byte count : 23
  • Address : 0x00
  • Data : 0x44...
  • Checksum : 0xDB
となっています。つまり0x00番地に0x44...を書き込めです。
これは指定したメモリ番地にデータを書き込む命令ということを考えると、当然メモりに何が書き込まれたかを知りたくなります。
つまりData部のみを抽出する必要があり、すると以下のようになります。
 先頭のDOCシグネチャ、V6、jxw.jexからこれは一太郎 Version 6の文書であることがわかります。もう解けたと思った瞬間。

まさかのパスワード付きですね。調べてみると今ではもうVersion 22になってセキュリティは万全ですが、Vserion 6のパスワード保護には脆弱性があるようで、それを解くアルゴリズムもあるみたいです。(ヒント:ビットシフト)
それを用いて解いてあげると、瞬く間にパスワードが得られます。
#速くてちょっとびっくりですね
それを入力してあげると、なんとCTFの歴史について語っている文書が手に入ります。
図には載せていないですが、そんな趣がある文書を経て、実は文書の内容と全く関係なく、キーワードが最後にぽつんと載っていることがこの問題の面白い点と言えますね。


2012年5月7日月曜日

SECCON CTF 2012/2/18-19 Network 200?

問題:「彼の本名を答えよ」

問題とともにpcapファイルが渡されました。
192.168.1.4から192.168.1.12へ一方的に通信が行なわれてますが、ポート、Seqから考えるとこれは通常ではあり得ないTCP通信であることがわかります。
 パケットの長さも(Len)86から124と大差ありません。

次にパケットの内容を見てみます
 すると、データ部が規則的であることがわかります。
 a1e1が非常に多くてa1fcとa2a3がちらほら見えます。
ここで、これはEUCの文字コードだと閃けば、あとは簡単です。
 EUCの文字列コード表をみると
a1e1 : "="
a1fc : "●"
a2a3 : "■"
であることがわかるが、意味ある文字列ではないのでこれで何かの図形をあらわしているに違いないと疑います。
ぼくは、ScapyというPythonのpcapファイルを扱うためのライブラリ(ユーティリティ)を使ってデータ部のみ切り出して、パケット毎に改行を差し込みました。
以下はそのPythonプログラムです

from scapy.all import *
pcap = rdpcap("network200.pcap")
data = "¥n".join(map(lambda x: str(x.payload.payload.payload), pcap))
open("network200.txt", "w").write(data)

そしてこのnetwork200.txtをテキストエディタで開くとなんと!
 「赤い彗星」という文字が見えてくるので、これで確実に正解がわかった気がします。