バッファオーバーフローへの対策技術入門



はじめに


社会の情報化が進み私たちの生活が便利になる一方,サイバー攻撃による被害も多く報告されています.

サイバー攻撃は,ソフトウェアの脆弱性を悪用した攻撃が原因となって引き起こされることがあります.脆弱性の中でもバッファオーバーフロー脆弱性は,1980年代から存在する代表的な脆弱性の一つです.このサイトでは,バッファオーバーフロー脆弱性とその脆弱性を悪用する攻撃の概要を説明し,その後,対策技術の一部をご紹介します.


対象とする読者

このサイトは,情報セキュリティの初学者を対象としています.目安としては,情報系学部もしくは学科で学ぶ学部2年生,もしくは,CやC++でのプログラミングの経験がある方を想定しています.



(ソフトウェアの)脆弱性とは


ソフトウェアの脆弱性の定義には様々なものがありますが,ここでは,以下に示す,CVE(Common Vulnerabilities and Exposures)での定義を参考にして,「OSやアプリケーションなどのソフトウェアにおけるセキュリティ上の欠陥」とします.

A "vulnerability" is a weakness in the computational logic (e.g., code) found in software and some hardware components (e.g., firmware) that, when exploited, results in a negative impact to confidentiality, integrity, OR availability.

抄訳:「脆弱性」とはソフトウェア内の計算ロジック(例えば,コード)やハードウェアコンポーネント(例えば,ファームウェア)の弱点であり,悪用されることで機密性,完全性,可用性に悪影響をもたらす.

(CVEより引用)

攻撃者によって脆弱性を悪用されると,プログラムのクラッシュや,管理者権限の奪取といった深刻な被害が発生する可能性があります.プログラムに脆弱性が作りこまれる主な要因として設計の段階でのミス,コーディングでの不具合などが挙げられます.

以下に,NVD(National Vulnerability Database:米国立標準技術研究所脆弱性データベース)に登録された(バッファオーバーフロー脆弱性を含めた)脆弱性の件数を示します.このグラフから分かるように年々脆弱性の報告件数は増えています.バッファオーバーフロー脆弱性の報告件数は,増加傾向にはありませんが,なくなってはおりません.



バッファオーバーフロー脆弱性の概要


(バッファオーバーフロー脆弱性自体のより詳しい解説は,「マスタリング TCP/IP 情報セキュリティ編 オーム社」などを参照ください.)

バッファオーバーフロー脆弱性は,CやC++などで作成されたプログラムにおいて,メモリ上のバッファ(プログラム実行中に一時的に利用するメモリ領域)の境界を越えて高位にある他のデータの書き換えを許してしまう脆弱性です.

バッファオーバーフロー脆弱性には,スタック領域上でのバッファ操作に起因するものとヒープ領域上でのバッファ操作に起因するものが主にあり,それぞれスタックベースバッファオーバーフロー脆弱性(以後,スタックバッファオーバーフロー脆弱性),ヒープベースバッファオーバーフロー脆弱性(以後,ヒープバッファオーバーフロー脆弱性)と呼ばれています.ここではスタックバッファオーバーフロー脆弱性を例に説明していきます.

プログラムはその実行時,プログラムごとに他のプログラムと独立して,スタックと呼ばれる領域が用意されています.スタック領域では,関数の呼び出しごとに必要なデータをスタックフレームという単位で管理しています.スタックフレームは,高位のアドレスから関数実行後の戻りアドレスとなるリターンアドレスの値,ローカル変数へアクセスする際の基底アドレスとなるebpレジスタの値,関数で使用するデータを一時的に保存する領域であるバッファ,ローカル変数といったデータで構成されます.例えば,プログラムの実行中にmain関数からfuncという関数が呼び出されると,スタック領域は図2のようになります.

スタックバッファオーバーフロー脆弱性のあるプログラムで,その実行時,スタック領域に確保されたバッファに対し,その容量を上回るサイズの(プログラムの外部から)入力を与えると,バッファの高位にあるデータが上書きされてしまうことがあります.

例えば,攻撃者がバッファオーバーフロー脆弱性を悪用して,リターンアドレスを書き換えた場合,実行しているプログラムのクラッシュや,shellを起動されることによって管理者権限を奪われ,コンピュータを自由に操作されてしまう可能性があります.



対策技術について

SSP(Stack Smash Protection)

SSP(Stack Smash Protection)とはコンパイラを用いて,スタックバッファオーバーフロー脆弱性を悪用した攻撃を防ぐ対策技術です.SSPはスタックフレーム内のebpレジスタとローカル変数の間に配置したcanaryと呼ばれる値の書き換えの有無によってスタックバッファオーバーフロー攻撃を検知する対策技術です.プログラムの実行中で,関数の呼び出し時にスタックにcanaryを挿入し,関数からリターンする直前に埋め込んだcanaryの値が書き換えられていないかチェックします.canaryの値が書き換えられていない場合は,通常通り実行が完了しますが,スタックバッファオーバーフロー攻撃によってバッファのサイズ以上の入力があり,canaryの値が書き換えられていた場合は,実行を中止します.図4はSSPのイメージ図です.実行を中止することによって同じスタックフレーム内のリターンアドレスや変数,ポインタなどが改竄されてしまうことを防ぎます.ただし,canaryの値を正しく上書きすると攻撃が成功してしまいます.

SSPは,コンパイラを用いた対策技術で,コンパイル時にcanaryを挿入する命令とcanaryの値をチェックする命令が,オリジナルのプログラムの関数の前後に挿入されます.


データ実行防止機能

データ実行防止機能とは,CPUの機能を用いた,スタックバッファオーバーフロー脆弱性を悪用した攻撃を防ぐ対策技術です.データ実行防止機能では,プログラムの実行時に用意される仮想メモリ空間を「(プログラムが配置される)テキスト領域」と「(データのみ配置される)データ領域」に分離します.プログラムの実行時に,データ領域にあるコードを実行しようとすると,CPUがプログラムを停止させます.図5はデータ実行防止機能のイメージ図です.

データ実行防止機能はバッファオーバーフロー脆弱性を悪用してデータ領域に送り込んだ不正なコードを実行する攻撃に対して有効です.万が一リターンアドレスを書き換えられてしまったとしても,データ領域から不正なコードの実行を阻止することができます.

データ実行防止機能の実現には,CPUの機能が必要で,Intel CPUではNXbitろ呼ばれます.NXbitなどを用いたOSでのデータ実行防止機能を,WindowsOSではDEP,LinuxではPaXやExec Shield,OpenBSDではW^Xと呼ばれます.※一部,CPUの機能を用いていないものもあります.


アドレス空間のランダム化(ASLR:Address Space Layer Randomization)

ASLRは,プログラムの実行開始時に決定されるデータ領域やスタック領域,ヒープ領域などのアドレスを実行ごとにランダムにすることで攻撃者によるアドレスを指定した攻撃を防ぐ対策技術です.図6はASLRのイメージ図です.データ領域やスタック領域,ヒープ領域などのアドレスが固定されている場合,攻撃者から重要なデータのアドレスを特定されやすくなります.これらの領域のアドレスをランダムにすることで,特定のアドレスを狙った書き換えることが困難になります.ASLRはOSの機能として実装されていて,Windows,Linux,iOSやAndroidなどに実装されています.

前述の領域に加えて,対象プログラムのテキスト領域のアドレスもランダム化することができます.それを実現するのが,PIE(Position-Independent Executable:位置独立実行形式)と呼ばれる仕組みです.


Automatic Fortification

Automatic Fortificationは,GCCのバージョン4.0から導入されているセキュリティ機構です.バッファオーバーフローを引き起こす要因の一つとして,プログラム中でstrcpy関数のように書き込み先のバッファのサイズを確認せずに処理をおこなってしまうライブラリ関数を利用していることが挙げられます.Automatic Fortificationは脆弱性の原因となるような関数をコンパイル時に,別の安全性の高い同じ機能を持つ関数へと置換します.


まとめ

ここではバッファオーバーフロー脆弱性に対する対策技術を4つ紹介しました.しかし,上記の対策をすべて適用したとしても,バッファオーバーフロー脆弱性を悪用する攻撃を防げるとは限りません.これらの対策技術を回避する攻撃は存在します.バッファオーバーフローを含む脆弱性を悪用した攻撃のリスクは,当面存在し続けることを認識しておくことが重要です.