本やWebで調べ物をしているとき「バッファオーバーフロー」という言葉に出会うときがある。あまり聞きなれない言葉だ。
バッファオーバーフローとはメモリ領域のバッファを超えて起こるバグのことを指す。
このページでは、バッファオーバーフローについて簡単にまとめた。初心者向けのざっくり解説なので、初めてこのワードを聞いた方にはちょうどいい内容になっているはずだ。
目次
コンピュータにおける「バッファ」とは?
バッファとは?
そもそも「バッファ」とは何を指すかご存知だろうか? 一般的に「緩衝材」としての役割を指すことが多い。
「スケジュールのバッファは?」と聞かれたら、「納期までの余裕のある日数は?」のような意味合いになる。
コンピュータ的なバッファとは?
コンピュータ用語でも一応にその解釈で問題ないのだが、もっと具体的にいえば、「データの保管領域」の事だ。例えの代表として、プリンタとコンピュータの関係がある。
コンピュータのデータをプリンタで印刷するときに、プリンタの印刷速度とコンピュータの処理速度では圧倒的にコンピュータの方が速い。そのためコンピュータの処理データを一時的に貯めこみ、保持しておく必要がでてくる。
これが「バッファ」だ。データを保持し順番に印刷していくのである。
また、コンピュータではメモリ上に確保する領域のことをバッファ領域とよばれる。コンピュータ上のメモリで確保されるメモリの中の領域は「スタック」と「ヒープ」領域の2種類ある。
スタックは一時領域、ヒープ領域は動的に領域が確保されたり、プログラム終了まで領域は保たれる。
バッファオーバーフローのバッファは、このメモリ領域のことを指している。
セキュリティホールとバッファオーバーフロー
オーバーフローになると?
セキュリティホールとバッファオーバーフローの関係は、最終的にメモリ操作に行き着く。
オーバーフローとは「バッファが溢れた状態」だ。メモリ上に確保されたバッファが溢れ、「不定の動作」を引きおこす。隣接したメモリアドレスに不正上書きなどが発生してしまう。
これを使うとユーザの意図しないメモリ操作による、「任意コード実行」ができてしまう。悪意をもったユーザの狙いは、ここだ。
今回はスタックに関してのオーバーフローをざっくりと説明する。
スタックオーバーフロー
セキュリティホールとして利用されやすい、最も古典的なオーバーフローとして、スタックオーバーフローがある。
関数の呼び出しにも方法としてスタックが利用される。スタックされた領域には、データの領域とリターンアドレスが保持され、バッファでオーバーフローが発生すると、リターンアドレスが格納されているアドレスも書き換えられてしまう可能性がある。
バッファオーバーフローによってこのアドレスが書き換えられた場合、関数に戻る際に正常なアドレスに戻れなくなる。悪意あるユーザはリターンアドレスに「悪意のあるコードへのメモリアドレス」をセットすることで 悪意のあるコードや関数を実行させようとする。
これがバッファオーバーフローがセキュリティホールとなる理由だ。
バッファオーバーフローはC言語など、比較的メモリアドレスの操作をユーザに委ねる処理系を使っているとどうしても単純なミスで発生してしまうものだ。
C言語での例
例えば 「char str[10];」 という風に文字列用にバッファを確保し、バッファに文字列を代入したとする。このとき11バイト以上の文字を代入してもC言語の処理系はそのまま代入してしまう。
結果、意図しないメモリの書き込みが発生してしまう。
また関数呼び出しには「コールスタック」と言う物がある、コールスタックには上限が存在し、関数内部であまりに大きすぎる配列などを宣言した場合、無限回の相互呼び出しなどをすると、これもオーバーフローを引き起こす。このときに最悪リターンアドレスなどを書き換えられると「任意コード実行」がおこなわれる可能性がある。
また作成したプログラムを実行したときに「セグメンテーション違反」「Segmentation Fault」となりプログラムが異常終了した場合、十中八九プログラムに致命的なバグが存在することになる。
これはメモリに許可されていない動作をした場合に異常終了したことを示す。よって「Segmentation Fault」は絶対に放置してはいけない。
なにが起こるのか?
攻撃が成功した場合、任意のコードに制御を移すことができれば、そのコードどおりにものごとを実行できる。DoS攻撃をはじめ、不正侵入の糸口となるのだ。
実際のところ、バッファオーバーフローによる脆弱性を攻撃するのはなかなか難しい。
攻撃を成功させるには、
- プログラムにオーバーフローさせるための脆弱性がないかを確認する手段
- 実際にオーバーフローさせた後にリターンアドレスを書き換える知識
- 実際のシェルコード(埋め込みコード)作成など大変高度な知識
- 主にバックトレースやデバッグの知識
が求められる。そう簡単にはできない。
しかし、それをスクリプト化、攻撃用コードなどにして配布している迷惑なクラッカーも世の中にはいるのだ。
その場合、初心者でも簡単にセキュリティホールを悪用できてしまう。セキュリティホールを発見した場合、悪用される前に対策をうたなければならない。
防止策
下記の条件はアウト!
バッファオーバーフローは下記の条件がそろうと悪用されセキュリティホールとして利用される。
- オーバーフローによりリターンアドレスの書き換えが可能
- 内部に機械語コード(シェルコード)を送り込むことが出来る
- リターンアドレスの先へプログラムの制御を移すことが出来る
その対策
入力データのチェックを厳格におこなう
入力データを受け付けるとき基本的にバッファが必要になる。これらを始めバッファのチェックを厳格にすることで、攻撃コードのはいる隙をなくす。
危険な関数はつかわない
古くから存在する関数はセキュアではない可能性が高いgetsやstrcpyなど。
低級言語を使わない
高級言語をつかっていても絶対安全とはいえないが、C言語やアセンブリはメモリの操作をユーザに委ねている、Cは高級言語だが、メモリ操作が原因でオーバーフローになることが多い、よってより高級な言語を使うことで、ある程度、安全性が向上する。
まとめ
バッファオーバーフローに関してざっくりとまとめてみたが、いかがだっただろうか?
少し理解が難しい部分もあったかもしれないが、
- メモリの領域を超えることでバグが起こる
- セキュリティホールになるから注意が必要
- なるべく新しめの言語を使って、リスクを下げよう
というだけ掴んでおけば、まずはいいだろう。