バッファオーバーラン

出典: フリー百科事典『ウィキペディア(Wikipedia)』
2014年4月21日 (月) 21:27時点におけるGecko.gecko (トーク)による版 (外部リンク)
(差分) ← 古い版 | 最新版 (差分) | 新しい版 → (差分)
移動先: 案内検索

バッファオーバーランテンプレート:Lang-en-short) 、バッファオーバーフローテンプレート:Lang-en-short)とは、コンピュータプログラムにおける、設計者が意図していないメモリ領域の破壊が起こされるバグのひとつ、またはそれにより引き起こされた現象を言う。

バッファオーバーランはコンピュータセキュリティ上の深刻なセキュリティホールとなりうるため、バッファオーバーランが起こる可能性のあるコンピュータプログラムはすぐに修正する必要がある。 バッファオーバーランは、現在もっとも重大なセキュリティホールのひとつと考えられている。あるプログラムでバッファオーバーランの脆弱性が発見されると、一般に高い優先度で修正作業が行われ、更新バージョンのプログラムや修正パッチの公開・配布などが行われる。

概要

バッファオーバーランは、入力データ(多くはデータサイズ)を検査しないプログラムの脆弱性によって、バッファ領域として設定されているアドレス範囲を超えたメモリが上書きされ、誤動作が引き起こされる。バッファオーバーランが起きた場合、通常は該当プログラム(ないしオペレーティングシステム)の動作が不安定になったり停止したりする。しかし、しばしばそのようなバグを含むプログラムに対して、意図的に悪意のあるデータ(コード)を与えることにより、コンピュータの動作を乗っ取ってしまえる可能性が問題となる。

バッファオーバーフローは、上書きされる領域の対象によって、スタックバッファオーバーフローヒープオーバーフローとに大別される。

スタック、ヒープとも、プログラムの実行に不可欠なデータ(例えばサブルーチンのリターンアドレスや、ヒープ内のコード)を内包することがある。これらに近接するバッファにおいてオーバーランを引き起こすことで、これらのデータを意図的に上書きさせ、意図したコードを実行させることが可能になる。

テンプレート:節stub

具体例

簡単な例

以下の例では、プログラム中の隣接したアドレスに2つのデータ項目が定義されている。一つは 8 バイトの文字列バッファ A、もう一つは 2 バイトの整数 B である。初期状態では、A は 0 で初期化されており可読な文字は入っていない。また、B には整数1979が格納されている。文字のバイト幅は 1 バイトとする。

変数名 A B
NUL NUL NUL NUL NUL NUL NUL NUL 1979
16進数値 00 00 00 00 00 00 00 00 07 BB

ここで、プログラムがバッファ Aヌル終端文字列「excessive」を書きこもうとした場合を考える。文字列の長さチェックが行われていないと、この処理で B の値が上書きされてしまう。

変数名 A B
「e」 「x」 「c」 「e」 「s」 「s」 「i」 「v」 25,856
16進数値 65 78 63 65 73 73 69 76 65 00

プログラマとしては B を変更する意図はなかったが、B の値は文字列の一部で置き換えられてしまった。この例ではビッグエンディアンASCII コードを仮定しているため、文字「e」とゼロというバイト列は整数 25,856 として解釈される。ここで、仮にプログラム中で AB 以外にデータ項目変数が定義されていないとものすると、さらに長い文字列を書き込んで B の終端を超えた場合にはセグメンテーション違反などのエラーが発生してプロセスが終了する。

電子メールアドレスを題材にした例

コンピュータプログラムを作るとき、固定長のバッファとよばれる領域を確保してそこにデータを保存するという手法がよく使われる。

たとえば、電子メールアドレスは200文字を超えないだろうと予想して

  1. 200文字分の領域をバッファとして用意する。
  2. ユーザが200文字より長いメールアドレスを入力する。
  3. プログラムがバッファの大きさをチェックせずに入力データを書き込む。
  4. バッファとして確保した領域をはみだしてデータが書き込まれてしまう。

これがバッファオーバーランである。仮にはみ出した部分にプログラムの動作上意味を持つデータがあれば、これを上書きして破壊することにより、プログラムはユーザの意図しない挙動を示すであろう。

このようにバッファオーバーランは、プログラムが用意したバッファの大きさを超えてデータを書き込んでしまうバグである。

C言語特有の例

C言語の標準入出力関数であるgets関数はバッファ長のチェックを行わないで標準入力をバッファに書き込むので、この関数を使う全てのプログラムには、バッファオーバーランによる不正動作の危険性がある。また使い方が分かりやすいという理由でC言語初心者向けの入門プログラミングでしばしば用いられるscanf関数も書式指定を誤った場合は同じ危険性を持っている[1]。これらの関数を実用的なプログラムで用いる場合には注意が必要である。

次のプログラムはgets関数を用いた例である(セキュリティ上、gets関数はそれ自体をテストする以外の目的で使用されるべきではない。Linux Programmer's Manualには「gets()は絶対に使用してはならない。」と書かれている)。バッファ長として200バイト確保されている。gets関数はバッファの長さについては関知しないため、200バイトを超えても改行文字かEOFが現れなければバッファオーバーランが発生する。

#include <stdio.h>
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の多くは、このようなメモリ保護機能を持たないことが問題の根底にある。

悪用

クラッカーは、このバッファオーバーランを意図的に起こしてデータの改竄やコンピュータシステムの損壊につながる操作をおこなう(通常は、ワームウイルス等の不正プログラムを作成し、それに攻撃を実行させる)。

通常は、不正アクセスの手段として不正なデータをコンピュータに対して送信すると言うことがあるが、バッファオーバーラン攻撃を行う場合には、送信データに不正なプログラムのコード(シェルコード)を挿入し、さらに前述のスタック領域上のリターンアドレス等を、この不正コードが存在するアドレスに書き換える事等により、任意の不正なコードを相手のコンピュータにおいて実行させ、OS上の管理者権限を不正に奪取するなどさまざまな攻撃を行う。

近年、コンピュータの制御を乗っ取り、攻撃を行うウイルスはバッファオーバーランを利用した物が多い。脆弱性を示すために作られたプログラムExploitを悪用し、そのプログラムにウイルス機能を載せた物が大多数を占める。2003年8月インターネットトラフィックにおいてバックグラウンドノイズとされるトラフィックが1kbps未満から突然30〜40kbpsに跳ね上がった。これはWindowsのRPCサブシステムにおけるバッファオーバーランによるセキュリティホールを攻撃し、制御を乗っ取り自らウイルスを放出するMSBlastウイルスによる攻撃が全世界規模で発生したためである。

C言語でかかれ、古いライブラリ関数を多用している、そして多くの文字列処理を行っている、"sendmail"プログラムは近年こそ毎年のペースまで下がったが、以前は毎月のようにバッファオーバーランによるセキュリティホールが発覚し、修正されていた。そしてsendmailを突破口としてセキュリティを破られたシステムはとても多く、その数はWinnyによる情報流出を上回るものである。このようなセキュリティ上の観点から(またライセンスの関係もあるが)sendmailを標準プログラムから排除する動きがあり、いくつかのOSディストリビューションの標準セットからsendmailは取り除かれてしまった。

エクスプロイト

バッファオーバーフローに起因する脆弱性を利用したエクスプロイトは、コンピュータ・アーキテクチャオペレーティングシステム、メモリ領域によって大きく内容が異なる。たとえば、ヒープ領域(動的メモリ確保で利用される領域)に対するエクスプロイトコールスタックに対するそれとは大きく異なる。

スタックベースのエクスプロイト

テンプレート:Main

悪意のある利用者がスタックベースのバッファオーバーフローを発生させてプログラムの処理を操作する際には、以下のような方法が用いられる。

  • スタック上でバッファの近くにあるローカル変数の値を上書きして、プログラムの動作を攻撃者にとって有利なように変更する。
  • スタックフレーム中の戻りアドレスを上書きする。関数から戻る際、攻撃者が指定したリターンアドレス(通常はユーザの入力を格納しているバッファ)から処理が再開されるようにする。
  • 後で実行される関数ポインタ[2]

または例外ハンドラを書き換える。

ここで、ユーザの入力したデータのアドレスは未知であるが、アドレスがレジスタに格納されていることは分かっているという場合には、トランポリン(trampolining)と呼ばれる手法が利用される。この手法では、ユーザの入力したデータへジャンプするオペコードのアドレスをリターンアドレスへ上書きする。例えばアドレスがレジスタRに格納されている場合、jump R(あるいはcall Rなど)というオペコードが格納されているアドレスへジャンプさせることでユーザの入力したデータを実行させる。この手法で使用するオペコードはDLLや実行ファイルの中のものを利用する。ただし、一般的にオペコードのアドレスにヌル文字が含まれていてはならず、また処理に使用するオペコードのアドレスはアプリケーションやオペレーティングシステムのバージョンによって異なる。Metasploitプロジェクトはこのような目的に適したオペコードのデータベースの一つであり、Windowsで使用できるオペコードが記載されている[3]

名前が似ているスタックオーバーフローと混同しないよう注意すること。

また、このような脆弱性はファジングを使用して発見されることが多い[4]

ヒープベースのエクスプロイト

ヒープ領域で発生するバッファオーバーフローはヒープオーバーフローと呼ばれ、エクスプロイトもスタックベースのオーバーフローとは異なる。ヒープ領域上のメモリはアプリケーションの実行時に動的に確保され、一般的にはプログラムのデータが格納される。エクスプロイトは、何らかの方法でこのデータを破壊し、アプリケーションが内部構造(例えば連結リストのポインタ)を上書きするように仕向けることで行われる。基本的なヒープオーバーフローのテクニックでは、動的メモリ確保で使われる連結リストの連結部分(mallocのメタデータなど)を上書きし、その結果のポインタを使ってプログラムの関数ポインタを上書きする。

マイクロソフトGDI+におけるJPEG処理の脆弱性は、ヒープオーバーフローの危険性を示す例といえる[5]

エクスプロイトに対する障壁

バッファの読み込みや実行の前に行われるバッファの操作が原因で、エクスプロイトが失敗する場合もある。このような操作を利用してエクスプロイトの脅威を軽減することはできるが、それでもエクスプロイトが不可能にはならない。バッファに対する操作としては大文字小文字変換、メタ文字の除去、非英数字のフィルタリングなどがあるが、これらの処理をくぐり抜けるテクニックも存在する(英数字コード(w:alphanumeric code)、ポリモルフィックコード自己書き換えコードReturn-to-libc攻撃など)。また、侵入検知システムをすり抜けるのにも同様の方法が使用できる。また場合によっては、コードがUnicodeでエンコードされている場合など、実際にはリモートから任意のコードの実行が可能であるにも関わらず、発見者によってただのDoSであると不正確に伝えられているような脆弱性もある[6]

エクスプロイトの実際

実世界においてエクスプロイトを確実に実行させるためには、克服しなければならない様々な課題が存在する。例えば、アドレス中のNULLバイト、シェルコードの位置のばらつき、環境ごとの差異、各種の攻撃対策などが挙げられる。

NOPスライド

テンプレート:Main

ファイル:Nopsled.svg
スタック中のNOPスライドのペイロード

NOPスライドは、スタックバッファオーバーフローを利用するテクニックとして最古のものであり、また最も広く知られているものでもある[7]

このテクニックでは、攻撃のためにはバッファの正確なアドレスを知る必要があるという問題に対し、攻撃のターゲットとなる領域を拡大させることで問題を解決する。このためには、スタック中のかなり広い区域がNOP命令で破壊されている必要がある。攻撃者が入力したデータの末尾(NOP命令の後)には、シェルコードが位置するバッファの先頭への相対ジャンプ命令が格納されている。ここで、NOP命令の集まりはNOPスレッドと呼ばれるが、これは戻りアドレスをNOP領域のどのアドレスで上書きしたとしても、NOP命令の上を「スライド」していき、末尾のジャンプ命令により実際の悪意あるコードへ転送が行われるためである。 このテクニックを利用すれば、攻撃者は比較的小さなシェルコードの場所を推測する代わりに、スタック上のどこにNOPスレッドがあるかだけを推測するだけでよい[8]

このテクニックは頻繁に使用されるため、このパターンのNOP命令は多くの侵入防止システムベンダーでシェルコードの判定に使用されている。ただし、NOPスライディングを通常のNOP命令だけで構成する必要はないという点には注意すべきである。シェルコードの実行と直接関係ない箇所では、マシンの状態を変更しなければどのような命令でもNOP命令の代わりに使用できる。このため、エクスプロイトの作成者側では、シェルコードの実行に影響を及ぼさない任意の命令をランダムに選んでNOPスレッドを構成することが常套手段となっている[9]

この手法は攻撃が成功する確率を劇的に増加させるが、問題がないわけではない。このテクニックではスタック中でNOPスレッドの領域を指すオフセットを推測することになるため、このテクニックを使用したエクスプロイトはある程度運に依存することになる[10]。この推測を誤ると、通常の場合ターゲットとなるプログラムはクラッシュし、システムアドミニストレータに攻撃の動きを警戒されることになってしまう。またそれとは別に、NOPスレッドが非常に多くのメモリを必要とするという問題もある。これはNOPスレッドが役に立つためにはそのサイズが十分に大きい必要があるためだが、これはNOPスライドで影響を受けるバッファの割り当てサイズが小さく、かつ現在のスタックの深さが浅い場合(すなわち、現在のスタックフレームの末尾からスタックの先頭までの間に十分なスペースがないとき)に問題となる。こういった問題にもかかわらずNOPスライドがいまだに重要なテクニックであり続けているのは、所与のプラットフォーム、環境、状況によってはNOPスライドが唯一利用可能な攻撃方法となる場合がしばしばあるためである。

レジスタに格納されているアドレスへジャンプする方法

jump to registerとは、NOPスレッドの格納領域もスタックのオフセットの推測も必要とせずに、スタックバッファオーバーフローを用いた確実なエクスプロイトを可能にするテクニックである。戦略としては、リターンポインタを上書きして、レジスタへ格納されている既知のポインタへのジャンプを起こさせる(このポインタはコントロールされたバッファ、ひいてはシェルコードを指している)。例えばレジスタAがバッファの先頭へのポインタを格納しているとすると、そのときレジスタAをオペランドにとる任意のjumpまたはcall命令が実行フローの支配権を得るのに使用できる[11]

ファイル:JumpToEsp.png
ntdll.dll中のDbgPrint()ルーチンを呼び出す命令にはi386のオペコードjmp espが含まれている

実際には、特定のレジスタへのジャンプ命令を意図的に含めないようにしたプログラムもある。よくある解決法としては、プログラムのメモリ中の固定の位置にあるオペコードの中に、使える命令のインスタンスが意図せず作られているのを探してくる方法がある。左図はそのような意図しないインスタンスの例で、i386jmp esp命令が作られている。この命令のオペコードはFF E4である[12]

この2バイトの並びは、call DbgPrint命令の先頭から1バイトオフセットした位置(アドレス0x7C941EED)にある。攻撃者がプログラムのリターンアドレスをこのアドレスで上書きしたら、プログラムはまず最初に0x7C941EEDへジャンプし、オペコードFF E4jmp espと解釈する。そしてスタックの先頭へジャンプし、攻撃者の指定したコードを実行する[13]

このテクニックが利用可能な場合、脆弱性の重大性は相当に高くなる。これは、処理を自動化してもほぼ確実に攻撃が成功するほどエクスプロイトの成功率が高くなるためである。そのため、これはスタックバッファオーバーフローの脆弱性を利用するインターネットワームにおいて最もよく使われるテクニックとなっている[14]

また、この方法を使えば、Windowsプラットフォームにおいては上書きしたリターンアドレスの後ろにシェルコードを配置することもできる。実行ファイルは多くの場合アドレス0x00400000から配置され、またx86はリトルエンディアンアーキテクチャのため、リターンアドレスの末尾のバイトは必ずNULLになる。そのため、バッファへのコピーはそこで終了されてしまい、それ以降には何も書き込まれない。これにより、シェルコードのサイズはバッファのサイズに制限されることになるが、これは場合によっては非常に厳しい制限となる。一方DLLはハイメモリ(アドレス0x01000000より上)に配置されるため、アドレスにNULLバイトが含まれないようにできる。そのため、この方法であれば上書きするリターンアドレスがNULLバイト(あるいはその他の禁止された文字)を含まないようにできる。このようにDLLを使った方法はDLLトランポリン(DLL Trampolining)などとも呼ばれる。

防御的対策

バッファオーバーフローの検出や防止には様々なテクニックが用いられ、それぞれにトレードオフがあるが、いまだに決め手となる技術は存在していない。バッファオーバーフローを防ぐ最も信頼性の高い方法は言語レベルの自動的な保護であるが、この種の保護はレガシーコードには適用できない。また、技術的・業務的・文化的制約によって、自動的な保護機能を備えていない言語を使わなければならない場合もある。以降では、利用可能な対策方法やその実装について述べる。

プログラミング言語の選択

プログラミング言語の選択はバッファオーバーフローの発生の有無に大きく影響する。2008年時点ではC言語またはC++言語で実装されているソフトウェアが数多く存在するが、これらの言語ではメモリ中のいかなる場所に対する読み書きに関しても組み込みの保護機構が提供されていない。より具体的に言えば、バッファに書きこまれたデータが、そのバッファの境界に収まっているかのチェックは行われない。ただし、標準C++ライブラリは安全にバッファリングを行うための各種の機構を提供しているし、バッファオーバーフローを避けるためのテクニックはC言語にも存在する。

その他多くのプログラミング言語では、実行時に、また場合によってはコンパイル時にチェックを行い、CやC++ではプログラムがクラッシュするような場合でも、警告を送ったり例外を上げたりできるようになっている。このような言語の例としては、AdaEiffelLISPModula-2SmalltalkOCaml、およびC言語から派生したCycloneD言語が挙げられる。また、Javaプラットフォーム.NET Frameworkでは全ての配列に対して境界チェックが必須とされる。ほぼすべてのインタプリタ言語ではバッファオーバーフローへの対策が行われており、エラー発生時にはその状態が明確に伝えられる。境界チェックを行うのに十分な型情報を保持しているようなプログラミング言語では、境界チェックの有効・無効を切り替えるためのオプションが提供されていることもある[15]。 ただし、インタプリタそのものは多くがコンパイラ言語によって書かれているので、絶対に信頼できるわけではない。また言語に付属するライブラリやモジュールには母体となった言語のライブラリやシステムコールに由来した脆弱性が組み込まれてしまうこともある。

また、静的コード解析を行えば多くの動的な境界チェック・型チェックを取り除くことができる。これについてもすべてのエラーを発見できるわけではなく、解析処理の実装が貧弱だったり、解析対象のコードに厄介なケースが含まれている場合には、静的解析の効果は低下する。ソフトウェア技術者は、使用する言語やコンパイラの設定の決定に際しては安全性と性能のトレードオフについて慎重に検討すべきであると言える。

安全なライブラリの利用

バッファオーバーフローがC言語およびC++言語に多い原因として、これらの言語では、データ型のコンテナとしてのバッファの低レベルな実装の詳細が公開されてしまっていることが挙げられる。従って、バッファ管理を行うコード中で高いレベルの正当性を維持することができれば、バッファオーバーフローは回避できるはずだと言える。例えば、標準ライブラリ関数のうち、getsstrcpyといった境界チェックを行わない関数は使用を避けることが昔から推奨されている。 例えば、Morris wormfingerdgetsを使用しているのを突いて攻撃を行った[16]

よく書けたテスト済みの抽象データ型ライブラリにより、境界チェックを含むバッファ管理を集中化・自動化することで、 バッファオーバーフローの発生率や影響度合いを減らすこともできる。プログラミング言語の構成要素となるデータ型のうち、バッファオーバーフローが多発するのは文字列と配列である。したがって、これらのデータ型においてバッファオーバーフローを防止することで、保護が必要となる部分の大半をカバーすることができる。だが、このような安全性を提供するライブラリの誤った使用法によりバッファオーバーフローや脆弱性を引き起こす可能性は残っている。またもちろん、これらライブラリ自身のバグも潜在的な脆弱性となる。「安全な」ライブラリの実装としては、"The Better String Library"[17]、 Vstr[18]、 Erwin[19]などが挙げられる。 OpenBSD標準Cライブラリでは、strlcpystrlcatといった関数が提供されているが、これらの関数の提供する安全性は上記のような実装と比べると限定されている。

2007年にはC標準委員会によるTechnical Report 24731が公開された。ここでは、C標準ライブラリの文字列関数及び入出力関数に、バッファのサイズを引数として追加したものを定義しているテンプレート:Cite webテンプレート:Cite web。 しかしながら、これらの関数がバッファオーバーフローの低減に寄与するかは疑わしい。これらの関数では関数呼び出しの度にプログラマによる判断が必要となるが、これでは標準ライブラリ中に昔からある類似の関数を呼び出す際にその都度プログラマが判断を行うのと大して変わらない[20]

また、配列領域をチェックする機能自体は16bit時代からx86 CPUのBOUND命令などすでに実装されていたが、チェックの結果問題があった場合には例外を発生させる等扱いが難しく、現在のCPUにおいてもチェックにまつわるオーバーヘッドが無視できないという理由から、リリースバイナリにこれらの機能を含められないということもある。

バッファオーバーフロー保護

バッファオーバーフロー保護は最も多いタイプのバッファオーバーフローを検出するのに使用される。方法としては、関数から戻る際にコールスタックが変更されていないかチェックし、変更されていた場合はプログラムがセグメンテーション違反で終了する。このような処理系としては、Libsafe[21]や、 StackGuard[22] およびProPolice[23]などのGNUコンパイラコレクションへのパッチが挙げられる。

マイクロソフトのデータ実行防止モードでは、SEH例外ハンドラへのポインタが上書きされないように明示的に保護を行う[24]

より強力なスタック保護の方法としては、スタックを2つに分けて、片方をデータ用、もう片方を関数の戻りアドレス用とする方法がある。セキュリティを意図した設計によるものではないが、Forthではこのようなスタックの分割が行われている。ともかく、この方法もバッファオーバーフローに対する完璧な解決策とは成り得ない。これは、戻りアドレスは上書きされないが、プログラムの動作に影響を与えるデータの上書きは依然として発生しうるためである。

ポインタ保護

バッファオーバーフローは、最終的にはポインタやアドレスの操作によって効果を発揮する。PointGuardは攻撃者がポインタ及びアドレスを確実に操作するのを妨害するためのコンパイラの拡張として提案された [25]。 このアプローチでは、ポインタに対してXOR演算によるエンコードを行うコードをコンパイラがポインタの使用前後に自動的に追加する。これにより、攻撃者には(理論的には)ポインタのエンコード・デコードにどのような値を使用しているか分からないため、ポインタを別の値で上書きしてもそれが実際にはどこを指すのか前もって知ることができない。PointGuardが実際にリリースされることはなかったが、Microsoftは似たような機能をWindows XP SP2およびWindows Server 2003 SP1で実装している[26]。 マイクロソフトはポインタの保護機能が自動的に行われるよう実装するのではなく、プログラマが自らの判断で呼び出せるようにAPIルーチンを追加する形で実装を行った。これはパフォーマンスの面では有利(エンコード処理が常に入るのではないため)だが、使う必要のある場所がどこであるかプログラマが知っているため、プログラマには負担となる。

ここで、XORは線形演算であるため、攻撃者がエンコードされたポインタを操作する際にアドレス中の下位バイトのみを書き換える方法が考えられる(上位バイトは上書きされないため正しくデコードされる)。これにより、攻撃者が攻撃を複数回試行できるか、またはポインタがある範囲のうちの一箇所(例えばNOPスレッド中のある一箇所)を指せば攻撃が成功するという条件に当てはまる場合には、攻撃が可能となってしまう[27]。 マイクロソフトでは、このアドレスの部分的な上書きに対する脆弱性への対処として、エンコード処理にランダムなローテーション処理を追加した [28]

実行保護

テンプレート:Main

実行保護とはバッファオーバーフロー保護のアプローチの一つで、スタックやヒープ上のコードの実行を防止するものである。攻撃者がバッファオーバーフローを利用するのは任意のコードをプログラムのメモリ上に挿入するためであるが、実行保護が行われている場合は、そのようなコードを実行しようとしても例外が発生する。

一部のCPUではNXビット("No eXecute"の略)と呼ばれる機能が提供されており、ソフトウェアと連携することで、スタックやヒープの内容を含むページをマーク付けすることで、読み書きは可能だが実行は不可能であるようにし、データ領域の任意のデータをコードとして実行させないようにできる。 インテル製品ではXDビット("eXecute Disabled"の略)という名前でNXビットと同じ機能を実装しており、NetBurstアーキテクチャPentium4ファミリにのIntel 64対応プロセッサ、および現在リリースされているCoreマイクロアーキテクチャの全てのプロセッサが対応している。AMD製品ではAMD64 Enhanced Virus Protectionという名前で、Opteron、Athlon 64、Turion 64、Sempronの一部製品がこれに対応している

一部のUNIX(OpenBSDMac OS Xなど)は実行保護(W^Xなど)が有効になった状態で出荷されている。それ以外のオプショナルなパッケージとしては以下のものが挙げられる。

また、Windowsでも最近のものはデータ実行防止(DEP)と呼ばれる実行保護機能を備えている[32]。 また、プロプライエタリなアドオンとしては以下のものがある。

実行保護は一般に、Return-to-libc攻撃や、その他攻撃者が作成したコードの実行に依存しないタイプの攻撃に対しては効果がない。 また、実行保護を行なっている場合でも、バッファオーバーフローによりプログラムを異常動作させることは可能である。

ハーバードアーキテクチャのハードウエアとオペレーティングシステムの組み合わせを使用することでもこの保護を実現できる。

アドレス空間配置のランダム化

テンプレート:Main

アドレス空間配置のランダム化(address space layout randomization, ASLR)はコンピュータのセキュリティ機能の一種で、攻撃の鍵となるデータの格納エリアの位置(通常は実行コードの開始位置や、ライブラリ、ヒープ、スタックなどの位置を含む)をプロセスのアドレス空間中でランダムに設定するものである。 バッファオーバーフローは本来メモリ中の特定の位置にプログラムが配置されていることを前提としている点に着目した対策である。

関数や変数に対する仮想記憶アドレスのランダム化によりバッファオーバーフローによるエクスプロイトをより困難にすることが可能と見られるが、ただし不可能とはならない。また、これにより攻撃者はエクスプロイトを個別のシステムごとに誂えなけれたならなくなるため、ワームの増殖を防ぐのにも役立つ[35]。 似た方法でより効果が弱いものとしては仮想記憶上で行うプロセスやライブラリの再配置が挙げられる。

ディープパケットインスペクション

テンプレート:Main

ディープパケットインスペクション(deep packet inspection, DPI)を使用すれば、ネットワークの周辺から行われるバッファオーバーフローを狙った攻撃を、攻撃シグネチャとの比較やヒューリスティクスを利用して検知することができる。これらの方法は既知の攻撃のシグネチャを持つパケットをブロックすることができる他に、長いNOP命令の列(NOPスレッド)が見つかった場合、エクスプロイトのペイロードが微妙に変化しても使用することができる。

パケットスキャンは決して効果的な方法ではない。これは、既知の攻撃だけしか防ぐことができず、またNOPスレッドは様々な方法でエンコードできるためである。攻撃者の使うシェルコードは、ヒューリスティックなパケットスキャナや侵入検知システムを回避するため、英数字コードメタモーフィックコード自己書き換えコードなどを使って構成される。

歴史

バッファオーバーフローがある程度公に文書化されたのは1972年の初めで、Computer Security Technology Planning Studyで以下のように説明されている[36]

"The code performing this function does not check the source and destination addresses properly, permitting portions of the monitor to be overlaid by the user. This can be used to inject code into the monitor that will permit the user to seize control of the machine."
「この処理を実行するコードは読み込み元と書き込み先のアドレスに対するチェックを適切に行なっておらず、モニターの一部に対しユーザによる上書きを許すことになっている。これはモニタにコードを挿入するのに利用される可能性があり、結果としてユーザがマシンの制御を掌握する可能性がある」

モニターとは、現在カーネルと呼ばれているのと同じものである。

バッファオーバーフローを利用した悪意のあるエクスプロイトで最初に文書化されたのは、1988年に書かれたMorris wormがインターネット上で増殖するのに利用していたエクスプロイトのうちの一つである。攻撃対象のプログラムはUNIXサービスであるfingerであった[37]。 1995年、Thomas Lopaticはそれとは独立にバッファオーバーフローを発見し、セキュリティに関するメーリングリストBugtraqへ投稿した[38]。 1996年、エリアス・レヴィ(ハンドルネームAleph one)はオンラインマガジンPhrackで記事"Smashing the Stack for Fun and Profit"を発表した[39]。 これはスタックベースのバッファオーバーフローを使用したエクスプロイトを手順を追って説明していく内容である。

これ以降、少なくとも2つの有名なインターネットワームがバッファオーバーフローを利用したエクスプロイトで多くのシステムに被害を与えている。2001年にはCode RedがマイクロソフトのInternet Information Services(IIS) 5.0のバッファオーバーフローを利用している[40]。 また2003年にはSQL SlammerMicrosoft SQL Server 2000の動作するマシンに被害を与えている[41]

2003年には、市販のXboxのゲームに含まれるバッファオーバーフローが利用され、無認可のソフトウェア(例えばHomebrewのゲームなど)をModチップなどのハードウェアの改造なしに動作させるのに利用された[42]プレイステーション2では同じ目的のためにPS2 Independence Exploitが使用される。またWiiではHomebrewが利用されるが、これはゼルダの伝説 トワイライトプリンセスに存在するバッファオーバーフローを利用している。

脚注

  1. テンプレート:Cite web
  2. テンプレート:Cite web
  3. テンプレート:Cite web
  4. テンプレート:Cite web
  5. テンプレート:Cite web
  6. テンプレート:Cite web
  7. テンプレート:Cite journal
  8. テンプレート:Cite journal
  9. テンプレート:Cite conference
  10. テンプレート:Cite journal
  11. テンプレート:Cite conference
  12. テンプレート:Cite book
  13. テンプレート:Cite journal
  14. テンプレート:Cite conference
  15. テンプレート:Cite web 例えばDelphiでは$RangeChecksディレクティブで境界チェックの有効・無効を切り替えられる。
  16. http://wiretap.area.com/Gopher/Library/Techdoc/Virus/inetvir.823
  17. テンプレート:Cite web
  18. テンプレート:Cite web
  19. テンプレート:Cite web
  20. テンプレート:Cite web
  21. テンプレート:Cite web
  22. テンプレート:Cite web
  23. テンプレート:Cite web
  24. テンプレート:Cite web
  25. テンプレート:Cite web
  26. テンプレート:Cite web
  27. テンプレート:Cite web
  28. テンプレート:Cite web
  29. テンプレート:Cite web
  30. テンプレート:Cite web
  31. テンプレート:Cite web
  32. テンプレート:Cite web
  33. テンプレート:Cite web
  34. テンプレート:Cite web
  35. テンプレート:Cite web
  36. テンプレート:Rpテンプレート:Cite web
  37. テンプレート:Cite web
  38. テンプレート:Cite web
  39. テンプレート:Cite web
  40. テンプレート:Cite web
  41. テンプレート:Cite web
  42. テンプレート:Cite web

関連項目

外部リンク