簡単なバッファオーバーフローを再現してみる(3)

かなり間が空いてしまいましたが、前回に引き続きバッファオーバーフロー再現していきたいと思います。自分の復習用のメモです。

こちらの参考書の内容を読み、参考にして記事を書いています。より詳しく知りたい方は参考書を読んでいただければと思います。

前回、スタッグセグメントの戻りアドレス値をBOFにより上書きして、強制的に当該アドレスの処理を実行させる手法でしたが、今回は任意のコードを挿入し、それを実行させる手法です。

ちょっと用意するべきプログラムが多いので、今回は実際に再現するよりも、攻撃プログラムが書かれた箇所のみを解説していきます。プログラム中にある、「./notesearch」はroot権限のSUIDが設定されたBOFの脆弱性があるプログラム、ということだけ、ここでは確認できればOKです。(そちらも併せて詳しく、という方は書籍を読んでみていただければ、と思います。)

参考コード

main()から順番に解読していこうと思います。

まずは

12 行目command = (char *)malloc(200)ですが、200byteをヒープメモリに確保しchar型ポインタを返します。この際、確保された領域はゼロクリアされませんので、次のbzero(command, 200)でメモリ領域をゼロで初期化します。[malloc]参考URL [bzero]参考URL

次の15行目 strcpy(command, “./notesearch ‘ “);は、commandに”./notesearch ‘ “をコピーするだけ、その次のbuffer = command + strlen(command)は、最初ちょっとわかりづらかったので、以下のサンプル書きました。

先ほどの部分を抜き出して、変数の値を出力するだけですね。commandとbufferはchar型のポインタなので、ポインタが指すアドレスを出力させています。

実行すればわかるとおもいますが、commnadのポインタが指すアドレスから” ./notesearch ‘ “の文字列の分、ポインタを進めたアドレスがbufferに格納されています。(command + 14byte )

次のif文とoffsetへの代入は、引数を与えたときのみ、今は解説省略します。

次のret = (unsigned int) &i – offsetでは、iのアドレスからmain関数開始時に宣言されたoffset文引いたアドレスを戻りアドレスとしてretに格納しておきます。

その後のfor文では4byteずつ40回(160byte分)、先ほど設定した戻りアドレスretをbufferの中に繰り返し埋めていきます。(buffer開始から160byte全てがretで設定された戻りアドレスの状態)

Screen Shot 2013-04-10 at 0.32.00

その後memset()でbufferから60byte分、0x90を上書きしていきます。(0x90の意味はコードの解説が終わったらまとめて)(buffer開始から60byteは0x90、以降61byteから160byteまでは戻りアドレスの状態)

Screen Shot 2013-04-10 at 0.32.20

そしてmemcpy()でbufferから60byte後ろに、グローバル変数として宣言されているshellcode[]を更に上書きします。(buffer開始から60byteは0x90、61byteからsizeof(shellcode)-1はグローバル変数shellcode、そして以降は戻りアドレス、の状態)Screen Shot 2013-04-10 at 0.32.40

最後にstrcat()でcommandの末尾にシングルクォーテーションを追加します。ここで注意するのは、文字列は先頭アドレスからnullまで、なので、commandのすぐ後ろから開始されているbufferもこの場合commandに含まれます。つまり

commandの内容: “./notesearch ‘ ” [bufferの内容] “‘ “となっています。

Screen Shot 2013-04-10 at 0.50.38文字化けしてますが、上記の内容と一致していることがわかると思います。

そして、system()関数でcommandに格納されている文字列をOSコマンドとして実行することで攻撃を実行します。

ここで0x90という数字を繰り返しメモリに書き込んでいますが、これはx86アーキテクチャにおけるnop命令(0x90)の連続です(NOP(No OPerationの略)スレッドと呼ばれる)

これは、何もせずに次の処理に進めるだけの命令ですが、これを繰り返しメモリに書き込むことで戻りアドレスが、NOPスレッド内のアドレスであれば、どこでもシェルコードまで自動的に滑っていきます。これによって、シェルコードを実行させやすくしているようです。

ただ、この攻撃手法の難しいところが、notesearchでBOFを発生させる際の戻りアドレスをある程度試行錯誤しなくてはならない、ということです。

main()関数内で一番最初の変数iがスタックフレームにpushされたときのアドレスを参考値として、そこから変数offset(初期値270)を減じた値を戻りアドレスとしていますね。

引数によって、offsetを調整できるように、 引数チェックのif文の箇所の処理があります。

ここで設定する戻りアドレスを、notesearchでBOFさせたときにNOPスレッド内のアドレスの範囲内にできれば、シェルコードが実行される、という仕組みですね。

変数shellcodeの中に書かれている16進数の文字列は、シェル起動の命令が書かれたものです。これの説明はまた改めます。(これを実行するとシェルが起動する、ということだけ今は確認できればと思います。)

 

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です