バッファとは、データを一時的に保持する物理メモリの領域です。通常、RAM上に存在します。コンピュータは処理速度向上のためにバッファを利用しており、多くの最新ハードドライブやオンラインアプリもこの仕組みを採用しています。例えば、オンライン動画配信では、動画プレイヤーが先に動画の20%をダウンロードしバッファに保存するため、接続速度が少し低下しても配信に影響が出ません。
しかし、バッファには限られた量のデータが入っており、複数のアプリがこの仕組みを利用するため、短時間しかデータを保持できません。その結果、さらにデータが流し込まれると、バッファオーバーフローと呼ばれる状態になります。これは、アプリがバッファの容量を超えてデータを書き込むことで、隣接するメモリ領域を上書きしてしまう不具合です。つまり、十分な空きがない保管場所に情報が過剰に伝えられ、周囲のデータと入れ替わってしまいます。
バッファオーバーフローは、あらゆる種類のソフトに影響を及ぼします。通常、予期しない入力や、バッファに十分な領域を確保しなかったことが原因です。不正なデータが実行可能なコードに渡ると、プログラムは誤作動を起こし、誤った結果、メモリアクセスエラー、クラッシュなどを引き起こす可能性があります。
例えば、ログインデータ用のバッファが8バイトのユーザー名とパスワードを要求するよう設定されている場合、10バイト(予想より2バイト多い)の入力があると、バッファの上限を超えて余分なデータが書き込まれる可能性があります。
悪意のある者は、カスタム入力を慎重にプログラムに投入し、不十分な大きさのバッファにデータを格納させ、隣接するメモリ部分を上書きさせることがあります。決まったメモリバッファ内で、ハッカーは実行可能なコードが入る領域を狙って上書きする場合もあります。これにより、攻撃者はその部分を自分の実行可能なコードに置き換え、プログラムの動作を大きく変える可能性があります。
たとえば、書き換えられたメモリ部分にポインタが含まれている場合、攻撃者のコードがそのポインタをペイロードへ向けるよう変更することができます。これにより、アプリ全体の制御が攻撃者のコードに移る恐れがあります。
バッファオーバーフローは、静的・動的なWeb構造を提供するサーバサービスやアプリ自体で発生することがあります。サーバ製品での事例が多いことから、これらの製品利用者はリスクが高いとされています。また、画像生成などでアーカイブを利用するWebアプリでは、バッファオーバーフローの可能性が一層高まります。
個々のWebアプリのコード内でもバッファオーバーフローは起こり得ます。特定のアプリでは、悪意ある者がエラーを捕捉し悪用するケースは少ないですが、エラーが検出されれば、ソースコードやエラーメッセージがハッカーに公開されないため、悪用の可能性は大幅に低くなります。
Cのような言語では、効率を優先するあまりメモリアクセスの管理が十分に行われず、バッファの脆弱性が生じやすいです。一方、Python、PHP、Perl、Java、JavaScriptなど、Webアプリ構築に使われる高水準言語では、不要なデータをバッファに書き込むことができないため、その可能性は低いです。
しかし、高水準言語を使用するプログラマもバッファ攻撃には注意を払う必要があります。多くの場合、プログラムはCで書かれたOS上で動作するか、Cベースの実行環境を利用しているため、Cコードが脆弱性の対象となる可能性があります。
最新のOSでは、バッファオーバーフロー攻撃を防ぐための実行時保護が施されています。しかし、JavaやPythonなど、バッファオーバーフローの影響を受けにくい解釈系言語環境(ただしインタープリタを除く)を除けば、ほとんどのWebサーバ、アプリサーバ、アプリ環境はこのリスクにさらされています。
言語によって、バッファオーバーフローへの影響度は異なります。CやC++は、メモリへのアクセスや上書きを防ぐ機能が内蔵されていないため、特に脆弱性が高い言語です。Windows、Mac OSX、Linuxなどでは、これらの言語が使用されることが多いです。一方、Java、PERL、C#などの近代的な言語は、リスクを低減する機能が備わっていますが、完全に防ぐことはできません。
さまざまな戦略で、異なるコード部分を狙ったバッファオーバーフロー攻撃が存在します。以下は、最も知られている攻撃です:
スタックは後入れ先出し(LIFO)のデータ構造です。PUSHで値を挿入し、POPで取り出す操作を行います。スタックに配置されたデータが破損すると、隣接するメモリ領域が上書きされ、他のプログラムが保持しているデータやポインタに影響を与えることがあります。スタックオーバーフローは、実行時のみ存在するスタックメモリを狙った、歴史ある脆弱性です。特にCやC++ではポインタの利用が自由なため、攻撃者はこの脆弱性を利用して、データ操作やポインタ作成により悪意あるコードを実行させる恐れがあります。
ヒープオーバーフローは、メモリがヒープ領域に割り当てられた後、データが検証されずに書き込まれることで発生します。これにより、ヒープヘッダーなどの重要なデータ構造や、動的オブジェクトのポインタといったヒープ内の情報が、仮想関数テーブルを上書きする危険があります。
整数オーバーフローは、整数演算の結果が割り当てられたメモリ領域に収まらなくなる算術演算エラーです。プログラムエラーとは異なり、通常は予期しない結果を生み出します。ほとんどのプログラミング言語では、整数は一定のビット数で表現されます。
例えば、32ビット整数型では、符号なしなら0〜4,294,967,295、符号付きなら−2,147,483,648〜2,147,483,647が確保されています。しかし、4,294,967,295に1を足した場合、結果が型の最大値を超えてしまいます。多くの言語やコンパイラは、エラーを出さずにモジュロ演算や切捨てを行うため、プログラムがクラッシュし、攻撃リスクが高まります。
通常、整数オーバーフローは単に誤作動を引き起こすだけですが、場合によっては、金融計算の操作など深刻な結果を招くこともあります。
Unicode文字列は、世界中のあらゆる言語が問題なく利用できるように設計されています。たとえば、アラビア文字は英語の文字と異なり、従来のASCIIコードでは変換できません。Unicodeでは任意の文字が使用可能であり、ASCII入力が期待される箇所にUnicode文字が入力されると、許容範囲を超えることでバッファオーバーフローを引き起こす恐れがあります。
詳細は第2部をご覧ください。
最新情報を購読