ITの教科書検索

バッファオーバーフローとは、領域を超えてデータを書き込む不具合

OSやブラウザの更新情報には、「バッファオーバーフローの脆弱性を修正」という一文がよく出てきます。バッファオーバーフローは、データを置く領域に入りきらない量を書き込み、隣にある別のデータを上書きする不具合です。攻撃に悪用されるので、見つかれば修正の対象になります。

データを置く領域をバッファと呼びます。被害は、上書きされる場所によって変わります。プログラムが異常終了するだけで済む場合もあります。一方、攻撃者にプログラムの制御を奪われ、思いどおりに動かされる場合もあります。

アメリカの国立標準技術研究所(NIST)の説明も同じです。システムをクラッシュさせることも、コードを挿入して制御を奪うこともできるとしています。

入力の長さを変えて、メモリの様子を見る

次の図は、関数が使う8バイトのバッファに入力を書き込む様子を示します。入力の長さと対策を切り替えると、どこまで上書きされるかを比べられます。

入力
対策
上が、アドレスの小さい側です。書き込みは、上から下へ進みます。
バッファ8バイト書き込まれた
退避領域(呼び出し元の情報)4バイト上書きされた
戻り先アドレス4バイト上書きされた
呼び出し元のデータ4バイト無傷
下が、アドレスの大きい側です。
16バイトの入力:戻り先アドレスまで届く
あふれた8バイトが、退避領域と戻り先アドレスを上書きします。関数が終わると、プログラムは戻り先アドレスが指す場所へ進みます。
戻り先アドレスが、入力の値に書き換わった

入力が8バイト以内なら、何も起きません。8バイトを超えた分は、バッファの外へ書き込まれます。メモリの並びは説明用に単純にしたもので、実際の並びはコンパイラや環境で変わります。

仕組み:スタックの上で、戻り先アドレスが隣にある

関数が呼び出されると、ローカル変数の領域が、スタックというメモリ領域に確保されます。ローカル変数とは、その関数の中だけで使う変数です。同じスタックには、関数が終わったあとに戻る場所を記録した値も置かれます。

この値を、戻り先アドレス、またはリターンアドレスと呼びます。情報処理推進機構(IPA)の説明でも、スタックにはローカル変数と戻り先アドレスの両方が置かれます。そのため、ローカル変数の領域があふれると、戻り先アドレスが容易に書き換わります。

書き込みは、アドレスの小さい側から大きい側へ進みます。戻り先アドレスは、バッファよりアドレスの大きい側にあります。そのため、あふれた分は、戻り先アドレスのある側へ広がります。前の図の最初の表示(対策なし・16バイト)が、この状態です。

関数が終わると、プログラムは戻り先アドレスが指す場所へ進みます。書き換えられた値が使われていない場所を指していれば、プログラムは異常終了します。入力した側が選んだ場所を指していれば、その場所の命令が実行されます。

IPAの説明では、攻撃者は、CPUが直接実行する機械語のバイト列を入力データとして送り込みます。同時に領域をあふれさせて、戻り先アドレスを書き換えます。標的になる領域には、スタックのほかにヒープと静的領域があります。スタックへの攻撃は最も実行が容易とされてきたので、ここではスタックを例にします。

あふれると何が起きるか

CWE(Common Weakness Enumeration)は、ソフトウェアの弱点を分類した一覧です。CWEは、バッファオーバーフローの結果を2つに分けています。1つはサービスの停止で、あふれによってプログラムがクラッシュしたり、無限ループに入ったりします。もう1つは不正なコードの実行で、制御を奪われます。

制御を奪われると、機密性・完全性・可用性のすべてが損なわれる恐れがあります。3つの性質は、情報セキュリティの3要素の記事で説明しています。戻り先アドレスまで届かない場合でも、近くの変数が書き換わって、プログラムの状態が変わることがあります。

対策:攻撃が成功する4つの段階を止める

IPAは、この攻撃が成功する条件を4つ挙げています。4つがすべて成り立ったときだけ攻撃が成功するので、1つでも止めれば、攻撃は失敗します。

コードが入力に混ざる機械語を送り込む入力の検査形式・内容を調べるコピーする戻り先が書き換わるバッファがあふれる長さの検査安全な関数・言語関数が終わる制御が移る関数が終わるときカナリア・ASLR検出・配置を変える命令として読むデータが実行される送り込まれた内容DEP(NX)実行を禁止する
攻撃は、左の4段階がすべて成り立ったときに成功します。右の対策は、同じ高さの段階を1つずつ止めます。どれか1つの段階が止まれば、攻撃は失敗します。

ただし、IPAは、どの手段も100%完璧ではないと注意しています。そのため、1つの対策に頼らず、複数を重ねます。CWEも、ASLRやDEPのような実行環境の対策を、対策を重ねる多層防御の1つとしています。

開発者の対策:あふれさせない書き方にする

最も基本の対策は、書き込む前に、入力の長さがバッファに収まるかを調べることです。IPAは、領域の長さとデータの長さを常に意識してコードを書くよう求めています。CWEも、入力の長さ・型・範囲・形式を調べる検査を勧めています。

コピーに使う関数も、長さの上限を指定できるものを選びます。IPAは、長さを調べずにコピーする strcpy の使い方を、あふれが起こり得る例として挙げています。gets という関数は、あふれの原因としてよく知られています。この関数は、C言語の2011年の仕様から削除されました。

同じ2011年の仕様には、出力先の大きさを引数に取る gets_s や memcpy_s のような関数も定められています。CWEは、安全な文字列関数を備えたライブラリを使うことも、被害を抑える緩和策に挙げています。

言語の選び方も対策になります。CWEは、メモリを言語側が管理するJavaやPerlのような言語を、緩和策の1つに挙げています。ただし、CPUが直接実行する外部のコードを呼び出す部分には、あふれが残る場合があります。アメリカのCISAとNSAは、2025年6月に、メモリを安全に扱える言語への移行を進める手引きを公開しています。

コードを書いたあとは、ソースコード検査ツールや、あふれを検出するデバッグで問題を見つけます。完成したシステムを診断する方法は、脆弱性診断とペネトレーションテストの記事にあります。

実行環境の対策:カナリア・ASLR・DEP

プログラムにあふれが残っていても、実行環境の機能で攻撃を失敗させる対策があります。IPAが主なものとして挙げるのは、スタック保護・メモリ空間レイアウトのランダマイズ・データ実行防止です。CWEの現在の版も、同じ3つを緩和策に挙げています。

対策働きIPAの分類
スタックカナリア戻り先アドレスの手前に値を置く。関数が終わる直前に調べ、変わっていれば止めるあふれの検知
ASLRスタックやヒープなどの置き場所を、実行のたびにランダムに変える配置のランダマイズ
DEP(NXビット)データを置く領域の内容を、命令として実行させないデータ実行防止

スタックカナリアは、戻り先アドレスの手前に置いた値です。あふれると、この値が先に書き換わります。関数が終わる直前に値を調べ、変わっていれば、戻り先アドレスを使う前にプログラムを止めます。GCCの -fstack-protector と、Visual C++の /GS が、この仕組みを備えています。

攻撃者が、置かれた値をあらかじめ知っていれば、同じ値で上書きして検査をすり抜けられます。そのため、実際のコンパイラやOSは、値を予測されにくくする工夫をしています。CWEによると、カナリアが検出できるのは一部のあふれだけです。結果も、多くの場合はプログラムの停止です。

ASLRは、スタックやヒープなどの置き場所を、実行のたびにランダムに変えます。攻撃は、書き換えたい場所や、制御を移したい場所のアドレスを想定して組み立てます。置き場所が変われば、その想定が外れます。CWEは、情報漏えいの脆弱性があると、ASLRを回避される場合があると注意しています。

DEPは、データを置く領域の内容を、命令として実行させません。この対策に使うCPUの機能を、AMDではNXビット、IntelではXDビットと呼びます。送り込まれたデータに制御が移っても、実行されずに止まります。CWEによると、DEPは近くの変数の上書きまでは防げません。

IPAの講座は2007年版で、挙げている実装はその時点のものです。仕組みの説明は、CWEの現在の版の緩和策と一致しています。

利用者の対策:修正プログラムを適用する

あふれの原因はプログラムの作りにあるので、利用者は自分で直せません。作り手が修正プログラムを公開したら、適用します。組織で修正プログラムを当てる流れは、脆弱性の記事を読むと分かります。

IPAは、パソコンやスマートフォンのOSと各種ソフトウェアを最新の状態にするよう呼びかけています。そのために、修正プログラムを適用します。そのほかの不正アクセスの手口と対策は、不正アクセスの記事にまとめています。

SQLインジェクションとの違い

SQLインジェクションとXSSは、入力が命令の一部として読まれる攻撃です。バッファオーバーフローは、入力が領域を超えて、隣にある別のデータを書き換える攻撃です。

SQLインジェクションやXSSの原因は、入力をSQL文やHTMLと区別せずに混ぜることです。バッファオーバーフローの原因は、入力の長さを確かめずにコピーすることです。そのため、SQLインジェクションなどの対策であるプレースホルダやエスケープは効きません。長さの検査や、あふれを起こさない書き方が対策になります。

練習問題(全7問)

Q1バッファオーバーフローの説明として正しいものはどれ?
Q2スタック上のバッファがあふれたとき、攻撃者にとって特に重要な上書き先はどれ?
Q3バッファオーバーフローへの、ソースコードでの基本的な対策はどれ?
Q4関数が終わる直前に、戻り先アドレスの手前に置いた値が書き換わっていないかを調べる対策はどれ?
Q5ASLRの説明として正しいものはどれ?
Q6DEP(NXビット)の説明として正しいものはどれ?
Q7バッファオーバーフロー対策について、正しい説明はどれ?

選んでから「採点する」を押すと、解説つきで答え合わせができます。

参考にした資料

関連記事

おすすめの本 PR

セキュリティとデータベースは、基本情報技術者試験の対策本でまとめて学べます。

目的別の本の選び方を見る

セキュリティの記事を続けて読む

セキュリティ 全37本中11本目