コンテンツにスキップ

英文维基 | 中文维基 | 日文维基 | 草榴社区

「バッファオーバーフロー」の版間の差分

出典: フリー百科事典『ウィキペディア(Wikipedia)』
削除された内容 追加された内容
Labocho (会話 | 投稿記録)
Wakuna99 (会話 | 投稿記録)
編集の要約なし
(3人の利用者による、間の13版が非表示)
1行目: 1行目:
'''バッファオーバーラン'''({{lang-en-short|buffer overrun}})、'''バッファオーバーフロー'''({{lang-en-short|buffer overflow}})とは、[[コンピュータ]]の[[プログラム (コンピュータ)|プログラム]]における、設計者が意図していないメモリ領域の破壊が起こされる[[バグ]]のひとつ、またはそれにより引き起こされた現象をう。
'''バッファオーバーラン'''({{lang-en-short|buffer overrun}})、'''バッファオーバーフロー'''({{lang-en-short|buffer overflow}})とは、[[コンピュータ]]の[[プログラム (コンピュータ)|プログラム]]における[[バグ]]のひとつ、またはそれにより引き起こされた現象で、プログラムで用意された[[バッファ]]よりも大きなサイズのデータバッファに書き込む事で、データがバッファ境界からあふれ、バッファの外側のメモリを上書きしてしま事により、元々そのメモリにあったデータを破壊してしまうものを指す


バッファオーバーランは[[コンピュタセキュリティ]]の深刻な[[セキュティホル]]とりうるため、バッファオーバーランが起こる可能性のあるコンピュプログラムはすぐに修正する必要がある。
バッファオーバーフロ書きされるメモ領域がスタック領域なのかヒプ領域のかに応じてそれぞれ'''スタックベースのバッファオーバーフロー、ヒープベースのバッファオーバーフー'''と呼ばれる。
なお、名称が似ている「[[スタックオーバーフロー]]」とは別の現象であるので注意されたい。
バッファオーバーランは、現在もっとも重大な[[セキュリティホール]]のひとつと考えられている。あるプログラムでバッファオーバーランの[[脆弱性]]が発見されると、一般に高い優先度で修正作業が行われ、更新バージョンのプログラムや修正パッチの公開・配布などが行われる。


[[サイバーセキュリティ]]/[[情報セキュリティ]]の分野ではバッファオーバーフローはメモリ破壊系の[[脆弱性]]の一つとして知られ<ref name=":0">[[#八木、村山、秋山 2015|八木、村山、秋山 2015]] p.59</ref>、攻撃者がバッファオーバーフローのあるプログラムに意図的に悪意のあるデータ(コード)を与えることにより、コンピュータの動作を乗っ取ってしまえる可能性がある。バッファオーバーフローを悪用した攻撃を'''バッファオーバーフロー攻撃'''という<ref>{{Cite web|url=https://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/c901.html|title=第10章 著名な脆弱性対策バッファオーバーフロー: #1 概要|accessdate=2018/12/14|work=セキュアプログラミング講座 C/C++言語編 旧2007年公開版|publisher=情報処理推進機構}}</ref>。
== 概要 ==
バッファオーバーランは、入力[[データ]](多くはデータサイズ)を検査しないプログラムの脆弱性によって、[[バッファ]]領域として設定されているアドレス範囲を超えたメモリが上書きされ、誤動作が引き起こされる。バッファオーバーランが起きた場合、通常は該当プログラム(ないし[[オペレーティングシステム]])の動作が不安定になったり停止したりする。しかし、しばしばそのようなバグを含むプログラムに対して、意図的に悪意のあるデータ(コード)を与えることにより、コンピュータの動作を乗っ取ってしまえる可能性が問題となる。


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

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

プログラムにデータ受信時のチェック機構を持たせることで、攻撃を防止することが可能となる。

{{節スタブ}}

== 具体例 ==
=== 簡単な例 ===
=== 簡単な例 ===
以下の例では、プログラム中の隣接したアドレスに2つのデータ項目が定義されている。一つは 8 バイトの文字列バッファ <code>A</code>、もう一つは 2 バイトの整数 <code>B</code> である。初期状態では、<code>A</code> は 0 で初期化されており可読な文字は入っていない。また、<code>B</code> には整数1979が格納されている。文字のバイト幅は 1 バイトとする。
以下の例では、プログラム中の隣接したアドレスに2つのデータ項目が定義されている。一つは 8 バイトの文字列バッファ <code>A</code>、もう一つは 2 バイトの整数 <code>B</code> である。初期状態では、<code>A</code> は 0 で初期化されており可読な文字は入っていない。また、<code>B</code> には整数1979が格納されている。文字のバイト幅は 1 バイトとする。
90行目: 81行目:
これ以外のC言語の標準文字列処理関数の多くにも同様の問題([[脆弱性]])がある。
これ以外のC言語の標準文字列処理関数の多くにも同様の問題([[脆弱性]])がある。


<br />
==バッファオーバーランが起こす問題==
[[オペレーティングシステム]](OS)によっては、プログラムの[[プログラム (コンピュータ)|コード]]領域と[[データ]]領域を区別せず、コードがデータ領域に書かれていてもそのまま実行してしまう物がある。


== バッファオーバーフロー攻撃 ==
もっとも典型的なバッファオーバーランは、データ領域のうちでも[[コールスタック|スタック]]領域に対するものである。前述のバッファがスタック領域に割り当てられたものである場合(この割当ては[[C言語]]の自動変数で典型的である)、はみ出したデータがスタック領域の当該バッファ割当て部分よりも外の部分を書き換えてしまう。一方、スタック領域には[[レジスタ (コンピュータ)|プログラムカウンタ]]にリストアされるべき[[サブルーチン]]からのリターンアドレスが格納されているが、そのリターンアドレスをバッファーオーバーランしたデータで書き換えてしまうことになる。これを利用した攻撃を[[Return-to-libc攻撃]]と呼ぶ。
情報セキュリティ/サイバーセキュリティにおいて'''バッファオーバーフロー攻撃'''は、バッファオーバーフローの[[脆弱性]]を利用した情報セキュリティ/サイバーセキュリティに対する攻撃である。バッファオーバーフローの脆弱性は[[算術オーバーフロー|整数オーバーフロー]]、[[書式文字列攻撃|書式文字列バグ]]、[[:en:Dangling pointer#Manual_deallocation_without_dangling_reference|Use-After-Free]]などと同様、メモリ破壊系の脆弱性に相当する<ref name=":0" />。


共通脆弱性タイプ[[Security Content Automation Protocol|CWE]]には、
バッファーオーバーラン等の不正動作に対する保護機能がないようなOS上で実行される[[アプリケーションソフトウェア]]では、プログラム作成者ないし利用ユーザの意図の有無に関わらず、常にこの危険性を含んでいる。現在一般消費者向けに販売されているOSの多くは、このような[[記憶装置|メモリ]]保護機能を持たないことが問題の根底にある。
{| class="wikitable"
|+
!番号
!名称
|-
|CWE-120
|入力サイズをチェックしないバッファのコピー(古典的バッファオーバーフロー)<ref name=":1">{{Cite web|url=https://cwe.mitre.org/data/definitions/120.html|title=CWE-120: Buffer Copy without Checking Size of Input ('Classic Buffer Overflow')|accessdate=2018/12/18|publisher=Mitre}}</ref>
|-
|CWE-121
|スタックベースのバッファオーバーフロー<ref name=":2">{{Cite web|url=https://cwe.mitre.org/data/definitions/121.html|title=CWE-121: Stack-based Buffer Overflow|accessdate=2018/12/18|publisher=Mitre}}</ref>
|-
|CWE-122
|ヒープベースのバッファオーバーフロー<ref name=":3">{{Cite web|url=https://cwe.mitre.org/data/definitions/122.html|title=CWE-122: Heap-based Buffer Overflow|accessdate=2018/12/18|publisher=Mitre}}</ref>
|}
などが登録されており、これら3つはいずれも「CWE-119: メモリバッファの境界内における操作の不適切な制限」<ref name=":4">{{Cite web|url=https://cwe.mitre.org/data/definitions/119.html|title=CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer|accessdate=2018/12/18|publisher=Mitre}}</ref>に属している<ref name=":4" /><ref>{{Cite web|url=https://cwe.mitre.org/data/definitions/787.html|title=CWE-787: Out-of-bounds Write|accessdate=2018/12/18|publisher=Mitre}}</ref>。


これら3つのバッファオーバーフロー脆弱性が頻繁に生じるのは[[C言語]]や[[C++]]であり<ref name=":1" /><ref name=":2" /><ref name=":3" />、古典的バッファオーバーフローは[[アセンブリ言語]]でも生じる<ref name=":1" />。
==悪用==
[[クラッカー (コンピュータセキュリティ)|クラッカー]]は、このバッファオーバーランを意図的に起こしてデータの改竄やコンピュータシステムの損壊につながる操作をおこなう(通常は、[[ワーム (コンピュータ)|ワーム]][[コンピュータウイルス|ウイルス]]等の不正プログラムを作成し、それに攻撃を実行させる)。


これら3種類のバッファオーバーフローはセキュリティポリシーの外側で任意のコードを攻撃者に実行可能にする事が頻繁にある<ref name=":1" /><ref name=":2" /><ref name=":3" />。さらに任意のコードの実行により他のセキュリティサービス機構を破壊する事も可能になる<ref name=":1" /><ref name=":2" /><ref name=":3" />。またこれら3種類のバッファオーバーフローはクラッシュの原因にもなるので<ref name=":1" /><ref name=":2" /><ref name=":3" />、意図的にクラッシュさせる攻撃が可能になる<ref name=":1" /><ref name=":2" /><ref name=":3" />。
通常は、[[不正アクセス]]の手段として不正なデータをコンピュータに対して送信すると言うことがあるが、バッファオーバーラン攻撃を行う場合には、送信データに不正なプログラムのコード([[シェルコード]])を挿入し、さらに前述のスタック領域上のリターンアドレス等を、この不正コードが存在するアドレスに書き換える事等により、任意の不正なコードを相手のコンピュータにおいて実行させ、OS上の管理者権限を不正に奪取するなどさまざまな攻撃を行う。


CWEでは「CWE-193: [[Off-by-oneエラー]]」<ref>{{Cite web|url=https://cwe.mitre.org/data/definitions/193.html|title=CWE-193: Off-by-one Error|accessdate=2018/12/18|publisher=Mitre}}</ref>がバッファオーバーフローの原因になると述べられており<ref>{{Cite web|url=https://cwe.mitre.org/data/definitions/131.html|title=CWE-131: Incorrect Calculation of Buffer Size|accessdate=2018/12/18|publisher=2018/12/18}}</ref>、整数オーバーフローもバッファオーバーフローの原因になる事が述べられている<ref>{{Cite web|url=https://cwe.mitre.org/data/definitions/680.html|title=CWE-680: Integer Overflow to Buffer Overflow|accessdate=2018/12/18|publisher=Mitre}}</ref>。<br />
近年、コンピュータの制御を乗っ取り、攻撃を行うウイルスはバッファオーバーランを利用した物が多い。脆弱性を示すために作られたプログラムExploitを悪用し、そのプログラムにウイルス機能を載せた物が大多数を占める。[[2003年]][[8月]]インターネットトラフィックにおいて[[インターネットバックグラウンドノイズ|バックグラウンドノイズ]]とされるトラフィックが1kbps未満から突然30〜40kbpsに跳ね上がった。これはWindowsのRPCサブシステムにおけるバッファオーバーランによるセキュリティホールを攻撃し、制御を乗っ取り自らウイルスを放出する'''MSBlastウイルス'''による攻撃が全世界規模で発生したためである。


=== 古典的バッファオーバーフロー攻撃 ===
C言語でかかれ、古いライブラリ関数を多用している、そして多くの文字列処理を行っている、"[[sendmail]]"プログラムは近年こそ毎年のペースまで下がったが、以前は毎月のようにバッファオーバーランによるセキュリティホールが発覚し、修正されていた。そしてsendmailを突破口としてセキュリティを破られたシステムはとても多く、その数は[[Winny]]による情報流出を上回るものである。このようなセキュリティ上の観点から(またライセンスの関係もあるが)sendmailを標準プログラムから排除する動きがあり、いくつかのOSディストリビューションの標準セットからsendmailは取り除かれてしまった。
バッファAの値を他のバッファBにコピーするとき、BのバッファサイズがAのバッファサイズよりも大きいことをチェックしない場合に生じる脆弱性である<ref name=":1" />。この脆弱性は、プログラマーがこのようなチェックの実装を怠った事により生じる<ref name=":1" />。これはプログラマーが最低限のセキュリティチェックすらしていないことを強く示唆する<ref name=":1" />。


脆弱性の具体例としては、例えば[[C言語]]や[[C++]]において、配列サイズをチェックする事なく、配列に[[strcpy]]や[[scanf]]で文字列等をコピーするといったものがある<ref name=":1" />。攻撃者は意図的に大きな入力を[[strcpy]]、[[scanf]]、[[gets]]に与えることで、古典的バッファオーバーフローを不正に生じさせる事ができる<ref name=":1" />。
== エクスプロイト==


例えば攻撃者がバッファAをバッファオーバーフローさせる事により、Aの隣りにある変数xを不正に変更できた場合、xがセキュリティ上重要なデータ(例えば管理者権限を持っているか否かを判定するビットを保持している)であれば、セキュリティ上重要な問題が生じる。
バッファオーバーフローに起因する脆弱性を利用したエクスプロイトは、[[コンピュータ・アーキテクチャ]]、[[オペレーティングシステム]]、メモリ領域によって大きく内容が異なる。たとえば、[[ヒープ領域]](動的メモリ確保で利用される領域)に対する[[エクスプロイト]]は[[コールスタック]]に対するそれとは大きく異なる。


<br />
=== スタックベースのエクスプロイト ===


{{Main|スタックバッファオーバーフロー}}
=== スタックベースのバッファオーバーフロー攻撃 ===
スタックベースのバッファオーバーフローは「上書きされるバッファがスタック(すなわち、局所変数や、まれに関数のパラメータ)にアロケートされる」<ref name=":2" />事が可能な場合に生じる脆弱性である。このような脆弱性は[[ファジング]]を使用して発見されることが多い<ref>{{cite web|url=http://raykoid666.wordpress.com|title=The Exploitant - Security info and tutorials|accessdate=2009-11-29}}</ref>。


==== C言語・C++におけるメモリ領域 ====
悪意のある利用者がスタックベースのバッファオーバーフローを発生させてプログラムの処理を操作する際には、以下のような方法が用いられる。
スタックベースのバッファオーバーフローについて説明するためにプロセスのメモリ利用方法を復習する。[[オペレーティングシステム|OS]]は各ユーザプロセスに[[仮想記憶|'''仮想アドレス空間''']]を割り振り、[[Windows]]や[[Linux]]などのOSでは上位のアドレスをカーネルが使う'''カーネル空間'''とし、残りをユーザプロセス自身が用いる'''ユーザ空間'''とする<ref group="注">i386の場合、Windowsなら仮想空間の上位2GB、Linuxなら仮想空間の上位1GBがカーネル空間になる。なお本節で書いたメモリ箇所はいずれも後述するセキュリティ技術[[アドレス空間配置のランダム化|アドレス空間配置のランダム化(ASLR)]]を用いていない場合の話である。</ref>。カーネル空間はユーザプロセスがアクセスする事はできず、通常のプログラミングでは意識する事はない。


[[C言語]]や[[C++]]で書かれたプログラムの場合、ユーザ空間をさらに分割する。これらの言語で書かれたプログラムでは、仮想アドレスの最低位の箇所から順にプログラムの実行コードを置く'''コード領域(テキスト領域とも)'''、初期化された静的変数・大域変数を置く'''データ領域'''、初期化されていない静的変数・大域変数を置く[[.bss|'''bss領域''']]<ref group="注">'''Block Started by Symbol'''という[[アセンブラ]]の疑似命令に由来する</ref>、[[malloc]]等で動的に確保されたメモリを置く(可変サイズの)'''ヒープ領域'''を確保する<ref name=":5">[[#Erickson(2011)|Erickson(2011)]] p.87-88</ref>。(なおデータ領域とbss領域を合わせて'''静的領域'''という)。
* スタック上でバッファの近くにあるローカル変数の値を上書きして、プログラムの動作を攻撃者にとって有利なように変更する。


一方、ユーザ空間における仮想アドレスの最高位の箇所は関数の[[コールスタック]]を保存する(可変サイズの)'''スタック領域'''として用いられる<ref name=":5" />。
* [[コールスタック|スタックフレーム]]中の戻りアドレスを上書きする。関数から戻る際、攻撃者が指定したリターンアドレス(通常はユーザの入力を格納しているバッファ)から処理が再開されるようにする。
{| class="wikitable"
|+
|最低位
| colspan="4" |        …
|         最高位
|-
| rowspan="2" |コード領域
| colspan="2" |静的領域
| rowspan="2" |ヒープ領域
(高位に向かって成長→)
| rowspan="2" |…
| rowspan="2" |スタック領域
(←低位に向かって成長)
|-
|データ領域
|bss領域
|}


スタック領域はプロセス中で呼ばれる関数のコールスタックを格納する領域で、コールスタック中の各関数のデータ('''スタックフレーム'''という)を並べて格納している<ref name=":6">[[#Erickson(2011)|Erickson(2011)]] p.84</ref>。プロセス中で関数fが関数gを呼び出した場合、コールスタックは以下のようになる<ref name=":6" /><ref name=":7">[[バッファオーバーラン#%E5%85%AB%E6%9C%A8%E3%80%81%E6%9D%91%E5%B1%B1%E3%80%81%E7%A7%8B%E5%B1%B1 2015|八木、村山、秋山 2015]] pp.60-61</ref>:
* 後で実行される関数ポインタ<ref>{{cite web
{| class="wikitable"
|url=http://www.securityfocus.com/archive/1/462728/30/150/
|+
|title=CORE-2007-0219: OpenBSD's IPv6 mbufs remote kernel buffer overflow
|低位アドレス
|date=2007-03-13
| colspan="7" |                            …
|accessdate=2007-05-15}}</ref>または例外ハンドラを書き換える。
|高位アドレス
|-
|…
| colspan="4" |gのスタックフレーム
| colspan="3" |fのスタックフレーム
|…
|-
|…
|gの処理に必要な一時的な情報
|gの局所変数
|gのSFP
|gのリターンアドレス
|gの引数の値
|fの処理に必要な一時的な情報
|…
|…
|}


ここで、ユーザの入力したデータのアドレスは未知であるが、アドレスがレジスタに格納されていることは分かっているという場合には、''トランポリン''(trampolining)と呼ばれる手法が利用される。この手法では、ユーザの入力したデータにジャンプする[[オペコード]]のアドレスをリターンアドレスへ上書きする。例えばアドレスがレジスタRに格納されている場合、jump R(あるいはcall Rなど)というオペコードが格納されているアドレスにジャンプさせることでユーザの入力したデータを実行させる。この手法で使用するオペコードは[[ダイナミックリンクライブラリ|DLL]]や実行ファイルの中のものを利用する。ただし、一般的にオペコードのアドレスに[[ヌル文字]]が含まれていてはならず、また処理に使用するオペコードのアドレスはアプリケーションやオペレーティングシステムのバージョンによって異なる。[[Metasploit]]プロジェクトはこのような目的に適したオペコードのデータベースの一つであり、[[Microsoft Windows|Windows]]で使用できるオペコードが記載されている<ref>{{cite web
|url = http://metasploit.com/users/opcode/msfopcode.cgi
|title = The Metasploit Opcode Database
|accessdate = 2007-05-15
|archiveurl = https://web.archive.org/web/20070512195939/http://www.metasploit.com/users/opcode/msfopcode.cgi
|archivedate = 2007年5月12日
|deadurldate = 2017年9月
}}</ref>。


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


プロセスで現在実行中の関数のスタックフレームの位置を覚えるためにプロセッサによって用いられるのが'''フレームポインタ'''(=[[x86]]ではebp)で具体的には(現在実行中の関数がgであれば)gのSFPのアドレスを指している。'''SFP'''は関数呼び出し時に呼び出し元の関数のフレームポインタのアドレスを覚えるための領域で、fがgを呼び出した際、スタックフレームの値(=fのSFPのアドレス)をgのSFPに格納する。なお、SPFは「Saved Frame Pointer」の略で日本語訳は「'''退避されたフレームポインタ'''」である<ref name=":6" />。
また、このような脆弱性は[[ファジング]]を使用して発見されることが多い<ref>{{cite web
|url=http://raykoid666.wordpress.com
|title=The Exploitant - Security info and tutorials |accessdate=2009-11-29}}</ref>。


一方gの'''リターンアドレス'''は呼び出し元関数fの'''プログラムカウンタ'''('''命令ポインタ'''とも。x86ではeip)のアドレスを格納する<ref name=":6" />。<br />
=== ヒープベースのエクスプロイト ===


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


[[マイクロソフト]]の[[Graphics Device Interface|GDI+]]におけるJPEG処理の脆弱性は、ヒープオーバーフローの危険性を示す例といえる<ref>{{cite web
|url=http://www.microsoft.com/technet/security/bulletin/MS04-028.mspx
|title=Microsoft Technet Security Bulletin MS04-028
|accessdate=2007-05-15}}</ref>。


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


今例えば、関数gはユーザから([[ASCII|ASCIIコード]]の)文字列を入力を受け取り、入力された文字列を配列char A[10]に格納するとする。Aのサイズは10であるので、関数gのプログラマはユーザから受け取った文字列をAに格納する前に、その文字列が本当に10文字以下なのかをチェックする機構をgに実装しておかねばならない。このようなチェック機構を実装するのを忘れていた場合、悪意のあるユーザ(以下、「攻撃者」と呼ぶ)によりスタックベースのバッファオーバーフロー攻撃を受けてしまう危険がある。
バッファの読み込みや実行の前に行われるバッファの操作が原因で、エクスプロイトが失敗する場合もある。このような操作を利用してエクスプロイトの脅威を軽減することはできるが、それでもエクスプロイトが不可能にはならない。バッファに対する操作としては大文字小文字変換、[[メタ文字]]の除去、非[[英数字]]のフィルタリングなどがあるが、これらの処理をくぐり抜けるテクニックも存在する([[英数字コード]]([[w:alphanumeric code]])、[[ポリモルフィックコード]]、[[自己書き換えコード]]、[[Return-to-libc攻撃]]など)。また、[[侵入検知システム]]をすり抜けるのにも同様の方法が使用できる。また場合によっては、コードがUnicodeでエンコードされている場合など、実際にはリモートから任意のコードの実行が可能であるにも関わらず、発見者によってただの[[DoS攻撃|DoS]]であると不正確に伝えられているような脆弱性もある<ref>{{cite web
|url=http://www.net-security.org/dl/articles/unicodebo.pdf
|title=Creating Arbitrary Shellcode In Unicode Expanded Strings
|format=PDF
|accessdate=2007-05-15}}</ref>。


=== エクスプロイトの実際 ===


具体的には、攻撃者は以下のような文字列をgに入力する:<blockquote>('''シェルコード''')…('''シェルコードの仮想アドレス''')</blockquote>ここで'''シェルコード'''とは、何らかの悪意のある短いプログラムで、例えば攻撃者のためにバックドアを開けたり、マルウェアのダウンロードを行ったりする<ref name=":7" />。
実世界においてエクスプロイトを確実に実行させるためには、克服しなければならない様々な課題が存在する。例えば、アドレス中のNULLバイト、シェルコードの位置のばらつき、環境ごとの差異、各種の攻撃対策などが挙げられる。


この文字列が配列Aの先頭から順に書き込まれていくと、'''A[i]のアドレスはiが大きいほど大きくなるので'''、攻撃者が入力文字列の長さを適切に選べば、アドレス空間には以下のようにデータが書き込まれ、'''gのリターンアドレスがシェルコードの仮想アドレスに上書きされる'''事になる<ref name=":7" />:
==== NOPスライド====
{| class="wikitable"
|+
|低位アドレス
| colspan="7" |                            …
|高位アドレス
|-
|…
| colspan="4" |gのスタックフレーム
| colspan="3" |fのスタックフレーム
|…
|-
|…
|gの処理に必要な一時的な情報
|gの局所変数
|gのSFP
|gのリターンアドレス
|gの引数の値
|fの処理に必要な一時的な情報
|…
|…
|-
|
|
|'''(シェルコード)…'''
|…
|'''(シェルコードの仮想アドレス)'''
|
|
|
|
|}


よって関数gが終了したとき、gのリターンアドレス(の箇所に上書きされたシェルコードの仮想アドレス)が読み込まれるので、プログラムカウンタはシェルコードの位置にジャンプし、攻撃者の狙い通り、シェルコードが実行される事になる<ref name=":7" />。
{{Main|NOPスライド}}


==== NOPスレッド ====
[[Image:nopsled.svg|right|thumb|200px|スタック中のNOPスライドのペイロード]]
上で述べた攻撃のアイデアが実行可能であるためには、攻撃者がリターンアドレスに上書きすべき想アドレスを正確に知り、それを関数gに入力する必要があるが、スタックは動的に変化するため、これは容易ではない<ref name=":8">[[バッファオーバーラン#Erickson(2011)|Erickson(2011)]] p.161-164</ref>。そこで本節ではリターンアドレスに上書きすべき仮想アドレスの「おおよその値」さえ分かれば攻撃が可能になるテクニック('''NOPスレッド''')を述べる。


'''NOP'''とは「何も行わない」事を意味するアセンブリ命令で<ref name=":8" />、本来はタイミングを合わせるなどの動機により何もせずにCPU時間を消費するために用いられる<ref name=":8" />。NOPスレッド(sled、[[そり]])<ref name=":8" />とはこのNOP命令を複数個並べたもので、これを利用する事により攻撃対象の関数をシェルコードの位置まで滑走させる。
NOPスライドは、スタックバッファオーバーフローを利用するテクニックとして最古のものであり、また最も広く知られているものでもある<ref name="neworder">{{cite journal

|author = Vangelis
具体的には攻撃者は下記のような文字列を攻撃対象の関数gに入力する:<blockquote>'''NOP … NOP''' ('''シェルコード''')('''戻りアドレス''')…('''戻りアドレス''')</blockquote>
|title = Stack-based Overflow Exploit: Introduction to Classical and Advanced Overflow Technique
最初の「NOP … NOP」の部分がNOPスレッドである。攻撃者がNOPスレッドの長さや戻りアドレスの繰り返し回数を適切に選んでgに入力すると、スタック領域は例えば以下のようになる(攻撃に関係する部分だけ抜書き):
|publisher = Wowhacker via Neworder
{| class="wikitable"
|date = 2004-12-08
|+
|url = http://www.neworder.box.sk/newsread.php?newsid=12476
| colspan="4" |                            gのスタックフレーム
|format = text
|fのスタックフレーム
|archiveurl = https://web.archive.org/web/20070818115455/http://www.neworder.box.sk/newsread.php?newsid=12476
|-
|archivedate = 2007年8月18日
|…
|gの局所変数
|gのSFP
|gのリターンアドレス
|…
|-
|…
|'''NOP…''' '''NOP''' …
|'''NOP''' …'''NOP (シェルコード)…(戻りアドレス)…(戻りアドレス)'''
|'''(戻りアドレス)'''
|'''(戻りアドレス)…'''
|}

これでgのリターンアドレスは「戻りアドレス」にセットされるので、攻撃者が「戻りアドレス」としてNOPスレッド部分のアドレスを指定する事に事前に成功していれば、gの終了時にNOPスレッドへとプログラムカウンタが移動する。するとプログラムはNOPを順に実行して右へ右へと移動し、シェルコードの位置にたどり着いてシェルコードが実行されるので、攻撃成功となる<ref name=":8" /><ref>[[バッファオーバーラン#%E5%85%AB%E6%9C%A8%E3%80%81%E6%9D%91%E5%B1%B1%E3%80%81%E7%A7%8B%E5%B1%B1 2015|八木、村山、秋山 2015]] p.64</ref>。戻りアドレスがNOPスレッドのどこかに落ちさえすればよいので、前節で述べた攻撃違い、リターンアドレスにセットする値を完璧に制御する必要はなく、NOPスレッドの長さ分の誤差が発生しても攻撃が成功する事になる。

NOPスレッドは頻繁に使用されるため、多くの[[侵入防止システム]]ベンダーでシェルコードの判定に使用されている。このため、エクスプロイトの作成者側では、シェルコードの実行に影響を及ぼさない(NOP以外の)任意の命令をランダムに選んでスレッドを構成することが常套手段となっている<ref name="Akritidis1">{{cite conference|last=Akritidis|first=P.|coauthors=Evangelos P. Markatos, M. Polychronakis, and Kostas D. Anagnostakis|year=2005|booktitle=Proceedings of the 20th IFIP International Information Security Conference (IFIP/SEC 2005)|url=http://dcs.ics.forth.gr/Activities/papers/stride-IFIP-SEC05.pdf|title=STRIDE: Polymorphic Sled Detection through Instruction Sequence Analysis.|publisher=IFIP International Information Security Conference|format=PDF}}</ref>。

==== 戻りアドレスの値の予想 ====
攻撃を実行するには、あとは「戻りアドレス」として具体的にどの程度の値を代入すればよいかを知ればよい。しかし攻撃の標的となる組織の環境で戻りアドレスの絶対アドレスがいくつ程度の値になるのかを事前に知る事は難しい。そこで相対アドレスを利用して戻りアドレスを適切に選ぶ攻撃テクニックを紹介する。この攻撃のシナリオでは、攻撃者はシェルコードを含んだ攻撃用のプログラムh(をコンパイルして作った実行コード)を攻撃の標的となる組織に送りつけ、hのサブルーチンとしてgを呼び出す事で攻撃を行う。



この攻撃用プログラムhでは、変数xを宣言が宣言されているものとする。hが標的の環境でgを呼び出したとき、gのスタックフレームはスタック領域上でhのスタックフレームのすぐ隣に配置される事から、攻撃用文字列を入れ込むgの変数の絶対アドレスvar_addは<blockquote>var_add = &x - (小さな値)</blockquote>になるはずである<ref name=":8" />。ここで「&x」はxのアドレスを表す。既に述べたようにNOPスレッドを使った攻撃では戻りアドレスとしてvar_add近辺の値を選べば成功するので、攻撃者はこの「小さな値」を決定しさえすればよい。

よって攻撃者は関数gの実行コードを事前に入手し、([[シェルスクリプト]]等を使って)NOPスレッドの長さや戻りアドレスを変えながら攻撃対象のプログラムを何度も実行することで適切な「小さな値」を選び、その「小さな値」を攻撃用プログラムhに書き込んでおけばよい<ref name=":8" />。

==== 埋め込めるコード量が小さい場合の対処 ====
関数gに埋め込む攻撃用の文字列は「NOPスレッド+シェルコード+戻りアドレスの繰り返し」という形をしており、gのリターンアドレスが「戻りアドレスの繰り返し」の部分に落ちない限り攻撃は成功しないので、関数gに攻撃用文字列を埋め込む箇所とgのリターンアドレスとが(仮想アドレス空間上で)あまりに近い場合は、攻撃に必要な長さのシェルコードを埋め込めないという問題が攻撃者に生じる。

しかし攻撃者が攻撃の標的となるマシンの[[環境変数]]を設定できる状況下では、攻撃者はこの問題を回避した攻撃が可能である。標的マシンでプロセスが実行される際には、そのプロセスの仮想アドレス空間に環境変数が読み込まれるので、攻撃者が事前に標的マシンの環境変数に「NOPスレッド+シェルコード」を書き込んでおけば、プロセスの仮想アドレスに「NOPスレッド+シェルコード」ができあがる事になる。プロセス中で関数gが実行された際、攻撃者は攻撃用文字列をgに入力して、リターンアドレスをそのNOPスレッドに書き換えれば攻撃が成功する事になる<ref>[[バッファオーバーラン#Erickson(2011)|Erickson(2011)]] p.164-168</ref>。

より確実な攻撃方法として、攻撃プログラムhの中に環境変数を読み込む関数(getenv()等)を用いるものもある<ref>[[バッファオーバーラン#Erickson(2011)|Erickson(2011)]] p.168-173</ref>。
<br />

==== ヒープスプレー ====
{{Main|:en:Heap spraying}}
NOPスレッドを長くしすぎると、gのリターンアドレスの位置にすらNOPが書き込まれてしまって攻撃に失敗する為、NOPスレッドを長くして攻撃成功率を上げる手法には限界がある。しかし攻撃者がプロセスのヒープ領域の値をも自由に操れるという条件下では、攻撃者は'''ヒープスプレー'''というテクニックを用いる事により、この限界を突破した攻撃を行う事が可能になる<ref name=":9">[[バッファオーバーラン#%E5%85%AB%E6%9C%A8%E3%80%81%E6%9D%91%E5%B1%B1%E3%80%81%E7%A7%8B%E5%B1%B1 2015|八木、村山、秋山 2015]] pp.65-67</ref>。

ヒープスプレーでは、NOPスレッドとシェルコードを、スレッド領域ではなくヒープ領域に埋め込み、戻りアドレスとしてヒープ領域中のNOPスレッドを指定する<ref name=":9" />。ヒープ領域上のNOPスレッドにはスレッド領域のNOPスレッドと違い前述した長さの上限が存在しないため、「長いNOPスレッド+シェルコード」の組み合わせを大量にヒープ中に書き込むことで攻撃成功率を上げる<ref name=":9" />。

[[ウェブブラウザ]]では[[JavaScript]]などのクライアントサイドスクリプトにより任意の長さのヒープを作成できるので、ブラウザを対象にした[[ドライブバイダウンロード]]攻撃ではヒープスプレーが使われる事が多い<ref name=":9" />。

==== トランポリング ====
攻撃者の入力したデータ(エクスプロイトなど)のアドレスは未知であるが、そのデータのアドレスがレジスタに格納されていることは分かっているという場合には、''トランポリン''(trampolining)と呼ばれる手法が利用される。この手法では、攻撃者の入力したデータにジャンプする[[オペコード]]のアドレスをリターンアドレスへ上書きする。例えばアドレスがレジスタRに格納されている場合、jump R(あるいはcall Rなど)というオペコードが格納されているアドレスにジャンプさせることでユーザの入力したデータを実行させる。この手法で使用するオペコードは[[ダイナミックリンクライブラリ|DLL]]や実行ファイルの中のものを利用する。ただし、一般的にオペコードのアドレスに[[ヌル文字]]が含まれていてはならず、また処理に使用するオペコードのアドレスはアプリケーションやオペレーティングシステムのバージョンによって異なる。[[Metasploit]]プロジェクトはこのような目的に適したオペコードのデータベースの一つであり、[[Microsoft Windows|Windows]]で使用できるオペコードが記載されている<ref>{{cite web
|url = http://metasploit.com/users/opcode/msfopcode.cgi
|title = The Metasploit Opcode Database
|accessdate = 2007-05-15
|archiveurl = https://web.archive.org/web/20070512195939/http://www.metasploit.com/users/opcode/msfopcode.cgi
|archivedate = 2007年5月12日
|deadurldate = 2017年9月
|deadurldate = 2017年9月
}}</ref>。
}}</ref>。


<br />
このテクニックでは、攻撃のためにはバッファの正確なアドレスを知る必要があるという問題に対し、攻撃のターゲットとなる領域を拡大させることで問題を解決する。このためには、スタック中のかなり広い区域が[[NOP]]命令で破壊されている必要がある。攻撃者が入力したデータの末尾(NOP命令の後)には、[[シェルコード]]が位置するバッファの先頭への相対ジャンプ命令が格納されている。ここで、NOP命令の集まりは''NOPスレッド''と呼ばれるが、これは戻りアドレスをNOP領域のどのアドレスで上書きしたとしても、NOP命令の上を「スライド」していき、末尾のジャンプ命令により実際の悪意あるコードへ転送が行われるためである。
このテクニックを利用すれば、攻撃者は比較的小さなシェルコードの場所を推測する代わりに、スタック上のどこにNOPスレッドがあるかだけを推測するだけでよい<ref name="enderunix">{{cite journal
| last=Balaban
| first=Murat
| title=Buffer Overflows Demystified
| publisher=Enderunix.org
| url=http://www.securityforest.com/downloads/educationtree/bof-eng.txt
| format=text }}</ref>。


=== ヒープベースのバッファオーバーフロー ===
このテクニックは頻繁に使用されるため、このパターンのNOP命令は多くの[[侵入防止システム]]ベンダーでシェルコードの判定に使用されている。ただし、NOPスライディングを通常のNOP命令だけで構成する必要はないという点には注意すべきである。シェルコードの実行と直接関係ない箇所では、マシンの状態を変更しなければどのような命令でもNOP命令の代わりに使用できる。このため、エクスプロイトの作成者側では、シェルコードの実行に影響を及ぼさない任意の命令をランダムに選んでNOPスレッドを構成することが常套手段となっている<ref name="Akritidis1">{{cite conference
<br />[[malloc]]などでヒープ領域に[[動的メモリ確保|動的にメモリを確保する]]関数に対するオーバーフロー攻撃である<ref name=":3" />。基本的な攻撃手法としては、関数がヒープに確保したメモリ領域が2つあるとき、そのうち一方の領域に対して確保済みのメモリサイズより大きなデータを入力する事でオーバーフローを起こし、もう一方のメモリ領域を書き換えるというものである<ref>[[バッファオーバーラン#Erickson(2011)|Erickson(2011)]] pp.173-179</ref>。mallocは複雑な方法でメモリ確保の場所を決定しているものの、連続して2度mallocした場合にはその結果として確保される2つのメモリ領域は(仮想アドレス空間上で)近くにある傾向があるため、上述のようなバッファオーバーフロー攻撃が可能になる。
| first=P.
| last=Akritidis
| coauthors=Evangelos P. Markatos, M. Polychronakis, and Kostas D. Anagnostakis
| title=STRIDE: Polymorphic Sled Detection through Instruction Sequence Analysis.
| booktitle=Proceedings of the 20th IFIP International Information Security Conference (IFIP/SEC 2005)
| publisher=IFIP International Information Security Conference
| year=2005
| format= PDF
| url=http://dcs.ics.forth.gr/Activities/papers/stride-IFIP-SEC05.pdf }}</ref>。


==== mallocのメモリ管理 ====
この手法は攻撃が成功する確率を劇的に増加させるが、問題がないわけではない。このテクニックではスタック中でNOPスレッドの領域を指すオフセットを推測することになるため、このテクニックを使用したエクスプロイトはある程度運に依存することになる<ref name="klein1">{{cite journal
より高度なヒープベースバッファオーバーフロー攻撃手法を説明する為の準備として、mallocのメモリ管理方法を説明する。なおメモリ管理方法の詳細はmallocの実装に依存するため、実行環境によって細かなところは下記の説明と異なる部分があるので注意されたい。
| last=Klein
| first=Christian
| title=Buffer Overflow
| date=2004-09
| url=http://c0re.23.nu/~chris/presentations/overflow2005.pdf
| format=PDF }}


mallocは未使用なヒープ領域(の一部)をメモリプールとして管理しており、メモリプールは複数のchunkと呼ばれる単位からなっている<ref name=":10">{{Cite web|url=https://www.valinux.co.jp/technologylibrary/document/linux/malloc0001/|title=malloc(3)のメモリ管理構造|accessdate=2018/12/26|author=技術本部 クラウド基盤エキスパート  角馬 文彦|date=2007/11/30|publisher=VA Linux Systems Japan}}</ref>。プログラム中でmallocが実行されるたびに、管理しているchunkの中から適切なサイズのものをプログラムに返す<ref name=":10" />。適切なサイズのchunkがない場合は、[[システムコール]]により新たなchunkを確保してプログラムに返す<ref name=":10" />。mallocはchunkを(サイズ毎に異なる複数の<ref>{{Cite web|url=http://g.oswego.edu/dl/html/malloc.html|title=A Memory Allocator|accessdate=2018/12/26|author=Doug Lea|publisher=}}</ref>)[[連結リスト]]として管理しており<ref name=":10" />、chunkをプログラムに渡す際にこの連結リストからchunkを削除し、プログラムがメモリ領域をfreeすると、freeされたchunkが連結リストに加わる<ref name=":10" />。
</ref>。この推測を誤ると、通常の場合ターゲットとなるプログラムはクラッシュし、[[システムアドミニストレータ]]に攻撃の動きを警戒されることになってしまう。またそれとは別に、NOPスレッドが非常に多くのメモリを必要とするという問題もある。これはNOPスレッドが役に立つためにはそのサイズが十分に大きい必要があるためだが、これはNOPスライドで影響を受けるバッファの割り当てサイズが小さく、かつ現在のスタックの深さが浅い場合(すなわち、現在のスタックフレームの末尾からスタックの先頭までの間に十分なスペースがないとき)に問題となる。こういった問題にもかかわらずNOPスライドがいまだに重要なテクニックであり続けているのは、所与のプラットフォーム、環境、状況によってはNOPスライドが唯一利用可能な攻撃方法となる場合がしばしばあるためである。


chunkは連結リストとして管理されているので、各chunkには「次のchunk」を指定するポインタ(windowsでは「flink」<ref name=":11">[[#FFRI(2013)|FFRI(2013)]] p7</ref>、linuxでは「fd」<ref name=":10" />)や「前のchunk」を指定するポインタ(windowsでは「blink」<ref name=":11" />、linuxでは「bk」<ref name=":10" />)がある。
==== レジスタに格納されているアドレスにジャンプする方法 ====


未使用chunkと隣接するメモリ領域が開放された場合は、開放されたメモリ領域と未使用chunkとを連結(coalesce)する事で1つの大きなchunkを作って管理する<ref name=":11" />。
''jump to register''とは、NOPスレッドの格納領域もスタックのオフセットの推測も必要とせずに、スタックバッファオーバーフローを用いた確実なエクスプロイトを可能にするテクニックである。戦略としては、リターンポインタを上書きして、レジスタへ格納されている既知のポインタへのジャンプを起こさせる(このポインタはコントロールされたバッファ、ひいてはシェルコードを指している)。例えばレジスタAがバッファの先頭へのポインタを格納しているとすると、そのときレジスタAをオペランドにとる任意のjumpまたはcall命令が実行フローの支配権を得るのに使用できる<ref name="shah">{{cite conference
<br />
| first=Saumil
| last=Shah
| title=Writing Metasploit Plugins: from vulnerability to exploit
| booktitle=Hack In The Box
| year=2006
| location=Kuala Lumpur
| format=PDF
| url=http://conference.hitb.org/hitbsecconf2006kl/materials/DAY 1 - Saumil Shah - Writing Metasploit Plugins.pdf }}</ref>。


==== mallocのメタデータの書き換え ====
[[Image:jumpToEsp.png|left|thumb|300px|ntdll.dll中の<code>DbgPrint()</code>ルーチンを呼び出す命令には[[Intel 80386|i386]]のオペコード<code>jmp esp</code>が含まれている]]
より高度なヒープベースバッファオーバーフロー攻撃として、mallocがメモリ管理に使うメタデータを書き換える手法がある。例えば[[Microsoft Windows XP|XP]] SP1ないしそれ以前のWindowsでは、mallocしたメモリ領域をオーバーフローさせる事で、そのメモリ領域の(仮想アドレス空間で)隣りにある未使用chunkのflinkやblinkを任意のアドレスに書き換えるという攻撃手法が可能あった<ref name=":12">[[#FFRI(2013)|FFRI(2013)]] p8</ref>。flinkやblinkはcoalesceのタイミングでmallocにより参照されるので攻撃が成功する<ref name=":12" />。
<br />


=== バッファオーバーフローの結果 ===
実際には、特定のレジスタへのジャンプ命令を意図的に含めないようにしたプログラムもある。よくある解決法としては、プログラムのメモリ中の固定の位置にある[[オペコード]]の中に、使える命令のインスタンスが意図せず作られているのを探してくる方法がある。左図はそのような意図しないインスタンスの例で、[[Intel 80386|i386]]の<code>jmp esp</code>命令が作られている。この命令のオペコードは<code>FF E4</code>である<ref name="intel1">{{cite book
| title=Intel 64 and IA-32 Architectures Software Developer's Manual Volume 2A: Instruction Set Reference, A-M
| publisher=Intel Corporation
| date=2007-05
| pages=3-508
| format=PDF
| url=http://developer.intel.com/design/processor/manuals/253666.pdf }}</ref>。


==== write-what-where状態 ====
この2バイトの並びは、<code>call DbgPrint</code>命令の先頭から1バイトオフセットした位置(アドレス<code>0x7C941EED</code>)にある。攻撃者がプログラムのリターンアドレスをこのアドレスで上書きしたら、プログラムはまず最初に<code>0x7C941EED</code>へジャンプし、オペコード<code>FF E4</code>を<code>jmp esp</code>と解釈する。そしてスタックの先頭へジャンプし、攻撃者の指定したコードを実行する<ref name="packetstorm1">{{cite journal
バッファオーバーフロー攻撃の結果として、'''write-what-where状態'''("write-what-where condition"<ref name=":13">{{Cite web|url=https://cwe.mitre.org/data/definitions/123.html|title=CWE-123: Write-what-where Condition|accessdate=2018/12/26|publisher=Mitre}}</ref>、CWE-123<ref name=":13" />。任意の場所に任意の値を書き込むことができる状態<ref>{{Cite web|url=https://jvndb.jvn.jp/ja/contents/2015/JVNDB-2015-004721.html|title=JVNDB-2015-004721 Silicon Integrated Systems WindowsXP Display Manager における権限を取得される脆弱性|accessdate=2018/12/26|website=JVN iPedia|publisher=}}</ref>)になる危険がある<ref name=":13" />。
| last=Alvarez
| first=Sergio
| title=Win32 Stack BufferOverFlow Real Life Vuln-Dev Process
| publisher=IT Security Consulting
| date=2004-09-05
| url=http://packetstormsecurity.org/papers/win/windowsdev.pdf
| format =PDF }}</ref>。


write-what-where状態になると、セキュリティポリシーのスコープ外にメモリのデータを書き込むことができる<ref name=":13" />。セキュリティポリシーのスコープ外にコードを置くことで、攻撃者はほぼ確実に任意のコードを実行できるようになる<ref name=":13" />。管理者権限を制御しているフラグ等が書き換えられる場合も、攻撃者は任意のコードが実行可能になる<ref name=":13" />。
このテクニックが利用可能な場合、脆弱性の重大性は相当に高くなる。これは、処理を自動化してもほぼ確実に攻撃が成功するほどエクスプロイトの成功率が高くなるためである。そのため、これはスタックバッファオーバーフローの脆弱性を利用する[[ワーム_(コンピュータ)|インターネットワーム]]において最もよく使われるテクニックとなっている<ref name="Yuji1">
{{cite conference
| first = Yuji
| last = Ukai
| coauthors = Soeder, Derek; Permeh, Ryan
| title = Environment Dependencies in Windows Exploitation
| booktitle = BlackHat Japan
| publisher = eEye Digital Security
| year = 2004
| location = Japan
| url = http://www.blackhat.com/presentations/bh-asia-04/bh-jp-04-ukai-eng.ppt }}</ref>。


==== プログラムのフリーズ・クラッシュ ====
また、この方法を使えば、Windowsプラットフォームにおいては上書きしたリターンアドレスの後ろにシェルコードを配置することもできる。実行ファイルは多くの場合アドレス<code>0x00400000</code>から配置され、またx86は[[リトルエンディアン]]アーキテクチャのため、リターンアドレスの末尾のバイトは必ずNULLになる。そのため、バッファへのコピーはそこで終了されてしまい、それ以降には何も書き込まれない。これにより、シェルコードのサイズはバッファのサイズに制限されることになるが、これは場合によっては非常に厳しい制限となる。一方DLLはハイメモリ(アドレス<code>0x01000000</code>より上)に配置されるため、アドレスにNULLバイトが含まれないようにできる。そのため、この方法であれば上書きするリターンアドレスがNULLバイト(あるいはその他の禁止された文字)を含まないようにできる。このようにDLLを使った方法は''DLLトランポリン''(DLL Trampolining)などとも呼ばれる。
攻撃者は意図的にバッファオーバーフローを起こすによりプログラムをクラッシュさせたり<ref name=":1" /><ref name=":2" /><ref name=":3" />、処理を書き換えて無限ループに追い込むことでプログラムをフリーズさせてたり<ref name=":1" /><ref name=":2" /><ref name=":3" />する事でプログラムの[[可用性]]を侵害できる。


==== 関数ポインタの書き換え ====
== 防御的対策 ==
古典的バッファオーバーフロー<ref name=":14">[[バッファオーバーラン#Erickson(2011)|Erickson(2011)]] pp.179-192</ref>やヒープオーバーフロー<ref name=":3" />などの結果として、関数ポインタの書き換えが可能になるケースがある。攻撃者は[[Nm (UNIX)|nm]]コマンドを用いる事でプログラム中で用いられている様々な関数のアドレスを知る事ができるので<ref name=":14" />、nmの結果を参照して攻撃に利用可能な関数に関数ポインタの値を書き換えられる。


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


== 技術的対策 ==
=== プログラミング言語の選択 ===


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


==== カナリア ====
具体的には、
バッファオーバーフローを検出するコードをコンパイル時に実行コードに挿入する手法がある。典型的手法としては、ローカル変数とSFPの間に、'''カナリア'''(canary)<ref>{{Cite web|url=https://thinkit.co.jp/cert/article/0603/5/14/2.htm|title=第14回:バッファオーバーフローとサーバ側のセキュリティ対策を考える (2/3)|accessdate=2019/01/01|author=芝国雄|date=2006/9/5|website=ThinkIT|work=オープンソースの適用可能性を示す|publisher=}}</ref><ref>{{Cite web|url=https://www.ibm.com/developerworks/jp/linux/library/l-sp4/index.html|title=セキュアなプログラマー バッファー・オーバーフローに対抗する 今日最大の脆弱性を防止する|accessdate=2019/01/01|author=David Wheeler|date=2004/1/27|publisher=IBM}}</ref>もしくは'''クッキー'''<ref name=":17">[[バッファオーバーラン#%E5%85%AB%E6%9C%A8%E3%80%81%E6%9D%91%E5%B1%B1%E3%80%81%E7%A7%8B%E5%B1%B1 2015|八木、村山、秋山 2015]] pp.69-70</ref>と呼ばれる領域を挿入する方法である。プログラムは実行中カナリアを監視し続け、バッファオーバーフローによりカナリアが書き換わったらプログラムを停止する。
# 不当な引数があれば、エラー終了する。
# <code>memset()</code>関数を用いてバッファをゼロクリアする
# 転送バイト数に制限をかけられる<code>strncpy()</code>などの関数を使用する
等のテクニック<ref>[http://www.ipa.go.jp/security/awareness/vendor/programmingv2/clanguage.html]IPAセキュアプログラミング講座</ref>があげられる。


* [[StackGuard]]<ref name=":17" /><ref>{{cite web|url=https://www.usenix.org/publications/library/proceedings/sec98/full_papers/cowan/cowan.pdf|title=StackGuard: Automatic Adaptive Detection and Prevention of Buffer-Overflow Attacks by Cowan et al.|accessdate=2012-02-09|format=PDF}}</ref>
その他多くのプログラミング言語では、実行時に、また場合によってはコンパイル時にチェックを行い、CやC++ではプログラムがクラッシュするような場合でも、警告を送ったり[[例外処理|例外]]を上げたりできるようになっている。このような言語の例としては、[[Ada]]、[[Eiffel]]、[[LISP]]、[[Modula-2]]、[[Smalltalk]]、[[OCaml]]、およびC言語から派生した[[Cyclone]]や[[D言語]]が挙げられる。また、[[Javaプラットフォーム]]や[[.NET Framework]]では全ての配列に対して境界チェックが必須とされる。ほぼすべての[[インタプリタ|インタプリタ言語]]ではバッファオーバーフローへの対策が行われており、エラー発生時にはその状態が明確に伝えられる。境界チェックを行うのに十分な型情報を保持しているようなプログラミング言語では、境界チェックの有効・無効を切り替えるためのオプションが提供されていることもある<ref>{{cite web
* [[Microsoft Visual C++]]のGSオプション<ref name=":17" /><ref>{{cite web|url=https://docs.microsoft.com/ja-jp/cpp/build/reference/gs-buffer-security-check?view=vs-2017|title=/GS (バッファーのセキュリティ チェック)|accessdate=2012-02-09|date=2016/11/04}}</ref>
| author=Neil Moffatt
| title=Delphi Basics : $RangeChecks command
| url=http://www.delphibasics.co.uk/RTL.asp?Name=$RangeChecks
| accessdate=2012-02-03
}} 例えばDelphiでは$RangeChecksディレクティブで境界チェックの有効・無効を切り替えられる。</ref>。
ただし、インタプリタそのものは多くがコンパイラ言語によって書かれているので、絶対に信頼できるわけではない。また言語に付属するライブラリやモジュールには母体となった言語のライブラリやシステムコールに由来した脆弱性が組み込まれてしまうこともある。


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


標準Cライブラリ等にはバッファオーバーフロー検知機能が施されていない関数が収録されているので、これを検知機能を持った関数に置き換えたライブラリを標準Cライブラリの代わりに使う事で攻撃を検知できる。例えばLibsafe<ref>{{cite web|url=http://directory.fsf.org/libsafe.html|title=Libsafe - Free Software Directory|accessdate=2012-02-09}}</ref>は標準Cライブラリのstrcpy(*dest,*src)をより安全な関数に置き換えており、入力srcのサイズがコピー先のdestのサイズが大きいか否かを検知できる<ref name="yma-71-73">[[バッファオーバーラン#%E5%85%AB%E6%9C%A8%E3%80%81%E6%9D%91%E5%B1%B1%E3%80%81%E7%A7%8B%E5%B1%B1 2015|八木、村山、秋山 2015]] pp.71-73</ref>。
=== 安全なライブラリの利用 ===


=== 実行環境での対策 ===
バッファオーバーフローがC言語およびC++言語に多い原因として、これらの言語では、データ型のコンテナとしてのバッファの低レベルな実装の詳細が公開されてしまっていることが挙げられる。従って、バッファ管理を行うコード中で高いレベルの正当性を維持することができれば、バッファオーバーフローは回避できるはずだと言える。例えば、標準ライブラリ関数のうち、<code>[[gets]]</code>や<code>[[strcpy]]</code>といった境界チェックを行わない関数は使用を避けることが昔から推奨されている。<!-- 原文ではscanfも例に挙がっていましたが外しました(脚注参照)。 -->
例えば、[[Morris worm]]は[[Fingerプロトコル|fingerd]]が<code>gets</code>を使用しているのを突いて攻撃を行った<ref>http://wiretap.area.com/Gopher/Library/Techdoc/Virus/inetvir.823</ref>


==== Write XOR eXecute ====
よく書けたテスト済みの抽象データ型ライブラリにより、境界チェックを含むバッファ管理を集中化・自動化することで、
典型的なスタックベースのバッファオーバーフロー攻撃では、本来データを格納すべき領域にシェルコードやNOP命令のような実行コードを置き、これをプログラムに実行させる事で攻撃が成立する。そこでこのような攻撃を防ぐため、データを格納すべき領域では実行不可にする、'''[[:en:W^X|Write XOR eXecute]]'''<ref name="yma-71-73" />('''W<math>\boldsymbol{\oplus}</math>X'''<ref name="yma-71-73" />もしくは'''W^X'''と略す)という対策手法が知られている<ref name="yma-71-73" />。W<math>\oplus</math>XのWindowsにおける実装は[[データ実行防止|'''DEP''']]([[英語|英]]: Data Execution Prevention, データ実行防止)と呼ばれる<ref name="yma-71-73" /><ref>{{cite web|url=http://support.microsoft.com/kb/875352/ja|title=Windows XP Service Pack 2、Windows XP Tablet PC Edition 2005、および Windows Server 2003 のデータ実行防止 (DEP) 機能の詳細|accessdate=2012-02-17}}</ref>。またDEPではSEH例外ハンドラへのポインタが上書きされないように明示的に保護を行う<ref>{{cite web|url=http://www.uninformed.org/?v=2&a=4&t=txt|title=Bypassing Windows Hardware-enforced Data Execution Prevention|accessdate=2007-05-20}}</ref>。
バッファオーバーフローの発生率や影響度合いを減らすこともできる。プログラミング言語の構成要素となるデータ型のうち、バッファオーバーフローが多発するのは文字列と配列である。したがって、これらのデータ型においてバッファオーバーフローを防止することで、保護が必要となる部分の大半をカバーすることができる。だが、このような安全性を提供するライブラリの誤った使用法によりバッファオーバーフローや脆弱性を引き起こす可能性は残っている。またもちろん、これらライブラリ自身のバグも潜在的な脆弱性となる。「安全な」ライブラリの実装としては、"The Better String Library"<ref>{{cite web
一部のUNIX([[OpenBSD]]や[[macOS]]など)はW<math>\oplus</math>Xなどの実行保護が有効になった状態で出荷されている。それ以外のオプショナルなパッケージとしては以下のものが挙げられる。
| url=http://bstring.sf.net/
| title=The Better String Library
| accessdate=2012-02-08}}</ref>、
Vstr<ref>{{cite web
| url=http://www.and.org/vstr/
| title=The Vstr Homepage
| accessdate=2012-02-08}}</ref>、
Erwin<ref>{{cite web
| url=http://www.theiling.de/projects/erwin.html
| title=The Erwin Homepage
| accessdate=2012-02-08}}</ref>などが挙げられる。
[[OpenBSD]]の[[標準Cライブラリ]]では、[[strlcpy]]や[[strlcat]]といった関数が提供されているが、これらの関数の提供する安全性は上記のような実装と比べると限定されている。


*[[PaX]]<ref>{{cite web|url=http://pax.grsecurity.net|title=PaX: Homepage of the PaX team|accessdate=2012-02-17}}</ref>
2007年にはC標準委員会によるTechnical Report 24731が公開された。ここでは、C標準ライブラリの文字列関数及び入出力関数に、バッファのサイズを引数として追加したものを定義している{{cite web
| url=http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1225.pdf
| title=ISO/IEC TR 24731-1:2007
| format=PDF
| date=2007-03-28
| accessdate=2012-02-08
}}{{cite web
| url=http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1173.pdf
| title=Rationale for TR 24731 Extensions to the C Library Part I: Bounds-checking interfaces
| format=PDF
| accessdate=2012-02-08}}。
しかしながら、これらの関数がバッファオーバーフローの低減に寄与するかは疑わしい。これらの関数では関数呼び出しの度にプログラマによる判断が必要となるが、これでは標準ライブラリ中に昔からある類似の関数を呼び出す際にその都度プログラマが判断を行うのと大して変わらない<ref>{{cite web
| url=https://www.securecoding.cert.org/confluence/x/QwY
| title=CERT Secure Coding Initiative
| accessdate=2007-07-30}}</ref>。


*[[Exec Shield]]<ref>{{cite web|url=http://kerneltrap.org/node/644|title=KernelTrap.Org|accessdate=2012-02-17|archiveurl=https://archive.is/20120529183334/http://kerneltrap.org/node/644|archivedate=2012年5月29日|deadurldate=2017年9月}}</ref>
また、配列領域をチェックする機能自体は16bit時代からx86 CPUのBOUND命令などすでに実装されていたが、チェックの結果問題があった場合には例外を発生させる等扱いが難しく、現在のCPUにおいてもチェックにまつわるオーバーヘッドが無視できないという理由から、リリースバイナリにこれらの機能を含められないということもある。


*[[Openwall]]<ref>{{cite web|url=http://linux.softpedia.com/get/System/Operating-Systems/Kernels/Openwall-Linux-kernel-patch-16454.shtml|title=Openwall Linux kernel patch 2.4.34-ow1|accessdate=2012-02-17}}</ref>
=== バッファオーバーフロー保護 ===


また、[[プロプライエタリ・ソフトウェア|プロプライエタリ]]なアドオンとしては以下のものがある。
バッファオーバーフロー保護は最も多いタイプのバッファオーバーフローを検出するのに使用される。方法としては、関数から戻る際に[[コールスタック]]が変更されていないかチェックし、変更されていた場合はプログラムが[[セグメンテーション違反]]で終了する。このような処理系としては、Libsafe<ref>{{cite web
| url=http://directory.fsf.org/libsafe.html
| title=Libsafe - Free Software Directory
| accessdate=2012-02-09}}</ref>や、
[[StackGuard]]<ref>{{cite web
| url=https://www.usenix.org/publications/library/proceedings/sec98/full_papers/cowan/cowan.pdf
| title=StackGuard: Automatic Adaptive Detection and Prevention of Buffer-Overflow Attacks by Cowan et al.
| format=PDF
| accessdate= 2012-02-09}}</ref>
および[[ProPolice]]<ref>{{cite web
|url = http://wiki.x.org/wiki/ProPolice
|title = X.ORG Wiki - ProPolice
|accessdate = 2007-05-20
|archiveurl = https://web.archive.org/web/20070212032750/http://wiki.x.org/wiki/ProPolice
|archivedate = 2007年2月12日
|deadurldate = 2017年9月
}}</ref>などの[[GNUコンパイラコレクション]]へのパッチが挙げられる。


* BufferShield<ref>{{cite web|url=http://www.sys-manage.com/english/products/products_BufferShield.html|title=BufferShield: Prevention of Buffer Overflow Exploitation for Windows|accessdate=2012-02-17}}</ref>
マイクロソフトの[[データ実行防止]]モードでは、SEH例外ハンドラへのポインタが上書きされないように明示的に保護を行う<ref>{{cite web
| url=http://www.uninformed.org/?v=2&a=4&t=txt
| title=Bypassing Windows Hardware-enforced Data Execution Prevention
| accessdate=2007-05-20}}</ref>。


* StackDefender<ref>{{cite web|url=http://www.ngsec.com/ngproducts/stackdefender/|title=NGSec Stack Defender|accessdate=2012-02-17|archiveurl=https://web.archive.org/web/20070513235539/http://www.ngsec.com/ngproducts/stackdefender/|archivedate=2007年5月13日|deadurldate=2017年9月}}</ref>
より強力なスタック保護の方法としては、スタックを2つに分けて、片方をデータ用、もう片方を関数の戻りアドレス用とする方法がある。セキュリティを意図した設計によるものではないが、[[Forth]]ではこのようなスタックの分割が行われている。ともかく、この方法もバッファオーバーフローに対する完璧な解決策とは成り得ない。これは、戻りアドレスは上書きされないが、プログラムの動作に影響を与えるデータの上書きは依然として発生しうるためである。


=== ポインタ保護 ===


バッファオーバーフローは、最終的にはポインタやアドレスの操作によって効果を発揮する。PointGuardは攻撃者がポインタ及びアドレスを確実に操作するのを妨害するためのコンパイラの拡張として提案された
<ref>{{cite web
| url=http://www.usenix.org/events/sec03/tech/full_papers/cowan/cowan_html/
| title=PointGuardTM: Protecting Pointers From Buffer Overflow Vulnerabilities
| author=Crispin Cowan, Steve Beattie, John Johansen and Perry Wagle
| date=2003-08-27
| accessdate= 2012-02-14}}</ref>。
このアプローチでは、ポインタに対してXOR演算によるエンコードを行うコードをコンパイラがポインタの使用前後に自動的に追加する。これにより、攻撃者には(理論的には)ポインタのエンコード・デコードにどのような値を使用しているか分からないため、ポインタを別の値で上書きしてもそれが実際にはどこを指すのか前もって知ることができない。PointGuardが実際にリリースされることはなかったが、Microsoftは似たような機能をWindows XP SP2およびWindows Server 2003 SP1で実装している<ref>{{cite web
| url=http://blogs.msdn.com/b/michael_howard/archive/2006/01/30/520200.aspx
| title=Protecting against Pointer Subterfuge (Kinda!)
| author=Michael Howard
| date=2006-01-31
| accessdate= 2012-02-14}}</ref>。
マイクロソフトはポインタの保護機能が自動的に行われるよう実装するのではなく、プログラマが自らの判断で呼び出せるようにAPIルーチンを追加する形で実装を行った。これはパフォーマンスの面では有利(エンコード処理が常に入るのではないため)だが、使う必要のある場所がどこであるかプログラマが知っているため、プログラマには負担となる。


なお、2018年現在広く使われている[[x64]]アーキテクチャのプロセッサでは、W<math>\oplus</math>Xを実現する為にデータ領域である事を識別する'''[[NXビット]]'''という仕組みがハードウェアレベルでサポートされている<ref name=":18">{{Cite web|url=http://e-words.jp/w/NX%E3%83%93%E3%83%83%E3%83%88.html|title=NXビット|accessdate=2019/01/01|website=IT用語辞典e-Words|publisher=}}</ref>(インテルではNXビットのことをXDビットと読んでいるが同一のものである<ref name=":18" />)。
ここで、XORは線形演算であるため、攻撃者がエンコードされたポインタを操作する際にアドレス中の下位バイトのみを書き換える方法が考えられる(上位バイトは上書きされないため正しくデコードされる)。これにより、攻撃者が攻撃を複数回試行できるか、またはポインタがある範囲のうちの一箇所(例えばNOPスレッド中のある一箇所)を指せば攻撃が成功するという条件に当てはまる場合には、攻撃が可能となってしまう<ref>{{cite web
| url=http://www.usenix.org/publications/login/2005-06/pdfs/alexander0506.pdf
| title=defeating compiler-level buffer overflow protection
| author=Steven Alexander
| format=PDF
| year=2005
| month=6
| accessdate= 2012-02-14}}</ref>。
マイクロソフトでは、このアドレスの部分的な上書きに対する脆弱性への対処として、エンコード処理にランダムなローテーション処理を追加した
<ref>{{cite web
| url=http://blogs.msdn.com/b/michael_howard/archive/2006/08/16/702707.aspx
| title=Protecting against Pointer Subterfuge (Redux)
| author=Michael Howard
| date=2006-08-16
| accessdate= 2012-02-14}}</ref>。


=== 実行保護 ===
==== ASLR ====
バッファオーバーフロー攻撃を含めたメモリ破壊攻撃全般を緩和する技術として[[アドレス空間配置のランダム化|'''ASLR''']](Address Space Layout Randomization、アドレス空間配置のランダム化)がある。これは仮想メモリ空間におけるスタック領域やコード領域の位置、読み込まれるDLLの位置等を(プログラム起動時もしくはOS自身の起動時に)ランダムに変える技術で、これにより攻撃者が攻撃に有効な実行コードの特定箇所を指定してメモリ改ざんを行うのを困難にする。


ASLRはバッファオーバーフロー攻撃の発展形である[[Return-to-libc攻撃]](後述)を緩和できるが、さらにその発展形である [[:en: Return-oriented programming|Return-oriented programming]]には対抗できない。
{{Main|実行保護}}


カーネル空間のASLRを'''KASLR'''(''Kernel Address Space Layout Randomization'')といい、linuxカーネル(バージョン3.14以降)<ref>{{cite web|url=http://kernelnewbies.org/Linux_3.14#head-192cae48200fccde67b36c75cdb6c6d8214cccb3|title=Linux kernel 3.14, Section 1.7. Kernel address space randomization|accessdate=2 April 2014|date=30 March 2014|website=kernelnewbies.org}}</ref>、iOS(バージョン6以降)<ref>{{cite web|url=https://www.slideshare.net/i0n1c/csw2013-stefan-esserios6exploitation280dayslater/19-KASLR_iOS_6_introduces_KASLR|title=iOS 6 Exploitation 280 Days Later|accessdate=2019/01/01|author=Stefan Esser|publisher=|at=Slide 19, "iOS 6 introduces KASLR"}}</ref>などで実装されている。またKASLRをバイパスしようとする攻撃に対抗する為の機構として[[:en:Kernel page-table isolation|Kernel page-table isolation]]がある。
実行保護とはバッファオーバーフロー保護のアプローチの一つで、スタックやヒープ上のコードの実行を防止するものである。攻撃者がバッファオーバーフローを利用するのは任意のコードをプログラムのメモリ上に挿入するためであるが、実行保護が行われている場合は、そのようなコードを実行しようとしても例外が発生する。


[[GNUコンパイラコレクション|gccとg++]]でコンパイルとスタック領域とヒープ領域に対してはASLRを用いるが、コード領域にASLRを用いるにはオプション「-pie」を使わねばならない<ref name=":20">{{Cite web|url=https://wiki.debian.org/Hardening|title=Hardening|accessdate=2019/01/01|publisher=Devian}}</ref>。また共有ライブラリにASLRを用いるにはオプション「-fPIC」を指定する<ref name=":20" />。
一部のCPUでは[[NXビット]]("No eXecute"の略)と呼ばれる機能が提供されており、ソフトウェアと連携し、スタックやヒープの内容を含む[[ページング方式|ページ]]をマーク付けすることで、読み書きは可能だが実行は不可能であるようにし、データ領域の任意のデータをコードとして実行させないようにできる。
インテル製品では[[XDビット]]("eXecute Disabled"の略)という名前でNXビットと同じ機能を実装しており、NetBurstアーキテクチャPentium4ファミリにのIntel 64対応プロセッサ、および現在リリースされているCoreマイクロアーキテクチャの全てのプロセッサが対応している。AMD製品ではAMD64 Enhanced Virus Protectionという名前で、Opteron、Athlon 64、Turion 64、Sempronの一部製品がこれに対応している。


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


== 開発時の対策 ==
* [[PaX]]<ref>{{cite web
| title=PaX: Homepage of the PaX team
| url=http://pax.grsecurity.net
| accessdate=2012-02-17}}</ref>


=== プログラミング言語・プログラミング環境の選択 ===
* [[Exec Shield]]<ref>{{cite web
C言語やC++以外の言語ではバッファオーバーフローが発生しないよう対策が取られているものも多く、コンパイル時にバッファオーバーフローのチェックを行ったり、実行時にバッファオーバーフローに対する警告や[[例外処理|例外]]を上げたりするものもある([[Ada]]、[[Eiffel]]、[[LISP]]、[[Modula-2]]、[[Smalltalk]]、[[OCaml]]、およびC言語から派生した[[Cyclone]]や[[D言語]]など)。
|title = KernelTrap.Org
|url = http://kerneltrap.org/node/644
|accessdate = 2012-02-17
|archiveurl = https://archive.is/20120529183334/http://kerneltrap.org/node/644
|archivedate = 2012年5月29日
|deadurldate = 2017年9月
}}</ref>


[[Javaプラットフォーム]]や[[.NET Framework]]では全ての配列に対して境界チェックが必須とされる。ほぼすべての[[インタプリタ|インタプリタ言語]]ではバッファオーバーフローへの対策が行われており、エラー発生時にはその状態が明確に伝えられる。境界チェックを行うのに十分な型情報を保持しているようなプログラミング言語では、境界チェックの有効・無効を切り替えるためのオプションが提供されていることもある<ref>{{cite web|url=http://www.delphibasics.co.uk/RTL.asp?Name=$RangeChecks|title=Delphi Basics : $RangeChecks command|accessdate=2012-02-03|author=Neil Moffatt}} 例えばDelphiでは$RangeChecksディレクティブで境界チェックの有効・無効を切り替えられる。</ref>。
* [[Openwall]]<ref>{{cite web
| title=Openwall Linux kernel patch 2.4.34-ow1
| url=http://linux.softpedia.com/get/System/Operating-Systems/Kernels/Openwall-Linux-kernel-patch-16454.shtml
| accessdate=2012-02-17}}</ref>


=== ソースコード記述時の対策 ===
また、Windowsでも最近のものは[[データ実行防止]](DEP)と呼ばれる実行保護機能を備えている<ref>{{cite web
バッファオーバーフロー攻撃は主にC言語やC++を対象としたものなので、以下ではプログラミング言語としてC言語かC++を選んだ場合に対しての対策を述べる。
| title=Windows XP Service Pack 2、Windows XP Tablet PC Edition 2005、および Windows Server 2003 のデータ実行防止 (DEP) 機能の詳細
| url=http://support.microsoft.com/kb/875352/ja
| accessdate=2012-02-17}}</ref>。
また、[[プロプライエタリ・ソフトウェア|プロプライエタリ]]なアドオンとしては以下のものがある。


==== 人手による対策 ====
* BufferShield<ref>{{cite web
バッファオーバーフロー攻撃を防ぐには、領域長とデータ長を意識したプログラミングを行う事が重要である<ref name=":15">{{Cite web|url=https://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/c902.html|title=第10章 著名な脆弱性対策 バッファオーバーフロー: #2 ソースコード記述時の対策|accessdate=2018/12/27|work=セキュアプログラミング講座(2007年公開版)|publisher=情報処理推進機構}}</ref>:
| title=BufferShield: Prevention of Buffer Overflow Exploitation for Windows
| url=http://www.sys-manage.com/english/products/products_BufferShield.html
| accessdate=2012-02-17}}</ref>


* データをバッファに挿入する際には、データ長がバッファ長を超えない事を調べる検査ロジックをプログラムに書き加えておく<ref name=":15" />
* StackDefender<ref>{{cite web
* データが文字列の場合は文字列長を数え間違えないよう、文字列の終端にあるナル文字も数える<ref name=":15" />
|title = NGSec Stack Defender
* データ長に依存したループを書くときに間違ってループを回しすぎる([[Off-by-oneエラー]])事が無いようにする<ref name=":15" />
|url = http://www.ngsec.com/ngproducts/stackdefender/
* 事前にデータ長の上限がわからない場合は、バッファをmalloc等で動的に確保し、不要になったら確実にfreeする<ref name=":15" />
|accessdate = 2012-02-17
|archiveurl = https://web.archive.org/web/20070513235539/http://www.ngsec.com/ngproducts/stackdefender/
|archivedate = 2007年5月13日
|deadurldate = 2017年9月
}}</ref>


==== 安全なライブラリの使用 ====
実行保護は一般に、[[Return-to-libc攻撃]]や、その他攻撃者が作成したコードの実行に依存しないタイプの攻撃に対しては効果がない。
[[標準Cライブラリ]]を使う代わりにバッファ溢れを未然に防いだりエラーとして検出してくれるセキュアなライブラリを使う事も重要である。このようなライブラリとして
<!--
原文では
「しかしながら、[[アドレス空間配置のランダム化]]を行なっている[[64ビット]]システムでは、後述のように、実行保護によりそれらの攻撃をより難しくすることができる」
となっていたが、この内容が後ろになかったので省略
-->
また、実行保護を行なっている場合でも、バッファオーバーフローによりプログラムを異常動作させることは可能である。


* Managed String(Linux環境)<ref name=":15" />
ハーバードアーキテクチャのハードウエアとオペレーティングシステムの組み合わせを使用することでもこの保護を実現できる。
* ISO/IEC 9899:2011 Annex K (Windows環境)<ref name=":15" />
* SafeStr (Linux環境、Windows環境)<ref name=":15" />
* "The Better String Library"<ref>{{cite web|url=http://bstring.sf.net/|title=The Better String Library|accessdate=2012-02-08}}</ref>
* Vstr<ref>{{cite web|url=http://www.and.org/vstr/|title=The Vstr Homepage|accessdate=2012-02-08}}</ref>
* Erwin<ref>{{cite web|url=http://www.theiling.de/projects/erwin.html|title=The Erwin Homepage|accessdate=2012-02-08}}</ref>


などがある。また標準Cライブラリであっても[[BSD libc]]などには[[strlcpy]]や[[strlcat]]といったより安全に配慮した文字列用関数が用意されている。
=== アドレス空間配置のランダム化 ===


=== 静的コード解析時における対策 ===
{{Main|アドレス空間配置のランダム化}}


==== 人手による静的コード解析 ====
アドレス空間配置のランダム化(address space layout randomization, ASLR)はコンピュータのセキュリティ機能の一種で、攻撃の鍵となるデータの格納エリアの位置(通常は実行コードの開始位置や、ライブラリ、ヒープ、スタックなどの位置を含む)をプロセスのアドレス空間中でランダムに設定するものである。
静的コード解析の際、前述した領域長とデータ長を意識したプログラミングが行われているか再確認し、strcpy, sprintf 等のデータ領域長を意識しないライブラリ関数が使用されていないか、使用されているならその入力長の計算が間違っていないかを確認する<ref name=":16">{{Cite web|url=https://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/c903.html|title=第10章 著名な脆弱性対策 バッファオーバーフロー: #3 ソースコードの静的検査|accessdate=2018/12/27|work=セキュアプログラミング講座(2007年公開版)|publisher=情報処理推進機構}}</ref>。またヒープオーバーフロー対策としてmalloc/freeやnew/deleteの多用に注意し、関数ポインタの書き換えを防ぐために関数ポインタが多用に注意し<ref name=":16" />、攻撃パターンを見逃す事がないようデータの一部を切り捨てている関数に注意する<ref name=":16" />。
バッファオーバーフローは本来メモリ中の特定の位置にプログラムが配置されていることを前提としている点に着目した対策である。


==== 自動化された静的コード解析 ====
関数や変数に対する[[仮想記憶]]アドレスのランダム化によりバッファオーバーフローによるエクスプロイトをより困難にすることが可能と見られるが、ただし不可能とはならない。また、これにより攻撃者はエクスプロイトを個別のシステムごとに誂えなければならなくなるため、[[ワーム]]の増殖を防ぐのにも役立つ<ref>{{cite web
strcpy等のバッファオーバーフローが起こりやすい関数の使用に対して警告を出してくれる関数名照合型検査ツール<ref name=":16" />や、各種ソースコード検査ツール<ref name=":16" />を使用してバッファオーバーフロー対策を行う。開発環境の中にはソースコード検査ツールをオプションとして備えているものもあるので、それを利用する事もできる<ref name=":16" />。
| title=PaX at GRSecurity.net

| url=http://pax.grsecurity.net/docs/aslr.txt
またソースコードが手に入らない製品等を利用する場合は、[[ファジング]]ツールでブラックボックス解析する。
| accessdate=2007-06-03}}</ref>。

似た方法でより効果が弱いものとしては仮想記憶上で行うプロセスやライブラリの[[再配置]]が挙げられる。
<br />

== 発展的な攻撃 ==

=== Return-to-libc攻撃 ===
{{Main|Return-to-libc攻撃}}
既に述べたように、典型的なスタックベースのオーバーフロー攻撃では、本来データを格納すべき箇所にシェルコードやNOP命令のようなコードを置き、リターンアドレスを書き換えてこれらのコードにジャンプして、これらのコードを実行する必要があった。しかしW<math>\oplus</math>Xが実装された実行環境ではデータを格納すべき箇所におけるコード実行を不許可としているので、こうしたオーバーフロー攻撃を仕掛ける事はできない。

そこでW<math>\oplus</math>Xを回避する為に考案されたのが'''Return-to-libc攻撃'''である。この攻撃では、リターンアドレスのジャンプ先をデータ格納箇所に書き換えるのではなく、標準Cライブラリ(libc)のような共有ライブラリ・DLLにジャンプするよう書き換える。こうしたライブラリはデータ格納箇所以外に置かれているので(W<math>\oplus</math>Xが実装された環境においても)実行許可がある。そこで攻撃者はライブラリ内の関数を悪用して、攻撃を仕掛ける事ができる。


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


実行環境がASLRを実装していれば、libc等のライブラリの仮想アドレスはランダムに変わるので、攻撃者がジャンプ先をライブラリに落ちるようリターンアドレスを書き換えるのは困難になる。
{{Main|ディープパケットインスペクション}}
<br />


=== Return-to-Register攻撃 ===
ディープパケットインスペクション(deep packet inspection, DPI)を使用すれば、ネットワークの周辺から行われるバッファオーバーフローを狙った攻撃を、攻撃シグネチャとの比較や[[ヒューリスティクス]]を利用して検知することができる。これらの方法は既知の攻撃のシグネチャを持つパケットをブロックすることができる他に、長いNOP命令の列(NOPスレッド)が見つかった場合、エクスプロイトの[[ペイロード (コンピュータ)|ペイロード]]が微妙に変化しても使用することができる。
Retrun-to-Register攻撃とは「ret命令実行後にレジスタが指しているアドレスに不正な命令コードを挿入し、その上で”そのレジスタ値に実行を移す命令群が格納されているアドレス”でリターンアドレスを書き換える攻撃」<ref>[[#メモリ破損脆弱性に対する攻撃の調査と分類|メモリ破損脆弱性に対する攻撃の調査と分類]] p769</ref>のことである。


例えばレジスタAがバッファの先頭へのポインタを格納しているとすると、そのときレジスタAをオペランドにとる任意のjumpまたはcall命令が実行フローの支配権を得るのに使用できる<ref name="shah">{{cite conference|last=Shah|first=Saumil|year=2006|booktitle=Hack In The Box|url=http://conference.hitb.org/hitbsecconf2006kl/materials/DAY 1 - Saumil Shah - Writing Metasploit Plugins.pdf|title=Writing Metasploit Plugins: from vulnerability to exploit|location=Kuala Lumpur|format=PDF}}</ref>。
パケットスキャンは決して効果的な方法ではない。これは、既知の攻撃だけしか防ぐことができず、またNOPスレッドは様々な方法でエンコードできるためである。攻撃者の使う[[シェルコード]]は、ヒューリスティックなパケットスキャナや[[侵入検知システム]]を回避するため、[[英数字コード]]、[[メタモーフィックコード]]、[[自己書き換えコード]]などを使って構成される。


==== ret2esp攻撃 ====
ret2esp(Return to esp)攻撃は、2017年現在のASLRの実装のデフォルトではコード領域(.textセクション)をランダマイズしない事を利用してASLRを回避する攻撃手法である<ref name=":19">{{Cite web|url=http://www.intellilink.co.jp/article/column/ctf01.html|title=CTFで学ぶ脆弱性(スタックバッファオーバーフロー編・その1)|accessdate=2019/01/01|publisher=NTTデータ先端技術株式会社}}</ref>。ここでespは[[x86]]における[[スタックポインタ]]である。この攻撃は.textセクション内に「jmp esp」のような命令がある場合に成立する<ref name=":19" />。攻撃者はバッファオーバーフローを利用してespの(仮想アドレス空間上の)下にシェルコードを配置した上で、リターンアドレスを「jmp esp」の箇所に書き換える。するとまずリターンアドレスの書き換えにより「jmp esp」のところにジャンプし、次に「jmp esp」が実行されてespの箇所にジャンプするので、その下に配置したシェルコードが実行される事になる<ref name=":19" />。
<br />
== 歴史 ==
== 歴史 ==
バッファオーバーフローがある程度公に文書化されたのは1972年の初めで、Computer Security Technology Planning Studyで以下のように説明されている<ref>{{rp|p.61}}{{cite web |title=Computer Security Technology Planning Study |url= http://csrc.nist.gov/publications/history/ande72.pdf |format=PDF |accessdate=2007-11-02}}</ref>。
バッファオーバーフローがある程度公に文書化されたのは1972年の初めで、Computer Security Technology Planning Studyで以下のように説明されている<ref>{{rp|p.61}}{{cite web |title=Computer Security Technology Planning Study |url= http://csrc.nist.gov/publications/history/ande72.pdf |format=PDF |accessdate=2007-11-02}}</ref>。
<blockquote>"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."</blockquote>
<blockquote>"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."</blockquote>
<blockquote>「この処理を実行するコードは読み込み元と書き込み先のアドレスに対するチェックを適切に行なっておらず、モニターの一部に対しユーザによる上書きを許すことになっている。これはモニタにコードを挿入するのに利用される可能性があり、結果としてユーザがマシンの制御を掌握する可能性がある」</blockquote>
<blockquote>「この処理を実行するコードは読み込み元と書き込み先のアドレスに対するチェックを適切に行なっておらず、モニターの一部に対しユーザによる上書きを許すことになっている。これはモニタにコードを挿入するのに利用される可能性があり、結果としてユーザがマシンの制御を掌握する可能性がある」</blockquote>
''モニター''とは、現在カーネルと呼ばれているのと同じものである。
''モニター''とは、現在カーネルと呼ばれているのと同じものである。


506行目: 472行目:
[[PlayStation 2]]では同じ目的のために[[PS2 Independence Exploit]]が使用される。また[[Wii]]ではHomebrewが利用されるが、これは[[ゼルダの伝説 トワイライトプリンセス]]に存在するバッファオーバーフローを利用している。
[[PlayStation 2]]では同じ目的のために[[PS2 Independence Exploit]]が使用される。また[[Wii]]ではHomebrewが利用されるが、これは[[ゼルダの伝説 トワイライトプリンセス]]に存在するバッファオーバーフローを利用している。


== 脚注 ==
== 参考文献 ==


* {{Cite book|ref=八木、村山、秋山 2015|author=八木毅|title=コンピュータネットワークセキュリティ|date=2015/3/17|year=|accessdate=|publisher=コロナ社|isbn=978-4339024951|author2=村山純一|author3=秋山満昭}}
<references />
*{{Cite book|ref=Erickson(2011)|author=Jon Erickson|title=Hacking: 美しき策謀 第2版 ―脆弱性攻撃の理論と実際|date=2011/10/22|year=|accessdate=|publisher=オライリージャパン|isbn=978-4873115146|translator=村上雅章|author2=|author3=|author4=|author5=|author6=|author7=|author8=|author9=}}
* {{Cite web|url=https://www.ipa.go.jp/security/awareness/vendor/programmingv2/clanguage.html|title=セキュア・プログラミング講座 C/C++言語編|accessdate=2018/12/14|publisher=情報処理推進機構}}
* {{Cite web|url=http://www.intellilink.co.jp/article/column/ctf01.html|title=CTFで学ぶ脆弱性(スタックバッファオーバーフロー編・その1)|accessdate=2018/12/14|publisher=NTTデータ先端技術株式会社}}
*ヒープベースのバッファオーバーフロー攻撃
**{{Cite web|url=http://security.cs.rpi.edu/courses/binexp-spring2015/lectures/17/10_lecture.pdf|title=Heap Exploitation Modern Binary Exploitation CSCI 4968 - Spring 2015|accessdate=2018/12/18|author=Markus Gaasedelen|format=pdf|publisher=CyberSecurity group, Department of Computer Science, [[レンセラー工科大学|Rensselaer Polytechnic Institute (RPI)]]}}
**{{Cite web|url=https://www.ffri.jp/blog/2013/12/2013-12-27-2.htm|title=2013-12-27 Monthly Research 「Heap Exploitのこれまでと現状」|accessdate=2018/12/26|publisher=FFRI}}
***{{Cite web|url=https://www.ffri.jp/assets/files/monthly_research/MR201312_History_and_Current_State_of_Heap_Exploit_JPN.pdf|title=Monthly Research Heap Exploitのこれまでと現状|accessdate=2018/12/18|publisher=FFRI|format=pdf|date=2013/12/27|ref=FFRI(2013)}}
*{{Cite web|url=https://ipsj.ixsq.nii.ac.jp/ej/index.php?action=pages_view_main&active_action=repository_action_common_download&item_id=106623&item_no=1&attribute_id=1&file_no=1&page_id=13&block_id=8|title=メモリ破損脆弱性に対する攻撃の調査と分類|accessdate=2019/01/01|author=鈴木舞音、上原崇史、金子洋平、堀洋輔、馬場隆彰、齋藤孝道|format=pdf|work=Computer Security Symposium 2014|publisher=|ref=メモリ破損脆弱性に対する攻撃の調査と分類}}
*[http://raykoid666.wordpress.com/2009/11/28/remote-buffer-overflow-from-vulnerability-to-exploit-part-1/ "Discovering and exploiting a remote buffer overflow vulnerability in an FTP server"] by Raykoid666
*[http://www.phrack.org/issues/49/14.html#article "Smashing the Stack for Fun and Profit"] by Aleph One
*[http://iac.dtic.mil/iatac/download/Vol7_No4.pdf An Overview and Example of the Buffer-Overflow Exploit. pps. 16-21.]
*[https://www.securecoding.cert.org CERT Secure Coding Standards]
*[http://www.cert.org/secure-coding CERT Secure Coding Initiative]
*[http://www.cert.org/books/secure-coding Secure Coding in C and C++]
*[http://www.sans.org/reading_room/whitepapers/securecode/386.php SANS: inside the buffer overflow attack]
*[https://web.archive.org/web/20130126024851/http://www.awarenetwork.org/etc/alpha/?x=5 "Advances in adjacent memory overflows"] by Nomenumbra
*[http://www.blackhat.com/presentations/bh-usa-04/bh-us-04-silberman/bh-us-04-silberman-paper.pdf A Comparison of Buffer Overflow Prevention Implementations and Weaknesses]
*[https://web.archive.org/web/20090817230359/http://doc.bughunter.net/buffer-overflow/ More Security Whitepapers about Buffer Overflows]
*[http://www.syngress.com/book_catalog/327_SSPC/sample.pdf Chapter 12: Writing Exploits III] from ''Sockets, Shellcode, Porting & Coding: Reverse Engineering Exploits and Tool Coding for Security Professionals'' by James C. Foster (ISBN 1-59749-005-9). Detailed explanation of how to use Metasploit to develop a buffer overflow exploit from scratch.
*[http://csrc.nist.gov/publications/history/ande72.pdf Computer Security Technology Planning Study], James P. Anderson, ESD-TR-73-51, ESD/AFSC, Hanscom AFB, Bedford, MA 01731 (October 1972) [NTIS AD-758 206]


== 関連項目 ==
== 関連項目 ==


* [[実行保護]]
*[[Phrack]]
* [[NXビット]]
* [[Phrack]]
* [[Exec Shield]]
* [[Exec Shield]]
* [[バッファアンダーラン]]
* [[バッファアンダーラン]]
* [[Billion laughs]]
* [[Billion laughs]]
* [[Computer insecurity]]
* [[Computer insecurity]]
* [[コンピュータセキュリティ]]
* [[End Of File]]
* [[End Of File]]
* [[書式文字列攻撃]]
* [[書式文字列攻撃]]
525行目: 508行目:
* [[Ping of death]]
* [[Ping of death]]
* [[ポートスキャン]]
* [[ポートスキャン]]
* [[Return-to-libc攻撃]]
* [[Security focused operating systems]]
* [[Security focused operating systems]]
* [[自己書き換えコード]]
* [[自己書き換えコード]]
531行目: 513行目:
* [[スタックバッファオーバーフロー]]
* [[スタックバッファオーバーフロー]]


== 外部リンク ==
== 脚注 ==
{{脚注ヘルプ}}
* [http://raykoid666.wordpress.com/2009/11/28/remote-buffer-overflow-from-vulnerability-to-exploit-part-1/ "Discovering and exploiting a remote buffer overflow vulnerability in an FTP server"] by Raykoid666
=== 注釈 ===
* [http://www.phrack.org/issues/49/14.html#article "Smashing the Stack for Fun and Profit"] by Aleph One
{{Reflist|group=注}}
* [http://iac.dtic.mil/iatac/download/Vol7_No4.pdf An Overview and Example of the Buffer-Overflow Exploit. pps. 16-21.]
=== 出典 ===
* [https://www.securecoding.cert.org CERT Secure Coding Standards]
{{Reflist|30em}}
* [http://www.cert.org/secure-coding CERT Secure Coding Initiative]
* [http://www.cert.org/books/secure-coding Secure Coding in C and C++]
* [http://www.sans.org/reading_room/whitepapers/securecode/386.php SANS: inside the buffer overflow attack]
* [https://web.archive.org/web/20130126024851/http://www.awarenetwork.org/etc/alpha/?x=5 "Advances in adjacent memory overflows"] by Nomenumbra
* [http://www.blackhat.com/presentations/bh-usa-04/bh-us-04-silberman/bh-us-04-silberman-paper.pdf A Comparison of Buffer Overflow Prevention Implementations and Weaknesses]
* [https://web.archive.org/web/20090817230359/http://doc.bughunter.net/buffer-overflow/ More Security Whitepapers about Buffer Overflows]
* [http://www.syngress.com/book_catalog/327_SSPC/sample.pdf Chapter 12: Writing Exploits III] from ''Sockets, Shellcode, Porting & Coding: Reverse Engineering Exploits and Tool Coding for Security Professionals'' by James C. Foster (ISBN 1-59749-005-9). Detailed explanation of how to use Metasploit to develop a buffer overflow exploit from scratch.
* [http://csrc.nist.gov/publications/history/ande72.pdf Computer Security Technology Planning Study], James P. Anderson, ESD-TR-73-51, ESD/AFSC, Hanscom AFB, Bedford, MA 01731 (October 1972) [NTIS AD-758 206]

{{エクスプロイト}}
{{エクスプロイト}}



2019年1月26日 (土) 02:31時点における版

バッファオーバーラン: buffer overrun)、バッファオーバーフロー: buffer overflow)とは、コンピュータプログラムにおけるバグのひとつ、またはそれにより引き起こされた現象で、プログラムで用意されたバッファよりも大きなサイズのデータをバッファに書き込む事で、データがバッファ境界からあふれ、バッファの外側のメモリを上書きしてしまう事により、元々そのメモリにあったデータを破壊してしまうものを指す。

バッファオーバーフローは上書きされるメモリ領域がスタック領域なのかヒープ領域なのかに応じてそれぞれスタックベースのバッファオーバーフロー、ヒープベースのバッファオーバーフローと呼ばれる。 なお、名称が似ている「スタックオーバーフロー」とは別の現象であるので注意されたい。

サイバーセキュリティ/情報セキュリティの分野ではバッファオーバーフローはメモリ破壊系の脆弱性の一つとして知られ[1]、攻撃者がバッファオーバーフローのあるプログラムに意図的に悪意のあるデータ(コード)を与えることにより、コンピュータの動作を乗っ取ってしまえる可能性がある。バッファオーバーフローを悪用した攻撃をバッファオーバーフロー攻撃という[2]

バッファオーバーフローの具体例

簡単な例

以下の例では、プログラム中の隣接したアドレスに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関数も書式指定を誤った場合は同じ危険性を持っている[3]。これらの関数を実用的なプログラムで用いる場合には注意が必要である。

次のプログラムは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言語の標準文字列処理関数の多くにも同様の問題(脆弱性)がある。


バッファオーバーフロー攻撃

情報セキュリティ/サイバーセキュリティにおいてバッファオーバーフロー攻撃は、バッファオーバーフローの脆弱性を利用した情報セキュリティ/サイバーセキュリティに対する攻撃である。バッファオーバーフローの脆弱性は整数オーバーフロー書式文字列バグUse-After-Freeなどと同様、メモリ破壊系の脆弱性に相当する[1]

共通脆弱性タイプCWEには、

番号 名称
CWE-120 入力サイズをチェックしないバッファのコピー(古典的バッファオーバーフロー)[4]
CWE-121 スタックベースのバッファオーバーフロー[5]
CWE-122 ヒープベースのバッファオーバーフロー[6]

などが登録されており、これら3つはいずれも「CWE-119: メモリバッファの境界内における操作の不適切な制限」[7]に属している[7][8]

これら3つのバッファオーバーフロー脆弱性が頻繁に生じるのはC言語C++であり[4][5][6]、古典的バッファオーバーフローはアセンブリ言語でも生じる[4]

これら3種類のバッファオーバーフローはセキュリティポリシーの外側で任意のコードを攻撃者に実行可能にする事が頻繁にある[4][5][6]。さらに任意のコードの実行により他のセキュリティサービス機構を破壊する事も可能になる[4][5][6]。またこれら3種類のバッファオーバーフローはクラッシュの原因にもなるので[4][5][6]、意図的にクラッシュさせる攻撃が可能になる[4][5][6]

CWEでは「CWE-193: Off-by-oneエラー[9]がバッファオーバーフローの原因になると述べられており[10]、整数オーバーフローもバッファオーバーフローの原因になる事が述べられている[11]

古典的バッファオーバーフロー攻撃

バッファAの値を他のバッファBにコピーするとき、BのバッファサイズがAのバッファサイズよりも大きいことをチェックしない場合に生じる脆弱性である[4]。この脆弱性は、プログラマーがこのようなチェックの実装を怠った事により生じる[4]。これはプログラマーが最低限のセキュリティチェックすらしていないことを強く示唆する[4]

脆弱性の具体例としては、例えばC言語C++において、配列サイズをチェックする事なく、配列にstrcpyscanfで文字列等をコピーするといったものがある[4]。攻撃者は意図的に大きな入力をstrcpyscanfgetsに与えることで、古典的バッファオーバーフローを不正に生じさせる事ができる[4]

例えば攻撃者がバッファAをバッファオーバーフローさせる事により、Aの隣りにある変数xを不正に変更できた場合、xがセキュリティ上重要なデータ(例えば管理者権限を持っているか否かを判定するビットを保持している)であれば、セキュリティ上重要な問題が生じる。


スタックベースのバッファオーバーフロー攻撃

スタックベースのバッファオーバーフローは「上書きされるバッファがスタック(すなわち、局所変数や、まれに関数のパラメータ)にアロケートされる」[5]事が可能な場合に生じる脆弱性である。このような脆弱性はファジングを使用して発見されることが多い[12]

C言語・C++におけるメモリ領域

スタックベースのバッファオーバーフローについて説明するためにプロセスのメモリ利用方法を復習する。OSは各ユーザプロセスに仮想アドレス空間を割り振り、WindowsLinuxなどのOSでは上位のアドレスをカーネルが使うカーネル空間とし、残りをユーザプロセス自身が用いるユーザ空間とする[注 1]。カーネル空間はユーザプロセスがアクセスする事はできず、通常のプログラミングでは意識する事はない。

C言語C++で書かれたプログラムの場合、ユーザ空間をさらに分割する。これらの言語で書かれたプログラムでは、仮想アドレスの最低位の箇所から順にプログラムの実行コードを置くコード領域(テキスト領域とも)、初期化された静的変数・大域変数を置くデータ領域、初期化されていない静的変数・大域変数を置くbss領域[注 2]malloc等で動的に確保されたメモリを置く(可変サイズの)ヒープ領域を確保する[13]。(なおデータ領域とbss領域を合わせて静的領域という)。

一方、ユーザ空間における仮想アドレスの最高位の箇所は関数のコールスタックを保存する(可変サイズの)スタック領域として用いられる[13]

最低位         …          最高位
コード領域 静的領域 ヒープ領域

(高位に向かって成長→)

スタック領域

(←低位に向かって成長)

データ領域 bss領域

スタック領域はプロセス中で呼ばれる関数のコールスタックを格納する領域で、コールスタック中の各関数のデータ(スタックフレームという)を並べて格納している[14]。プロセス中で関数fが関数gを呼び出した場合、コールスタックは以下のようになる[14][15]

低位アドレス                             … 高位アドレス
gのスタックフレーム fのスタックフレーム
gの処理に必要な一時的な情報 gの局所変数 gのSFP gのリターンアドレス gの引数の値 fの処理に必要な一時的な情報


プロセスで現在実行中の関数のスタックフレームの位置を覚えるためにプロセッサによって用いられるのがフレームポインタ(=x86ではebp)で具体的には(現在実行中の関数がgであれば)gのSFPのアドレスを指している。SFPは関数呼び出し時に呼び出し元の関数のフレームポインタのアドレスを覚えるための領域で、fがgを呼び出した際、スタックフレームの値(=fのSFPのアドレス)をgのSFPに格納する。なお、SPFは「Saved Frame Pointer」の略で日本語訳は「退避されたフレームポインタ」である[14]

一方gのリターンアドレスは呼び出し元関数fのプログラムカウンタ(命令ポインタとも。x86ではeip)のアドレスを格納する[14]

攻撃の基本的アイデア

今例えば、関数gはユーザから(ASCIIコードの)文字列を入力を受け取り、入力された文字列を配列char A[10]に格納するとする。Aのサイズは10であるので、関数gのプログラマはユーザから受け取った文字列をAに格納する前に、その文字列が本当に10文字以下なのかをチェックする機構をgに実装しておかねばならない。このようなチェック機構を実装するのを忘れていた場合、悪意のあるユーザ(以下、「攻撃者」と呼ぶ)によりスタックベースのバッファオーバーフロー攻撃を受けてしまう危険がある。


具体的には、攻撃者は以下のような文字列をgに入力する:

シェルコード)…(シェルコードの仮想アドレス

ここでシェルコードとは、何らかの悪意のある短いプログラムで、例えば攻撃者のためにバックドアを開けたり、マルウェアのダウンロードを行ったりする[15]

この文字列が配列Aの先頭から順に書き込まれていくと、A[i]のアドレスはiが大きいほど大きくなるので、攻撃者が入力文字列の長さを適切に選べば、アドレス空間には以下のようにデータが書き込まれ、gのリターンアドレスがシェルコードの仮想アドレスに上書きされる事になる[15]

低位アドレス                             … 高位アドレス
gのスタックフレーム fのスタックフレーム
gの処理に必要な一時的な情報 gの局所変数 gのSFP gのリターンアドレス gの引数の値 fの処理に必要な一時的な情報
(シェルコード)… (シェルコードの仮想アドレス)

よって関数gが終了したとき、gのリターンアドレス(の箇所に上書きされたシェルコードの仮想アドレス)が読み込まれるので、プログラムカウンタはシェルコードの位置にジャンプし、攻撃者の狙い通り、シェルコードが実行される事になる[15]

NOPスレッド

上で述べた攻撃のアイデアが実行可能であるためには、攻撃者がリターンアドレスに上書きすべき想アドレスを正確に知り、それを関数gに入力する必要があるが、スタックは動的に変化するため、これは容易ではない[16]。そこで本節ではリターンアドレスに上書きすべき仮想アドレスの「おおよその値」さえ分かれば攻撃が可能になるテクニック(NOPスレッド)を述べる。

NOPとは「何も行わない」事を意味するアセンブリ命令で[16]、本来はタイミングを合わせるなどの動機により何もせずにCPU時間を消費するために用いられる[16]。NOPスレッド(sled、そり)[16]とはこのNOP命令を複数個並べたもので、これを利用する事により攻撃対象の関数をシェルコードの位置まで滑走させる。

具体的には攻撃者は下記のような文字列を攻撃対象の関数gに入力する:

NOP … NOP (シェルコード)(戻りアドレス)…(戻りアドレス)

最初の「NOP … NOP」の部分がNOPスレッドである。攻撃者がNOPスレッドの長さや戻りアドレスの繰り返し回数を適切に選んでgに入力すると、スタック領域は例えば以下のようになる(攻撃に関係する部分だけ抜書き):

                            gのスタックフレーム fのスタックフレーム
gの局所変数 gのSFP gのリターンアドレス
NOP… NOP NOPNOP (シェルコード)…(戻りアドレス)…(戻りアドレス) (戻りアドレス) (戻りアドレス)…

これでgのリターンアドレスは「戻りアドレス」にセットされるので、攻撃者が「戻りアドレス」としてNOPスレッド部分のアドレスを指定する事に事前に成功していれば、gの終了時にNOPスレッドへとプログラムカウンタが移動する。するとプログラムはNOPを順に実行して右へ右へと移動し、シェルコードの位置にたどり着いてシェルコードが実行されるので、攻撃成功となる[16][17]。戻りアドレスがNOPスレッドのどこかに落ちさえすればよいので、前節で述べた攻撃違い、リターンアドレスにセットする値を完璧に制御する必要はなく、NOPスレッドの長さ分の誤差が発生しても攻撃が成功する事になる。

NOPスレッドは頻繁に使用されるため、多くの侵入防止システムベンダーでシェルコードの判定に使用されている。このため、エクスプロイトの作成者側では、シェルコードの実行に影響を及ぼさない(NOP以外の)任意の命令をランダムに選んでスレッドを構成することが常套手段となっている[18]

戻りアドレスの値の予想

攻撃を実行するには、あとは「戻りアドレス」として具体的にどの程度の値を代入すればよいかを知ればよい。しかし攻撃の標的となる組織の環境で戻りアドレスの絶対アドレスがいくつ程度の値になるのかを事前に知る事は難しい。そこで相対アドレスを利用して戻りアドレスを適切に選ぶ攻撃テクニックを紹介する。この攻撃のシナリオでは、攻撃者はシェルコードを含んだ攻撃用のプログラムh(をコンパイルして作った実行コード)を攻撃の標的となる組織に送りつけ、hのサブルーチンとしてgを呼び出す事で攻撃を行う。


この攻撃用プログラムhでは、変数xを宣言が宣言されているものとする。hが標的の環境でgを呼び出したとき、gのスタックフレームはスタック領域上でhのスタックフレームのすぐ隣に配置される事から、攻撃用文字列を入れ込むgの変数の絶対アドレスvar_addは

var_add = &x - (小さな値)

になるはずである[16]。ここで「&x」はxのアドレスを表す。既に述べたようにNOPスレッドを使った攻撃では戻りアドレスとしてvar_add近辺の値を選べば成功するので、攻撃者はこの「小さな値」を決定しさえすればよい。

よって攻撃者は関数gの実行コードを事前に入手し、(シェルスクリプト等を使って)NOPスレッドの長さや戻りアドレスを変えながら攻撃対象のプログラムを何度も実行することで適切な「小さな値」を選び、その「小さな値」を攻撃用プログラムhに書き込んでおけばよい[16]

埋め込めるコード量が小さい場合の対処

関数gに埋め込む攻撃用の文字列は「NOPスレッド+シェルコード+戻りアドレスの繰り返し」という形をしており、gのリターンアドレスが「戻りアドレスの繰り返し」の部分に落ちない限り攻撃は成功しないので、関数gに攻撃用文字列を埋め込む箇所とgのリターンアドレスとが(仮想アドレス空間上で)あまりに近い場合は、攻撃に必要な長さのシェルコードを埋め込めないという問題が攻撃者に生じる。

しかし攻撃者が攻撃の標的となるマシンの環境変数を設定できる状況下では、攻撃者はこの問題を回避した攻撃が可能である。標的マシンでプロセスが実行される際には、そのプロセスの仮想アドレス空間に環境変数が読み込まれるので、攻撃者が事前に標的マシンの環境変数に「NOPスレッド+シェルコード」を書き込んでおけば、プロセスの仮想アドレスに「NOPスレッド+シェルコード」ができあがる事になる。プロセス中で関数gが実行された際、攻撃者は攻撃用文字列をgに入力して、リターンアドレスをそのNOPスレッドに書き換えれば攻撃が成功する事になる[19]

より確実な攻撃方法として、攻撃プログラムhの中に環境変数を読み込む関数(getenv()等)を用いるものもある[20]

ヒープスプレー

NOPスレッドを長くしすぎると、gのリターンアドレスの位置にすらNOPが書き込まれてしまって攻撃に失敗する為、NOPスレッドを長くして攻撃成功率を上げる手法には限界がある。しかし攻撃者がプロセスのヒープ領域の値をも自由に操れるという条件下では、攻撃者はヒープスプレーというテクニックを用いる事により、この限界を突破した攻撃を行う事が可能になる[21]

ヒープスプレーでは、NOPスレッドとシェルコードを、スレッド領域ではなくヒープ領域に埋め込み、戻りアドレスとしてヒープ領域中のNOPスレッドを指定する[21]。ヒープ領域上のNOPスレッドにはスレッド領域のNOPスレッドと違い前述した長さの上限が存在しないため、「長いNOPスレッド+シェルコード」の組み合わせを大量にヒープ中に書き込むことで攻撃成功率を上げる[21]

ウェブブラウザではJavaScriptなどのクライアントサイドスクリプトにより任意の長さのヒープを作成できるので、ブラウザを対象にしたドライブバイダウンロード攻撃ではヒープスプレーが使われる事が多い[21]

トランポリング

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


ヒープベースのバッファオーバーフロー


mallocなどでヒープ領域に動的にメモリを確保する関数に対するオーバーフロー攻撃である[6]。基本的な攻撃手法としては、関数がヒープに確保したメモリ領域が2つあるとき、そのうち一方の領域に対して確保済みのメモリサイズより大きなデータを入力する事でオーバーフローを起こし、もう一方のメモリ領域を書き換えるというものである[23]。mallocは複雑な方法でメモリ確保の場所を決定しているものの、連続して2度mallocした場合にはその結果として確保される2つのメモリ領域は(仮想アドレス空間上で)近くにある傾向があるため、上述のようなバッファオーバーフロー攻撃が可能になる。

mallocのメモリ管理

より高度なヒープベースバッファオーバーフロー攻撃手法を説明する為の準備として、mallocのメモリ管理方法を説明する。なおメモリ管理方法の詳細はmallocの実装に依存するため、実行環境によって細かなところは下記の説明と異なる部分があるので注意されたい。

mallocは未使用なヒープ領域(の一部)をメモリプールとして管理しており、メモリプールは複数のchunkと呼ばれる単位からなっている[24]。プログラム中でmallocが実行されるたびに、管理しているchunkの中から適切なサイズのものをプログラムに返す[24]。適切なサイズのchunkがない場合は、システムコールにより新たなchunkを確保してプログラムに返す[24]。mallocはchunkを(サイズ毎に異なる複数の[25]連結リストとして管理しており[24]、chunkをプログラムに渡す際にこの連結リストからchunkを削除し、プログラムがメモリ領域をfreeすると、freeされたchunkが連結リストに加わる[24]

chunkは連結リストとして管理されているので、各chunkには「次のchunk」を指定するポインタ(windowsでは「flink」[26]、linuxでは「fd」[24])や「前のchunk」を指定するポインタ(windowsでは「blink」[26]、linuxでは「bk」[24])がある。

未使用chunkと隣接するメモリ領域が開放された場合は、開放されたメモリ領域と未使用chunkとを連結(coalesce)する事で1つの大きなchunkを作って管理する[26]

mallocのメタデータの書き換え

より高度なヒープベースバッファオーバーフロー攻撃として、mallocがメモリ管理に使うメタデータを書き換える手法がある。例えばXP SP1ないしそれ以前のWindowsでは、mallocしたメモリ領域をオーバーフローさせる事で、そのメモリ領域の(仮想アドレス空間で)隣りにある未使用chunkのflinkやblinkを任意のアドレスに書き換えるという攻撃手法が可能あった[27]。flinkやblinkはcoalesceのタイミングでmallocにより参照されるので攻撃が成功する[27]

バッファオーバーフローの結果

write-what-where状態

バッファオーバーフロー攻撃の結果として、write-what-where状態("write-what-where condition"[28]、CWE-123[28]。任意の場所に任意の値を書き込むことができる状態[29])になる危険がある[28]

write-what-where状態になると、セキュリティポリシーのスコープ外にメモリのデータを書き込むことができる[28]。セキュリティポリシーのスコープ外にコードを置くことで、攻撃者はほぼ確実に任意のコードを実行できるようになる[28]。管理者権限を制御しているフラグ等が書き換えられる場合も、攻撃者は任意のコードが実行可能になる[28]

プログラムのフリーズ・クラッシュ

攻撃者は意図的にバッファオーバーフローを起こすによりプログラムをクラッシュさせたり[4][5][6]、処理を書き換えて無限ループに追い込むことでプログラムをフリーズさせてたり[4][5][6]する事でプログラムの可用性を侵害できる。

関数ポインタの書き換え

古典的バッファオーバーフロー[30]やヒープオーバーフロー[6]などの結果として、関数ポインタの書き換えが可能になるケースがある。攻撃者はnmコマンドを用いる事でプログラム中で用いられている様々な関数のアドレスを知る事ができるので[30]、nmの結果を参照して攻撃に利用可能な関数に関数ポインタの値を書き換えられる。


技術的対策

コンパイラやライブラリによる対策

カナリア

バッファオーバーフローを検出するコードをコンパイル時に実行コードに挿入する手法がある。典型的手法としては、ローカル変数とSFPの間に、カナリア(canary)[31][32]もしくはクッキー[33]と呼ばれる領域を挿入する方法である。プログラムは実行中カナリアを監視し続け、バッファオーバーフローによりカナリアが書き換わったらプログラムを停止する。

安全なライブラリへの置き換え

標準Cライブラリ等にはバッファオーバーフロー検知機能が施されていない関数が収録されているので、これを検知機能を持った関数に置き換えたライブラリを標準Cライブラリの代わりに使う事で攻撃を検知できる。例えばLibsafe[36]は標準Cライブラリのstrcpy(*dest,*src)をより安全な関数に置き換えており、入力srcのサイズがコピー先のdestのサイズが大きいか否かを検知できる[37]

実行環境での対策

Write XOR eXecute

典型的なスタックベースのバッファオーバーフロー攻撃では、本来データを格納すべき領域にシェルコードやNOP命令のような実行コードを置き、これをプログラムに実行させる事で攻撃が成立する。そこでこのような攻撃を防ぐため、データを格納すべき領域では実行不可にする、Write XOR eXecute[37](WX[37]もしくはW^Xと略す)という対策手法が知られている[37]。WXのWindowsにおける実装はDEP: Data Execution Prevention, データ実行防止)と呼ばれる[37][38]。またDEPではSEH例外ハンドラへのポインタが上書きされないように明示的に保護を行う[39]。 一部のUNIX(OpenBSDmacOSなど)はWXなどの実行保護が有効になった状態で出荷されている。それ以外のオプショナルなパッケージとしては以下のものが挙げられる。

また、プロプライエタリなアドオンとしては以下のものがある。


なお、2018年現在広く使われているx64アーキテクチャのプロセッサでは、WXを実現する為にデータ領域である事を識別するNXビットという仕組みがハードウェアレベルでサポートされている[45](インテルではNXビットのことをXDビットと読んでいるが同一のものである[45])。

ASLR

バッファオーバーフロー攻撃を含めたメモリ破壊攻撃全般を緩和する技術としてASLR(Address Space Layout Randomization、アドレス空間配置のランダム化)がある。これは仮想メモリ空間におけるスタック領域やコード領域の位置、読み込まれるDLLの位置等を(プログラム起動時もしくはOS自身の起動時に)ランダムに変える技術で、これにより攻撃者が攻撃に有効な実行コードの特定箇所を指定してメモリ改ざんを行うのを困難にする。

ASLRはバッファオーバーフロー攻撃の発展形であるReturn-to-libc攻撃(後述)を緩和できるが、さらにその発展形である Return-oriented programmingには対抗できない。

カーネル空間のASLRをKASLRKernel Address Space Layout Randomization)といい、linuxカーネル(バージョン3.14以降)[46]、iOS(バージョン6以降)[47]などで実装されている。またKASLRをバイパスしようとする攻撃に対抗する為の機構としてKernel page-table isolationがある。

gccとg++でコンパイルとスタック領域とヒープ領域に対してはASLRを用いるが、コード領域にASLRを用いるにはオプション「-pie」を使わねばならない[48]。また共有ライブラリにASLRを用いるにはオプション「-fPIC」を指定する[48]


開発時の対策

プログラミング言語・プログラミング環境の選択

C言語やC++以外の言語ではバッファオーバーフローが発生しないよう対策が取られているものも多く、コンパイル時にバッファオーバーフローのチェックを行ったり、実行時にバッファオーバーフローに対する警告や例外を上げたりするものもある(AdaEiffelLISPModula-2SmalltalkOCaml、およびC言語から派生したCycloneD言語など)。

Javaプラットフォーム.NET Frameworkでは全ての配列に対して境界チェックが必須とされる。ほぼすべてのインタプリタ言語ではバッファオーバーフローへの対策が行われており、エラー発生時にはその状態が明確に伝えられる。境界チェックを行うのに十分な型情報を保持しているようなプログラミング言語では、境界チェックの有効・無効を切り替えるためのオプションが提供されていることもある[49]

ソースコード記述時の対策

バッファオーバーフロー攻撃は主にC言語やC++を対象としたものなので、以下ではプログラミング言語としてC言語かC++を選んだ場合に対しての対策を述べる。

人手による対策

バッファオーバーフロー攻撃を防ぐには、領域長とデータ長を意識したプログラミングを行う事が重要である[50]

  • データをバッファに挿入する際には、データ長がバッファ長を超えない事を調べる検査ロジックをプログラムに書き加えておく[50]
  • データが文字列の場合は文字列長を数え間違えないよう、文字列の終端にあるナル文字も数える[50]
  • データ長に依存したループを書くときに間違ってループを回しすぎる(Off-by-oneエラー)事が無いようにする[50]
  • 事前にデータ長の上限がわからない場合は、バッファをmalloc等で動的に確保し、不要になったら確実にfreeする[50]

安全なライブラリの使用

標準Cライブラリを使う代わりにバッファ溢れを未然に防いだりエラーとして検出してくれるセキュアなライブラリを使う事も重要である。このようなライブラリとして

  • Managed String(Linux環境)[50]
  • ISO/IEC 9899:2011 Annex K (Windows環境)[50]
  • SafeStr (Linux環境、Windows環境)[50]
  • "The Better String Library"[51]
  • Vstr[52]
  • Erwin[53]

などがある。また標準CライブラリであってもBSD libcなどにはstrlcpystrlcatといったより安全に配慮した文字列用関数が用意されている。

静的コード解析時における対策

人手による静的コード解析

静的コード解析の際、前述した領域長とデータ長を意識したプログラミングが行われているか再確認し、strcpy, sprintf 等のデータ領域長を意識しないライブラリ関数が使用されていないか、使用されているならその入力長の計算が間違っていないかを確認する[54]。またヒープオーバーフロー対策としてmalloc/freeやnew/deleteの多用に注意し、関数ポインタの書き換えを防ぐために関数ポインタが多用に注意し[54]、攻撃パターンを見逃す事がないようデータの一部を切り捨てている関数に注意する[54]

自動化された静的コード解析

strcpy等のバッファオーバーフローが起こりやすい関数の使用に対して警告を出してくれる関数名照合型検査ツール[54]や、各種ソースコード検査ツール[54]を使用してバッファオーバーフロー対策を行う。開発環境の中にはソースコード検査ツールをオプションとして備えているものもあるので、それを利用する事もできる[54]

またソースコードが手に入らない製品等を利用する場合は、ファジングツールでブラックボックス解析する。


発展的な攻撃

Return-to-libc攻撃

既に述べたように、典型的なスタックベースのオーバーフロー攻撃では、本来データを格納すべき箇所にシェルコードやNOP命令のようなコードを置き、リターンアドレスを書き換えてこれらのコードにジャンプして、これらのコードを実行する必要があった。しかしWXが実装された実行環境ではデータを格納すべき箇所におけるコード実行を不許可としているので、こうしたオーバーフロー攻撃を仕掛ける事はできない。

そこでWXを回避する為に考案されたのがReturn-to-libc攻撃である。この攻撃では、リターンアドレスのジャンプ先をデータ格納箇所に書き換えるのではなく、標準Cライブラリ(libc)のような共有ライブラリ・DLLにジャンプするよう書き換える。こうしたライブラリはデータ格納箇所以外に置かれているので(WXが実装された環境においても)実行許可がある。そこで攻撃者はライブラリ内の関数を悪用して、攻撃を仕掛ける事ができる。


実行環境がASLRを実装していれば、libc等のライブラリの仮想アドレスはランダムに変わるので、攻撃者がジャンプ先をライブラリに落ちるようリターンアドレスを書き換えるのは困難になる。

Return-to-Register攻撃

Retrun-to-Register攻撃とは「ret命令実行後にレジスタが指しているアドレスに不正な命令コードを挿入し、その上で”そのレジスタ値に実行を移す命令群が格納されているアドレス”でリターンアドレスを書き換える攻撃」[55]のことである。

例えばレジスタAがバッファの先頭へのポインタを格納しているとすると、そのときレジスタAをオペランドにとる任意のjumpまたはcall命令が実行フローの支配権を得るのに使用できる[56]

ret2esp攻撃

ret2esp(Return to esp)攻撃は、2017年現在のASLRの実装のデフォルトではコード領域(.textセクション)をランダマイズしない事を利用してASLRを回避する攻撃手法である[57]。ここでespはx86におけるスタックポインタである。この攻撃は.textセクション内に「jmp esp」のような命令がある場合に成立する[57]。攻撃者はバッファオーバーフローを利用してespの(仮想アドレス空間上の)下にシェルコードを配置した上で、リターンアドレスを「jmp esp」の箇所に書き換える。するとまずリターンアドレスの書き換えにより「jmp esp」のところにジャンプし、次に「jmp esp」が実行されてespの箇所にジャンプするので、その下に配置したシェルコードが実行される事になる[57]

歴史

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

"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であった[59]。 1995年、Thomas Lopaticはそれとは独立にバッファオーバーフローを発見し、セキュリティに関するメーリングリストBugtraqへ投稿した[60]。 1996年、エリアス・レヴィ(ハンドルネームAleph one)はオンラインマガジンPhrackで記事"Smashing the Stack for Fun and Profit"を発表した[61]。 これはスタックベースのバッファオーバーフローを使用したエクスプロイトを手順を追って説明していく内容である。

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

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

参考文献

関連項目

脚注

注釈

  1. ^ i386の場合、Windowsなら仮想空間の上位2GB、Linuxなら仮想空間の上位1GBがカーネル空間になる。なお本節で書いたメモリ箇所はいずれも後述するセキュリティ技術アドレス空間配置のランダム化(ASLR)を用いていない場合の話である。
  2. ^ Block Started by Symbolというアセンブラの疑似命令に由来する

出典

  1. ^ a b 八木、村山、秋山 2015 p.59
  2. ^ 第10章 著名な脆弱性対策バッファオーバーフロー: #1 概要”. セキュアプログラミング講座 C/C++言語編 旧2007年公開版. 情報処理推進機構. 2018年12月14日閲覧。
  3. ^ [迷信] scanf ではバッファオーバーランを防げない”. C/C++迷信集. 株式会社きじねこ. 2010年2月28日閲覧。 “書式指定が不適切なために発生する脆弱性であって、scanf の問題ではありません。”
  4. ^ a b c d e f g h i j k l m n CWE-120: Buffer Copy without Checking Size of Input ('Classic Buffer Overflow')”. Mitre. 2018年12月18日閲覧。
  5. ^ a b c d e f g h i CWE-121: Stack-based Buffer Overflow”. Mitre. 2018年12月18日閲覧。
  6. ^ a b c d e f g h i j CWE-122: Heap-based Buffer Overflow”. Mitre. 2018年12月18日閲覧。
  7. ^ a b CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer”. Mitre. 2018年12月18日閲覧。
  8. ^ CWE-787: Out-of-bounds Write”. Mitre. 2018年12月18日閲覧。
  9. ^ CWE-193: Off-by-one Error”. Mitre. 2018年12月18日閲覧。
  10. ^ CWE-131: Incorrect Calculation of Buffer Size”. 2018/12/18. 2018年12月18日閲覧。
  11. ^ CWE-680: Integer Overflow to Buffer Overflow”. Mitre. 2018年12月18日閲覧。
  12. ^ The Exploitant - Security info and tutorials”. 2009年11月29日閲覧。
  13. ^ a b Erickson(2011) p.87-88
  14. ^ a b c d Erickson(2011) p.84
  15. ^ a b c d 八木、村山、秋山 2015 pp.60-61
  16. ^ a b c d e f g Erickson(2011) p.161-164
  17. ^ 八木、村山、秋山 2015 p.64
  18. ^ Akritidis, P. (2005). "STRIDE: Polymorphic Sled Detection through Instruction Sequence Analysis." (PDF). Proceedings of the 20th IFIP International Information Security Conference (IFIP/SEC 2005). IFIP International Information Security Conference. {{cite conference}}: 不明な引数|coauthors=は無視されます。(もしかして:|author=) (説明)
  19. ^ Erickson(2011) p.164-168
  20. ^ Erickson(2011) p.168-173
  21. ^ a b c d 八木、村山、秋山 2015 pp.65-67
  22. ^ The Metasploit Opcode Database”. 2007年5月12日時点のオリジナルよりアーカイブ。2007年5月15日閲覧。
  23. ^ Erickson(2011) pp.173-179
  24. ^ a b c d e f g 技術本部 クラウド基盤エキスパート  角馬 文彦 (2007年11月30日). “malloc(3)のメモリ管理構造”. VA Linux Systems Japan. 2018年12月26日閲覧。
  25. ^ Doug Lea. “A Memory Allocator”. 2018年12月26日閲覧。
  26. ^ a b c FFRI(2013) p7
  27. ^ a b FFRI(2013) p8
  28. ^ a b c d e f CWE-123: Write-what-where Condition”. Mitre. 2018年12月26日閲覧。
  29. ^ JVNDB-2015-004721 Silicon Integrated Systems WindowsXP Display Manager における権限を取得される脆弱性”. JVN iPedia. 2018年12月26日閲覧。
  30. ^ a b Erickson(2011) pp.179-192
  31. ^ 芝国雄 (2006年9月5日). “第14回:バッファオーバーフローとサーバ側のセキュリティ対策を考える (2/3)”. ThinkIT. オープンソースの適用可能性を示す. 2019年1月1日閲覧。
  32. ^ David Wheeler (2004年1月27日). “セキュアなプログラマー バッファー・オーバーフローに対抗する 今日最大の脆弱性を防止する”. IBM. 2019年1月1日閲覧。
  33. ^ a b c 八木、村山、秋山 2015 pp.69-70
  34. ^ StackGuard: Automatic Adaptive Detection and Prevention of Buffer-Overflow Attacks by Cowan et al.” (PDF). 2012年2月9日閲覧。
  35. ^ /GS (バッファーのセキュリティ チェック)” (2016年11月4日). 2012年2月9日閲覧。
  36. ^ Libsafe - Free Software Directory”. 2012年2月9日閲覧。
  37. ^ a b c d e 八木、村山、秋山 2015 pp.71-73
  38. ^ Windows XP Service Pack 2、Windows XP Tablet PC Edition 2005、および Windows Server 2003 のデータ実行防止 (DEP) 機能の詳細”. 2012年2月17日閲覧。
  39. ^ Bypassing Windows Hardware-enforced Data Execution Prevention”. 2007年5月20日閲覧。
  40. ^ PaX: Homepage of the PaX team”. 2012年2月17日閲覧。
  41. ^ KernelTrap.Org”. 2012年5月29日時点のオリジナルよりアーカイブ。2012年2月17日閲覧。
  42. ^ Openwall Linux kernel patch 2.4.34-ow1”. 2012年2月17日閲覧。
  43. ^ BufferShield: Prevention of Buffer Overflow Exploitation for Windows”. 2012年2月17日閲覧。
  44. ^ NGSec Stack Defender”. 2007年5月13日時点のオリジナルよりアーカイブ。2012年2月17日閲覧。
  45. ^ a b NXビット”. IT用語辞典e-Words. 2019年1月1日閲覧。
  46. ^ Linux kernel 3.14, Section 1.7. Kernel address space randomization”. kernelnewbies.org (30 March 2014). 2 April 2014閲覧。
  47. ^ Stefan Esser. “iOS 6 Exploitation 280 Days Later”. 2019年1月1日閲覧。
  48. ^ a b Hardening”. Devian. 2019年1月1日閲覧。
  49. ^ Neil Moffatt. “Delphi Basics : $RangeChecks command”. 2012年2月3日閲覧。 例えばDelphiでは$RangeChecksディレクティブで境界チェックの有効・無効を切り替えられる。
  50. ^ a b c d e f g h 第10章 著名な脆弱性対策 バッファオーバーフロー: #2 ソースコード記述時の対策”. セキュアプログラミング講座(2007年公開版). 情報処理推進機構. 2018年12月27日閲覧。
  51. ^ The Better String Library”. 2012年2月8日閲覧。
  52. ^ The Vstr Homepage”. 2012年2月8日閲覧。
  53. ^ The Erwin Homepage”. 2012年2月8日閲覧。
  54. ^ a b c d e f 第10章 著名な脆弱性対策 バッファオーバーフロー: #3 ソースコードの静的検査”. セキュアプログラミング講座(2007年公開版). 情報処理推進機構. 2018年12月27日閲覧。
  55. ^ メモリ破損脆弱性に対する攻撃の調査と分類 p769
  56. ^ Shah, Saumil (2006). 1 - Saumil Shah - Writing Metasploit Plugins.pdf "Writing Metasploit Plugins: from vulnerability to exploit" (PDF). Hack In The Box. Kuala Lumpur. {{cite conference}}: |url=の値が不正です。 (説明)
  57. ^ a b c CTFで学ぶ脆弱性(スタックバッファオーバーフロー編・その1)”. NTTデータ先端技術株式会社. 2019年1月1日閲覧。
  58. ^ :p.61Computer Security Technology Planning Study” (PDF). 2007年11月2日閲覧。
  59. ^ "A Tour of The Worm" by Donn Seeley, University of Utah”. 2007年5月20日時点のオリジナルよりアーカイブ。2007年6月3日閲覧。
  60. ^ Bugtraq security mailing list archive”. 2007年9月1日時点のオリジナルよりアーカイブ。2007年6月3日閲覧。
  61. ^ Smashing the Stack for Fun and Profit”. 2007年6月3日閲覧。
  62. ^ eEye Digital Security”. 2007年6月3日閲覧。
  63. ^ Microsoft Technet Security Bulletin MS02-039”. 2007年6月3日閲覧。
  64. ^ Hacker breaks Xbox protection without mod-chip”. 2007年9月27日時点のオリジナルよりアーカイブ。2007年6月3日閲覧。