攻撃手法を技術的に理解するための連載、第2回目はバッファーオーバーフローを狙う「スタック」への攻撃を解説します。
第2回では、バッファーオーバーフローの代表的な手法である、スタックベースの攻撃について取り上げます。なお、ここで取り上げる内容は、現在はマイクロソフトのSecurity Science teamの一員である、Matt Millerの「A Brief History of Exploitation Techniques & Mitigations on Windows」をベースとして記載しています。
スタックベースの攻撃、特にスタックオーバーフローは、現在では古典的な攻撃手法です。マイクロソフトが公表している「Security Intelligence Report Volume 16」によれば、現在はスタックベースの攻撃はほとんど見られない状況ですが、現在の攻撃手法とその防御手法を理解するためには、スタックに対する攻防の理解が欠かせません。
メモリーベースの攻撃の多くは、プログラムが読み込むデータ(.doc, .pdf, など)の中に、(1)脆弱性を使って実行アドレスを変更する部分と、(2)攻撃のブートストラップとして稼働するシェルコードが埋め込まれます。
スタックに限っただけでも、さまざまな脆弱性悪用手法が存在します。図2は、スタックベースの攻撃手法と、Visual Studio(コンパイラー/リンカー)による攻撃の対策の推移を表したものです。
A Brief History of Exploitation Techniques & Mitigations on Windows
http://media.blackhat.com/bh-us-12/Briefings/M_Miller/BH_US_12_Miller_Exploit_Mitigation_Slides.pdf
スタックオーバーフローを理解するためには、関数やサブルーチン(以下、関数)の仕組みを、アセンブラーレベルで理解する必要があります。
プログラムが関数を呼び出すと、プログラムの制御は、引数とともに関数に移行し、関数の処理が終わると、関数を呼び出した次の命令に制御が戻ってきます。この一連の動作は、スタックと呼ばれる動的なデータ領域を使うことで実現しています。図3のように、関数内で使用するローカル変数の後ろにEIP(戻り番地)が記録されます。
次に実行されるCPU命令のアドレスを指す。CALL命令実行時にスタック上に保存される。JUMP系の命令はEIPレジスターを直接書き換える。
スタックフレーム上で、関数の引数やローカル変数を参照する時に使う基準点となるアドレスを格納する32ビットレジスター。ESPレジスターと違い、EBPレジスターは手動で変更する。「フレームポインター(Frame Pointer)」とも呼ばれる。
例えば、ローカル変数として確保した配列にデータを読み込む場合、データサイズを超えた入力が行われると、戻り番地が上書きされることになります。この際に、実行したいコード(プログラム)、つまりシェルコードが格納されているアドレスで戻り番地を書き換え、その後ろにシェルコードを埋め込むことで、入力データ中のシェルコードを実行することが可能となります。
リスト1は、バッファーオーバーフローの要因となる典型的な例で、図4は、入力データとスタックのマッピングを図にしたものです。リスト1では、入力される文字列に長さ制限のないgets関数を使っているため、256バイトを超える入力があるとバッファーオーバーフローが発生し、260バイト目に実行したいコードのアドレスを書き込むことで、そのコードを実行することができます。
int vulnerable_func1 { char buf[256]; gets(buf); return 0; }
Copyright © ITmedia, Inc. All Rights Reserved.