CTF for Beginners のバイナリ問題を見てみた

SECCON2014 オンライン予選もひと段落した。

前回のエントリ書いたときにreversing力が欲しいとか思ったので、手始めにCTF for Beginnersのバイナリを見てみた。
といってもそこまでちゃんと見たわけではないけど…。
問題名とかは忘れてしまったので、バイナリファイル5つをファイル名順に見ていく。

第1回に参加したため、第2回とは問題ファイルが違っているかもしれない。
なお、IDAはデモ版でなくてFree版を使っている。
なぜデモ版を使わないの?
と言われると特に理由はないです。しいて言えばデモ版のメリット/デメリットを評価できてないからかな。
今度調べてみよう。てか有償版ほしい。

ctf_bin1

ctf_bin1 ファイルが与えられるため、fileコマンドで確認する。

[root@kali] # file ctf_bin1
ctf_bin1: Microsoft Word 2007+

Word2007のようなので、ファイル名を"ctf_bin1.docx" にすればWordやWordpadでファイルを開ける。
f:id:f_rin:20140724012746p:plain

ctf_bin2

同じように ctf_bin2 ファイルが与えられる。まずはfileコマンドで確認。

[root@kali] # file ctf_bin2
ctf_bin2: data

バイナリデータっぽい。
サイズは、

[root@kali] # ls -lh ctf_bin2
-rw-r--r-- 1 root root 11M  628 12:36 ctf_bin2

めっちゃでかいw
ASCIIもめっちゃありそうだなぁ…と思いつつstringsコマンドを打ってみると、

[root@kali] # strings ctf_bin2
 Beginning_of_The_History 

FLAG出てきました。

ctf_bin3

ctf_bin3. ここからアセンブラを見ていく。

[root@kali] # file ctf_bin3
ctf_bin3: PE32 executable (console) Intel 80386, for MS Windows
[root@kali] # mv ctf_bin3 ctf_bin3.exe

実行してみると、誰だか聞かれる。
正しい名前を入力する必要がある模様。

C:\>.\ctf_bin3.exe
Who are you?
> hoge
Authorization failed...

stringsコマンドを打ってもFLAGっぽい文字列は見つからないため、IDAで逆アセンブルする。
見る場所は、"Who are you?" あたりの文字列から探せばすぐに見つかった。
"Alt + t" で検索窓を出して "Who are you" で検索すると、.textセクションで文字列をpushしているところが見つかるので、この付近から見ていく。
f:id:f_rin:20140724014803p:plain
検索結果
f:id:f_rin:20140724015919p:plain

赤四角の直前で al が0かどうか確認し、0の場合に右側の失敗ルートに入っているようだ。
てことは、ここでalが0以外の状態か、ここの命令を書き換えると左側(赤矢印)の成功ルートに入れる。

まずは手っ取り早く命令を書き換えてみることにする。
上図の青四角がアドレスである。"0000048E" の部分を書き換える。jmp命令2バイト分をNOP(何もしない命令:0x90)に書き換えると、条件判断をせずに無条件で左のルートに入ることができる。
IDAがFree版で書き換え機能がないので、他のバイナリエディタを使って書き換えを行った。使ったのはStirling.
f:id:f_rin:20140724020026p:plain
これで実行してみるとFLAGが表示されるかと思ったら…

C:\>.\ctf_bin3_edit.exe
Who are you?
> hoge
Authorization succeeded!
Here is the secret flag: zD^REnEwLZY (←実際は文字化け)

ですよね。でないとstrings打った時点でFLAG出てますよね。
先ほどのアセンブラの中に、"compareStr" という関数へのCall命令がある(赤四角の少し上の部分)ので、その中を見てみる。
あ、ちなみに画像に出てくる関数名や変数名はリネーム済み。コメントアウトも追記したものです。
f:id:f_rin:20140724021754p:plain

ざっくり言うと、
①で入力した文字列長が13かどうか確認して、
②で文字テーブルから取り出した値を0x39とXOR, その後3bit右ローテーションする
処理を繰り返し、入力値の正否を判断する文字列を生成している。

3B 1A 52 B0 4A B0 90 80 AA 3B 80 B8 AA
↓
02 23 6b 89 73 89 a9 b9 93 02 b9 81 93
↓
40 64 6d 31 6e 31 35 37 72 40 37 30 72 ← これと入力値を比較
 @  d  m  1  n  1  5  7  r  @  7  0  r

というわけで、正しい入力値は "@dm1n157r@70r".

C:\>.\ctf_bin3.exe
Who are you?
> @dm1n157r@70r
Authorization succeeded!
Here is the secret flag: ROTATION_AND_XOR

どうせなので、flag生成のところも見てみる。
最初に見ていたところの左側(赤矢印)ルートの真下に、親切にもFlagを生成するロジックがある。
# コメントとかリネームいれているので、これだけでわかると思うけど
f:id:f_rin:20140724020756p:plain

簡単に言ってしまうと、
①で文字数をカウントし、
②で、バイナリ内に存在するテーブル値と入力値をXORする
ことでFlagを生成している模様。
このとおりにトレースしてみると、確かにFlagが生成されている。

src = [0x12,0x2b,0x39,0x70,0x3a,0x78,0x7a,0x79,0x2d,0x01,0x79,0x74,0x2d,0x18,0x2b,0x3f]
inputstr = '@dm1n157r@70r'
l = len(inputstr)
dst = ''

for i in range(16):
    dst += str(unichr(ord(inputstr[i%l])^src[i]))                               

print dst
[root@kali] # python ctf_bin3.py
ROTATION_AND_XOR

ctf_bin4

ctf_bin4. これもアセンブラだった。

[root@kali] # file ctf_bin4
ctf_bin4: PE32 executable (console) Intel 80386 (stripped to external PDB), for MS Windows
[root@kali] # mv ctf_bin4 ctf_bin4.exe

実行してみると、Debugモードでないと動かんと言われる。
f:id:f_rin:20140724200908p:plain

IDAで開いてみる。"Ctrl + F12" で関数をチャート化してみた。
f:id:f_rin:20140724201008p:plain

memcpyとかも怪しいけど、ぱっと見、startから右下に下がっていったところ(赤四角部分)が怪しいかなと思い、見てみるとビンゴ。
f:id:f_rin:20140724202113p:plain

loc_40143Aから左下に下りたところらへんでFlagとかセットしてそう。
OllyDbgで開いてみて、このへんにBreakPointつけたら、Flag出てきました。
f:id:f_rin:20140724202009p:plain

Flag{Flag_is_always_with_debugger}

というわけで、詳しく見てみる。
上図のグラフの右側 "loc_40140F" が、文字列を取ってきてdecode処理をしている箇所になる。
ここのループを抜けるとFlag文字列が出来上がっている。その後に、左側のルートに入り、"call dispFirstMSG" 処理をしている。この関数の中身は以下のような処理が走っている。
f:id:f_rin:20140724203121p:plain

①で、1msのタイマーをセット
②でFlag文字列をセットして
③でメッセージボックスを表示

でも、1ms後にメッセージボックス消失。
という流れ。つまり、この1msのタイマーを消してやれば、Flagが消滅せずに表示される。
上図の青四角部分は、SetTimerを呼んでいるところのアドレス。"000009EC"のCall命令(000009EFまで)をNOPに書き換えてやる。
また、呼び出し元関数にデバッガモードかどうかの判断ロジックがあるため、ここも改竄してあげる。
f:id:f_rin:20140724203534p:plain
"jz loc_40151A" 命令を削除(000007EA~000007EF をNOPに書き換え)。
すると、通常起動でも動作してFlagが表示される。
f:id:f_rin:20140724203910p:plain

なお、この後の動作は、最初のFlag生成と同じdecode処理がもう一回走っている(赤枠部分)。
f:id:f_rin:20140724204115p:plain

その上で、
①2回decodeが走った後の文字列をメッセージボックスで出し、
②「2回decodeしちゃったテヘペロ (・ω<)」というメッセージボックスをもっかい出している
という流れだった。
f:id:f_rin:20140724204255p:plain

というわけで、出てくるMessageBoxは全部で3つ。
f:id:f_rin:20140724205027p:plain

という流れのプログラムなのであった。

ctf_bin5

はたまたこれもWindowsの実行ファイル。Windows縛り。

[root@kali] # file ctf_bin5
ctf_bin5: PE32 executable (console) Intel 80386, for MS Windows
[root@kali] # mv ctf_bin5 ctf_bin5.exe

実行してみると、"INVALID KEY!!" と表示されて終了。

C:\>.\ctf_bin5.exe
INVALID KEY!!

IDAで処理フローを確認して、"INVALID KEY!!" 付近を見てみると、またもや条件分岐でFlagを表示させるルートが。
f:id:f_rin:20140724205838p:plain

今回は、IDAのデモ版を使って書き換えてみる(深い意味はない。でもStirlingでやるより楽だった)。
書き換えたい"jz"命令を選択した状態で、
[Edit] > [Patch program] > [Change byte...]
f:id:f_rin:20140724210226p:plain

"jz"命令(0x74)を "jnz"命令(0x75)に書き換える。もちろんNOP(0x90)にしてもOK.
f:id:f_rin:20140724210605p:plain

書き換えたファイルを実行すると、Flagが表示される。

C:\>.\ctf_bin5_flag.exe
FLAG IS : "Salamander"

入力値をどうにかしているのか、と思ったらそうでもなさそうなので、ここを書き換えるのが解法なんかなーと思った。
# 単純にreversing力不足で見つけられていないだけかもしれないけど。

せっかくなのでFlag生成部分を見てみる。
この処理をしている関数"sub_401000"が呼ばれた際に、値を格納しまくっている。
f:id:f_rin:20140724223455p:plain

1byteなのでchar型と推測したけど、たまにバイナリも入ってるな…。
これが答えの種。
この宣言したやつが、どんどんスタックに積まれていく。

ここで宣言しまくった値を、
①1byteおきに参照して、
②0x20とxor(アルファベットの大文字/小文字逆転)して、
③変数(byte*)に入れる
処理をしているようだ。
f:id:f_rin:20140724220333p:plain

つまり、
①で"sALAMANDER"を参照し、
②"Salamander"に変換して、
③答えの変数に入れてる。
以下はOllyDbgで追ってみたもの。文字列がSalamanderの16進数(リトルエンディアン)になってる。
f:id:f_rin:20140724220531p:plain



ということで、CTF for Beginners のバイナリファイルをひととおり見てみた。
バイナリ力まだまだ足りないけど、IDAとOllyDBGは少しずつ使えるようになってきた感触がある。

strcpyだったりmemcpyだったり、はたまたprintfだったり、見ていく箇所は慣れてくると決まってくるのかもしれないけど、
実際にアセンブラから脆弱性を探してPwnできるようになるのは、まだ先かもしれない。