株式会社Ninjastars
セキュリティエンジニア:一瀬健二郎
このページでは弊社で行っている脆弱性診断技術の教育資料を公開しています。
Reversing編ではCTFや脆弱性診断においてのリバーシング技術の基本をお教えできればと思います。
またGhidraのチュートリアルとしても利用できるようにしております。
Ghidraについてはこちらを参照。
NSA 米国国家安全保障局 リバースエンジニアリングツール Ghidraを使う - 株式会社Ninjastars 技術研究部
参考問題
Hack.lu CTF 2013のRoboAuthという問題を使います。
http://shell-storm.org/repo/CTF/Hacklu-2013/Reversing/RoboAuth-150/
使用ツール
Ghidra
x64dbg
x64dbgはオープンソースのWindows用の高機能デバッガです。
x64dbg
上記サイトのDownload->遷移後のサイトでsnapshot_201......zipの最新版を選択するとダウンロードできます。
x64dbgの使い方はOllyDbgと似ています。以下のサイトが同じRoboAuthをOllyDbgで解いているので参照していただければと思います。
CTFの過去問を解いてOllyDbgの使い方を覚える。 - kira924ageの雑記帳
RoboAuth
起動するとコマンドプロンプトが立ち上がり、パスワード入力を求められます。
適当に入力するとプログラムが終了しています。
今回は静的解析に加え、動的解析を行いこのCTFを解いてみます。
Tips:
動的解析:実際にプログラムを実行し、デバッガなどを用いて対象プログラムを解析すること。
まずGhidraを用いてRoboAuth.exeを静的解析してします。
パスワード1の解法
初期設定のAnalysis Optionsで赤文字のAggressive~とCondense~の項目にチェックを入れてください。
(入れないと逆アセンブルが上手く行きませんでした。)
最初にWindow->Defind Stringから怪しい文字列を探します。
"You passed level1!"という文字列が見つかりました。
文字列をクリック->逆アセンブラ画面で"s_You_passed_level1!_00409126"の上で右クリック->References->Show References Addressをクリック。
ポップアップされたウィンドウの00401b75の部分をクリックすると逆アセンブラ画面がそのアドレスに移動します。
処理を読み解きましょう。
1)scanfで文字列を入力待機して取得。
2)入力文字列となんらかの文字列をstrcmpで比較
3)一致してたら"You_passed_level1!"をputsで出力。
2)の入力文字列と比較しているなんらかの文字列を取得するために今回はx64dbgを用いて動的解析を行います。
x64dbgを立ち上げましょう。
1)x96dbg.exeをダブルクリックして実行
2)x32dbgとx64dbgの選択画面が出ます。RoboAuthは32bitアプリケーションのためx32dbgを選択します。
3)ファイル->アタッチ->名前欄がRoboAuthになっているところを選択して右下のアタッチを選択します。
アタッチ完了すると以下の画面となります。
アタッチ後はプロセスが停止しているので、F9を押して実行再開します。
ブレークポイントというのを利用して先ほど解析した文字列比較処理でプログラムを停止させるようにしてみます。
逆アセンブル画面で右クリック->移動->式-00401b6c
00401b6c e8275f0000 call
の命令の部分でF2でこの命令部分にブレークポイントが設置できます。
RoboAuthでパスワードを適当に入力して実行するとこの部分で処理が止まります。
x64dbgの右下のスタックビューを見ると以下のようになります。
これでパスワード1はr0b0RUlez!であることが分かりました。
パスワード2の解法
パスワード1の場合scanfで文字列入力を待機していました。
これからパスワード2もscanfを使用していることが予想されます。
Symbol Treeの下部のFilterのところにscanfと入力してください。
Functionsフォルダの方の下にscanfが見つかるのでそれをクリックします。
逆アセンブラ画面の int __cdecl scanf(char * _Format, ...) で右クリック->References->Show References to Addressで呼び出し元が見つかります。
ポップアップされた画面で2つのアドレスが表示されます。パスワード1のときに00401b54にscanfがあったので、004015b2の方をクリックします。
クリックすると逆アセンブラ画面のアドレスが移動し以下の画面となります。
処理の流れを追いましょう。
1)scanfで文字列を入力待機して取得。
2)入力文字列となんらかの文字列をFUN_00401547に引数として渡す。
3)戻り値が0以外であれば0x004015dcにジャンプしてExitProcessで終了。戻り値が0であれば何らかの文字列をputsで表示してその後終了。
逆アセンブラ画面でcall FUN_00401547の部分をクリックするとFUN_00401547に移動できます。移動後の画面が以下です。
再度x64dbgで動的解析してみましょう。
RoboAuthを実行して立ち上げて、パスワード(1)の時と同じようにアタッチします。
今回はオプション->ユーザー設定->例外->範囲の追加->START:80000003 END:80000003->OK->保存。
これは0040161Fにあるint3を使ったアンチデバッグを無視するためです。
逆アセンブル画面で右クリック->移動->式-00401547
00401547 55 push ebp
の命令の部分で「F2」でこの命令部分にブレークポイントが設置できます。
x64dbgでF9を押してプログラムを実行再開し、RoboAuthに対してr0b0RUlez!と入力してEnterを押します。
2つ目のパスワードを求められるので、RoboAuthに対して試しにaを10文字”aaaaaaaaaa”と入力します。
00401547で処理が停止します。
Default(stdcall)となっているビューの下の[esp+4]が入力文字列である"aaaaaaaaaa"となっています。
[esp+8]の部分で右クリック->ダンプで[0060F580]を表示->結果が以下の画面です。
左下のダンプ画面を見ると、"u1nnf2lg"という文字列が現れました。
0040154C~0040155Eまでの処理からこの文字列と2でxorを取った値を入力文字列と比較しているのが分かります。
python等でスクリプトを書けば即座に答えは分かりますが今回は別の手法で求めます。
0040155Bの処理にF2でブレークポイントを設置し実行するとレジスタ画面が以下のようになります。
上記より正解の1文字目は'w'であると分かりました。
0040155D EB07 je roboauth.401566
逆アセンブラ画面で上記je roboauth.401566の部分でダブルクリック->jmp roboauth.401566に変更->OK。
これでどのような場合でも比較結果が等しい場合と同じになりました。
最後にF9を連続で押し1文字ずつ答えを取得すると
w3lld0ne
が答えであると分かります。
(実際に入力しても即座にプログラムが終了しますが、これが答えです。)
注意事項
本レポートに記載されている内容を許可されていないソフトウェアで行うと、場合によっては犯罪行為となる可能性があります。そのため、記事の内容を試す際には許可されたソフトウェアに対してのみ実施するようにしてください。
本レポートについて
お問い合せ
E-mail:ichise@ninjastars-net.com
株式会社Ninjastars セキュリティエンジニア
一瀬健二郎