バッファオーバーラン (buffer overrun) とは、コンピュータのプログラムにおける、設計者が意図していないメモリ領域の破壊が起こされるバグのひとつ、またはそれにより引き起こされた現象を言う。バッファオーバーフロー (buffer overflow) とも呼ばれる。
バッファオーバーランはコンピュータセキュリティ上の深刻なセキュリティホールとなりうるため、バッファオーバーランが起こる可能性のあるコンピュータプログラムはすぐに修正する必要がある。 バッファオーバーランは、現在もっとも重大なセキュリティホールのひとつと考えられている。あるプログラムでバッファオーバーランの脆弱性が発見されると、一般に高い優先度で修正作業が行われ、更新バージョンのプログラムや修正パッチの公開・配布などが行われる。
目次 - 1 概要
- 2 具体例
- 2.1 簡単な例
- 2.2 電子メールアドレスを題材にした例
- 2.3 C言語特有の例
- 3 バッファオーバーランが起こす問題
- 4 悪用
- 5 エクスプロイト
- 5.1 スタックベースのエクスプロイト
- 5.2 ヒープベースのエクスプロイト
- 5.3 エクスプロイトに対する障壁
- 5.4 エクスプロイトの実際
- 5.4.1 NOPスライド
- 5.4.2 レジスタに格納されているアドレスへジャンプする方法
- 6 防御的対策
- 6.1 プログラミング言語の選択
- 6.2 安全なライブラリの利用
- 6.3 バッファオーバーフロー保護
- 6.4 ポインタ保護
- 6.5 実行保護
- 6.6 アドレス空間配置のランダム化
- 6.7 ディープパケットインスペクション
- 7 歴史
- 8 脚注
- 9 関連項目
- 10 外部リンク
|
バッファオーバーランは、入力データ(多くはデータサイズ)を検査しないプログラムの脆弱性によって、バッファ領域として設定されているアドレス範囲を超えたメモリが上書きされ、誤動作が引き起こされる。バッファオーバーランが起きた場合、通常は該当プログラム(ないしオペレーティングシステム)の動作が不安定になったり停止したりする。しかし、しばしばそのようなバグを含むプログラムに対して、意図的に悪意のあるデータ(コード)を与えることにより、コンピュータの動作を乗っ取ってしまえる可能性が問題となる。
バッファオーバーフローは、上書きされる領域の対象によって、スタックバッファオーバーフロー、ヒープオーバーフローとに大別される。
スタック、ヒープとも、プログラムの実行に不可欠なデータ(例えばサブルーチンのリターンアドレスや、ヒープ内のコード)を内包することがある。これらに近接するバッファにおいてオーバーランを引き起こすことで、これらのデータを意図的に上書きさせ、意図したコードを実行させることが可能になる。
この節は執筆中です。加筆、訂正して下さる協力者を求めています。
[編集] 簡単な例
以下の例では、プログラム中の隣接したアドレスに2つのデータ項目が定義されている。一つは8バイトの文字列バッファA、もう一つは2バイトの整数Bである。初期状態では、Aは0で初期化されており文字は入っていない。また、Bには整数1979が格納されている。文字のバイト幅は1バイトとする。
variable name | A | B |
---|
value | [空文字列] | 1979 |
---|
hex value | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 07 | BB |
---|
ここで、プログラムがバッファAにヌル終端文字列"excessive"を書きこもうとした場合を考える。文字列の長さチェックが行われていないと、この処理でBの値が上書きされてしまう。
variable name | A | B |
---|
value | 'e' | 'x' | 'c' | 'e' | 's' | 's' | 'i' | 'v' | 25856 |
---|
hex | 65 | 78 | 63 | 65 | 73 | 73 | 69 | 76 | 65 | 00 |
---|
プログラマとしてはBを変更する意図はなかったが、Bの値は文字列の一部で置き換えられてしまった。この例ではビッグエンディアンとASCIIコードを仮定しているため、文字"e"とゼロというバイト列は整数25856として解釈される。ここで、仮にプログラム中でAとB以外にデータ項目変数が定義されていないとものすると、さらに長い文字列を書き込んでBの終端を超えた場合にはセグメンテーション違反などのエラーが発生してプロセスが終了する。
[編集] 電子メールアドレスを題材にした例
コンピュータプログラムを作るとき、固定長のバッファとよばれる領域を確保してそこにデータを保存するという手法がよく使われる。
たとえば、電子メールアドレスは200文字を超えないだろうと予想して
- 200文字分の領域をバッファとして用意する。
- ユーザが200文字より長いメールアドレスを入力する。
- プログラムがバッファの大きさをチェックせずに入力データを書き込む。
- バッファとして確保した領域をはみだしてデータが書き込まれてしまう。
これがバッファオーバーランである。仮にはみ出した部分にプログラムの動作上意味を持つデータがあれば、これを上書きして破壊することにより、プログラムはユーザの意図しない挙動を示すであろう。
このようにバッファオーバーランは、プログラムが用意したバッファの大きさを超えてデータを書き込んでしまうバグである。
[編集] C言語特有の例
C言語の標準入出力関数であるgets関数はバッファ長のチェックを行わないで標準入力をバッファに書き込むので、この関数を使う全てのプログラムには、バッファオーバーランによる不正動作の危険性がある。また使い方が分かりやすいという理由でC言語初心者向けの入門プログラミングでしばしば用いられるscanf関数も書式指定を誤った場合は同じ危険性を持っている[1]。これらの関数を実用的なプログラムで用いる場合には注意が必要である。
次のプログラムはgets関数を用いた例である(セキュリティ上、gets関数はそれ自体をテストする以外の目的で使用されるべきではない。Linux Programmer's Manualには「gets()は絶対に使用してはならない。」と書かれている)。バッファ長として200バイト確保されている。gets関数はバッファの長さについては関知しないため、200バイトを超えても改行文字かEOFが現れなければバッファオーバーランが発生する。
#include int main(int argc, char *argv[]) { char buf[200]; gets(buf); .... }
gets関数の代わりにfgets関数を用いることで、この問題を回避できる(fgets#getsを置き換える例等を参照)。fgets関数にはバッファのサイズを渡すことができ、このバイト数を超えてバッファに書き込みを行わない。したがってバッファサイズが正しく設定されていれば、fgets関数においてバッファオーバーランは起こり得ない。
これ以外のC言語の標準文字列処理関数の多くにも同様の問題(脆弱性)がある。
[編集] バッファオーバーランが起こす問題
オペレーティングシステム(OS)によっては、プログラムのコード領域とデータ領域を区別せず、コードがデータ領域に書かれていてもそのまま実行してしまう物がある。
もっとも典型的なバッファオーバーランは、データ領域のうちでもスタック領域に対するものである。前述のバッファがスタック領域に割り当てられたものである場合(この割当てはC言語の自動変数で典型的である)、はみ出したデータがスタック領域の当該バッファ割当て部分よりも外の部分を書き換えてしまう。一方、スタック領域にはプログラムカウンタにリストアされるべきサブルーチンからのリターンアドレスが格納されているが、そのリターンアドレスをバッファーオーバーランしたデータで書き換えてしまうことになる。これを利用した攻撃をReturn-to-libc攻撃と呼ぶ。
バッファーオーバーラン等の不正動作に対する保護機能がないようなOS上で実行されるアプリケーションソフトウェアでは、プログラム作成者ないし利用ユーザの意図の有無に関わらず、常にこの危険性を含んでいる。現在大衆向けに販売されているOSの多くは、このようなメモリ保護機能を持たないことが問題の根底にある。
!doctype>