バッファオーバーフローを起こして、バッファオーバーランさせようとしたけど駄目だった話(続)
どうも、体調がすっかり元に戻って大学まで自転車で全力疾走しています。
やっぱり健康ってすごく大事だなと感じだ次第です。
寝込んでる時に「パフォーマンス管理の大事さ」という話を思い出してました。
さて、今日は全開に引き続き「バッファオーバーラン」に関する話です。
一週間の内の開いてる時間に色々と調査はしたのですが、タイトルの通り駄目だったので、来週からは大人しく「デザインパターン」について話を進めていきたいと思います。
バッファオーバーランを起こす理屈
さて、前回の記事ではバッファオーバーフローというものがどのようなものか、という事について説明した。
バッファオーバーフローを起こして、バッファオーバーランさせようとしたけど駄目だった話 - datchの日記
そして、前回はバッファオーバーフローによって溢れさせたデータによって、リターンアドレスを書き換えて不正なコードのアドレスを指定し、同時に注入した悪意のあるコードを実行させようとするものです。
C言語関数辞典 - C言語用語集 スタックフレーム (stack frame)
上記の記事ではC言語におけるスタックフレームの説明を行っています。
スタックフレームにはBP(ベースポイント)とSP(スタックポイント)というアドレスが指定されており、この2つの間にローカル変数や関数の間で使用する値などが格納されます。
そして、BPの1つしたにはリターンアドレスが記述されており、このリターンアドレスで関数呼び出し元のスタックアドレスに戻り、SPとBPを更新してまた処理を続けようとします。
ではこのBPの下にあるリターンアドレスを書き換えて、本当に異なる命令に飛ぶか確認していきましょう。
リターンアドレスを書き換えてみる
次に参考にした記事はこちら。
C言語で関数の戻り先アドレスを書き換えてみる - くろの雑記帳
こちらのサイトでも図で解説されており、分かりやすいのですが、さきほどのスタックフレームの復習になります。
スタックには関数で使用するローカル変数が格納されており、その下にはBP、更にその下にリターンアドレスが格納されています。
では、リターンアドレスの上書きコードを書いておきます。
#include <cstdio> void exploit() { printf("Exploit!"); } void overwrite() { void (*funcs[1])(); funcs[2] = exploit; } int main(int argc, char** argv) { overwrite(); }
このリターンアドレスはfuncsの配列の大きさを1(つまりは、funcs[0]のみでのアクセスが正常)に設定しているので下2つにリターンアドレスがあることになります。
そのため、リターンアドレスの書き換えを行うために funcs[2]と指定することでリターンアドレスが格納されているスタックアドレスのデータを上書きすることができています。
これにより、本来呼び出されていないはずのexplot()関数を呼び出すことに成功しました。
あとは、任意のコードのバイナリの作り方をどのように行うか、という部分を学ぶだけです。
シェルコードの作成方法
いや、正直ここでつまりました。
自分が行ったのは >>system()<< 関数を使って任意のシェルコードを実行させようと考え、まずは以下のようなプログラムを作成しました。
int main() { char code[]={"echo test"}; system(code); }
あとはこれをIDA Proという逆アセンブラ搭載のデバッカを通して、バイナリ情報を覗いてみる。
うむ、アセンブラの知識はそんなに深く無いし授業で習ったのも4年以上前だ。
こいつは熱い作業になりそうだぜ!!
と、いった感じで燃え尽きた。
コンパイラがどのようにコードを変換するのか、CPUがどのようにデータを実行するのか、定数などの情報の格納の仕方、などなど知らない情報が多すぎた。
systemコールの方法が逆アセンブラの情報を見ると、
"0x8d 0x44 0x24 0x16"
だったので、あとはこの関数がどのレジスタの情報を引っ張ってきてるのか、という情報と、文字列の格納の仕方を学べば良さそうだ。
でも、今回は時間が足らずに諦めた。
他にもNOPオペレーションを用いたNOPスライドで攻撃可能なアドレスの拡張とかもやってみたかったけど、基本が出来てないので今回はそれも諦めた。