株式会社Ninjastars
セキュリティエンジニア:吉村碧海
CTFのpwnやreversing問題ではIDAやGhidra等の解析ツールで実行バイナリを逆アセンブルし、プログラムの内部動作を解析する技術(リバースエンジニアリング技術)が求められます。
今回は皆様にリバースエンジニアリング技術への理解を深めてもらうために、簡単なソースコードとそれをコンパイルした実行バイナリを逆アセンブルし、その際の結果と機械的な動作を解説していきたいと思います。
解説の前に少しだけ自己紹介をさせていただきます。
私はNinjastarsにてリバースエンジニアリングやプログラミングを学びながらお仕事をさせて頂いております。
今回初めてブログ投稿の任を任されました。
まだまだ若輩者ですが、私の解説を是非御一読頂ければ幸いでございます。
今回使用するソフト
IDA free
gdb-peda
今回は途中からgdbの拡張スクリプトであるgdb-pedaを使ってデバッグしていきます。
もしまだgdb-pedaをインストールされていない方は以下のリンクをご参照下さい。
gdb-peda インストール for Ubuntu - miyagawNote
まず最初に簡単なソース文を用意したのでご覧ください。
#include<stdio.h> int main() { int x=12345; int y; printf("please input password.\n"); scanf("%d",&y); if(x==y) printf("correct! flag is Ninjastars\n"); else printf("wrong...\n"); return 0; }
はい、ビックリする程簡単ですね。
下記コマンドでコンパイルを行ってください。
gcc -m32 -no-pie -o sample sample.c
gccのコンパイルオプションについては当ブログで解説を行っております。
自作ゲーム:チートチャレンジ2 - 株式会社Ninjastars 技術研究部
コンパイルに成功したら、ターミナルで実行してみます。
パスワードの入力が求められたので、先程のソースから[12345]という数字を入力すればcorrect...つまり問題に正解してフラグをゲットできることが読み取れるのでここはその数字を与えてみましょう。
はい、無事に正解することができました。
今回はソース文を予め知っていたのでパスワード入力をパスすることができたのですが、実際の環境では基本的に実行プログラムだけ渡されるので、それを逆アセンブルして解析する必要があります。
なので今回も静的解析ツールIDAで解析をかけていきましょう!
IDAで解析をかけた結果がこちらになります。
一般的にフラグの文字列は暗号化等の処理を加えて隠されている場合が多いのですが、今回の問題は解説の簡略化のため読み取れる形で表示されています。
ソースを一見しただけでは簡単な処理に見えましたが、実際の環境の中では様々な動作をしていることがわかると思います。
そして今回の問題のキー部分となるのが[cmp [ebp+var_10], eax]の結果により処理が2つに分岐している部分です。
片方はwrong...と表示されフラグが表示されていませんが、もう片方は"flag is Ninjastars"という文字列が表示されています。
[cmp [ebp+var_10], eax]ではscanfの直前で定義されたeaxと、その前の
[mov [ebp+var_10], 3039h]にて代入された3039hという値を比較しています。
ちなみに3039hは16進数表記の値です。10進数に直すと[12345]という値になっているので、ソースを知らずともIDAだけで問題を解決することができます!
このように逆アセンブルして解析をし、それによって得たパスワードを入力して問題を解く方法を皆様も理解できたかと思います。
しかし、それだけではボリュームとして些か物足りないと思われるので今回はもう一つ、解法をご紹介したいと思います。
それは12345の数字を用いずにフラグを入手できる方法です。
ここからは動的解析ツールgdb-pedaを使っていきたいと思います。
まずターミナル上でgdbで実行ファイルを開いてみましょう。
その後[r]コマンドでgdb上で一度プログラムを実行してみます。
パスワードが求められました。ここはあえて[12345]以外の適当なパスワードを打ち込んでみましょう。
はい、wrong...と表示され不正解になってしまいました。
先にIDAで解析した通り、[12345]を入力しなければ、間違った分岐に進んでしまいます。
しかし、gdbではプログラムの実行時の途中で一時停止することが可能です。
今回はwrong!の分岐に飛ぶ直前で一時停止をしたいので、その部分のアドレスを先程のIDAの解析結果から探してみましょう。
直前部分[jnz short loc_8048549]の部分のアドレスはIDAでは[08048533]と表示されていますね。
gdbのコマンド[b *(アドレスの値)]で一時停止部分、ブレークポイントを設けることができます。
アドレスは16進数表記なので0x08048533、というわけで今回は[b *0x08048533]のコマンドを使用します。
再びgdbの画面に戻り、[b *0x08048533]でブレークポイントを作成します。
そうしたら[r]コマンドと適当なパスワード[11111]でその部分まで実行をしてみましょう。
一時停止部分で止まりました。
今回はcmp命令の演算結果による分岐先を変更したいので、CPUのステータスレジスタの一種であるフラグレジスタを変更していきます。
一時停止中に[i r]のコマンドでレジスタの中身を詳しく表示することができます。
その中に eflags 0x206 [ PF IF ] とあります。
一見しただけでは何を意味しているかわからないと思うので参考サイトをご用意しました。
このサイトを丸ごと解説して皆様に更なる理解を深めて頂くのも結構なのですが、それではこの記事の文字数と私の記述時間がエライことになりそうなので、フラグレジスタの説明部分だけかいつまんで解説させていただきます...
画像の表を見て参照をしていただくと、レジスタの中身 0x206 [ PF IF ] とはPF : パリティフラグと IF : 割り込み可能フラグがセットされていることがわかります。
cmp命令は値が等しい、つまり比較して0であればいいので、フラグレジスタの中身にZF : ゼロフラグを追加しましょう。
[set $eflags=(フラグレジスタの値)]のコマンドでフラグレジスタの値を変更することができます。
ZFは6ビット目のフラグなので2^6=64、16進数に変換して0x40。
この値を元の値[0x206]に足した[0x246]がセットするべきフラグの値であることがわかります!
というわけで[set $eflags=0x246]で一時停止部分でのフラグレジスタに書き込んで、[c]のコンテニューコマンドでプログラムの続行をしてみましょう。
flagが表示され問題に正解することができました!
このようにプログラムの分岐の結果自体を書き換えてパスワードを知らずともflagを入手する方法もあります。
CTFの問題等でも多種多様なアプローチと解答例があるので皆様も色々な知識を深めていただければ幸いでございます。
拙い文章ではございましたが、御一読ありがとうございました!
注意事項
本レポートに記載されている内容を許可されていないソフトウェアで行うと、場合によっては犯罪行為となる可能性があります。そのため、記事の内容を試す際には許可されたソフトウェアに対してのみ実施するようにしてください。
本レポートについて
お問い合せ
E-mail yoshimura.aoi@ninjastars-net.com
株式会社Ninjastarsエンジニア
吉村 碧海
イベント開催のお知らせ
弊社のゲームセキュリティイベント開催のお知らせをさせていただきます。
この度、9月17日(火) 19:00-22:00から
株式会社Ninjastars主催、第2回のゲームセキュリティイベント
「Defense against Game Hack Cheater's Society」
を弊社オフィスにて開催いたします。
今回はCheater's Societyという題材で
RMT業者やチート代行サービスをはじめとする
チーター側の社会の実情に焦点を当てたセミナーとなります。
参加申し込みはこちらから受け付けております。
https://forms.gle/RbKnmH2FYW6LA8eQ8
皆様のご参加を心よりお待ちしております。