web-dev-qa-db-ja.com

NXビット:スタックを保護しますか?

NXビットが万能薬であると聞いたことがありますが、そうではなかったのです。私が不思議に思った1つの詳細:NX(実行なし)ビットは、スタックに挿入されてそこで実行されるコードから保護しますか?スタックはヒープではないため、通常はNXで保護されていないようです。ありがとう。

5
Franch

NXビット は、一部のCPU(最近の十分なx86を含む)の メモリ管理ユニット の機能です。これにより、各メモリページをコード実行に対して「許可」または「禁止」としてマークできます。 MMUはカーネルの制御下にあります。カーネルコードは、実行権限を取得するページと取得しないページを決定します。したがって、スタックスペースが実行に対して保護されているかどうかは、OSによって異なります。また、他の多くのものに依存します:

  • カーネルはページプロパティにデフォルト値を適用する場合がありますが、アプリケーションは変更を要求できます。例えば。 Linuxシステムでは、 mprotect() システムコールを使用して、呼び出しプロセスの制御下にあるページでの実行を有効または無効にできます。特に、 JITコンパイラ を含むアプリケーション(たとえば、効率的なJavaScriptサポートのためのWebブラウザ)は、mprotect()で再生する必要があります。これは、generateコード(つまり、一部のメモリにバイトを書き込む)を実行し、次にそのコードを実行します。

  • モノスレッドアプリケーションは、カーネルが認識している専用領域にスタックを取得します。特に、スタックページは最初の使用時に自動的に割り当てられます。マルチスレッドアプリケーションでは、状況が変化します。各スレッドには独自のスタックがあり、カーネルの観点からは、ヒープに割り当てられます。 OSによって、スレッドスタックに対するカーネルからの特別なサポートがある場合とない場合があります。

  • 特にGCCは 入れ子関数 と呼ばれるC言語拡張をサポートしています。これらの関数のセマンティクスのため、ネストされた関数のコンパイル済みコードは、動的にスタック上にいくつかの実行可能コードを生成する必要があります。これはトランポリンと呼ばれます。詳細については このページ を参照してください。つまり、トランポリンが実際に機能するためには、スタック(または少なくともトランポリンが存在するページ)が実行可能としてマークされている必要があります。

実行可能ファイルの形式である ELFファイル形式 およびLinuxではDLL)には、スタックが必要かどうかを指定できるフィールドがありますGCCは、ネストされた関数を含むコードをコンパイルするときに、生成されたバイナリにこのフラグを設定します。

私はかなり最近のLinux(NXビットをアクティブにした64ビットx86のUbuntu 13.10)でいくつかのテストを行いました。特定のページが実行可能かどうかは、特殊ファイル/proc/$$/mapsの内容から推測できます。ここで、$$はプロセスIDです。これらのテストでは、次のことが示されています。

  • デフォルトでは、メインスタックは実行可能ではありません。スタックページにNXビットが設定されています。
  • コードにネストされた関数が含まれている場合、実行可能ファイルは「実行可能スタック」フラグでマークされ、実際、メインスタックは現在実行可能です。
  • スレッドスタックは、メインスタックと同じ「実行可能ステータス」で作成されます。
  • メインスタックが実行可能でない「通常の」実行可能ファイルが、ネストされた関数のコードを含むDLL(with dlopen()))を動的にロードする場合、メインスタックは自動的に実行可能ステータスに切り替わります。
  • メインスタックとそのすべてのスレッドスタックが実行不可である通常のマルチスレッド実行可能ファイルが、ネストされた関数のコードを含むDLLを動的にロードする場合、メインスタックとすべてのスレッドスタックはen masseが実行可能ステータスに昇格します。これは、このLinuxシステムでは、「何か」(おそらくカーネル、私がチェックしていない)が現在のすべてを認識していることを意味しますスレッドはスタックし、そのマッピング権を動的に変更できます。

これらすべてから、少なくとも最近のバージョンのLinuxでは、スタック(メインとスレッド)は通常になると結論します実行不可能(つまり、NXビットセット)としてマークされます。ただし、アプリケーションの実行可能コード、またはDLLによってアプリケーションによってロードされたいくつかの実行可能コードに、ネストされた関数が含まれるか、実行可能スタックの必要性を通知する場合、すべてのスタックがアプリケーションは実行可能としてマークされます(つまり、NXビットが設定されていません)。言い換えると、アプリケーションの単一のロード可能なプラグインまたは拡張機能が、まれにしか使用されないが正当で文書化されたGCCの機能。

ただし、@ tylerlが指摘しているように、NXビットによって提供される保護はそれほど優れていないため、パニックに陥る必要はありません。これにより、一部のエクスプロイトが最も能力の低い攻撃者にとってより扱いにくくなります。しかし、優れた攻撃者は妨害されません。また、NXに関するこのすべての話は、バッファオーバーフローまたは解放後使用が発生した場合に損傷を封じ込めようとすることに関するものであり、おそらく反応が少し遅れます。

13
Tom Leek

スタックを非実行としてマークすることにより、スタックに挿入されたコードが実行されないようにすることができます。スタックを変更から保護していません。むしろ、コードがNXマークの付いたスタック内の位置にジャンプしようとしたときに、ハードクラッシュを引き起こしています。

回避策はnotスタックでコードを実行しようとすることです。戻り位置をスタック内のスポットに設定する代わりに、exec()などのシステム関数の場所に設定し、スタックで上書きしたスポットにexec()実行可能マシンコードの代わりにシステムコール。このようなシナリオでは、攻撃者は自分が選択したパラメーターでシステム関数を実行できます。これは、たとえば別のプログラムを実行するのに十分です。

したがって、NXはある程度の保護を提供しますが、トンは提供しません。攻撃者が最初のエクスプロイトを使用して実行できることの柔軟性は多少制限されますが、攻撃者がそのエクスプロイトを利用してシェルを実行できる場合、安全性はそれほど高くありません。

防御の次の層はASLRで、exec()などのシステム関数の場所をランダム化します。これにより、シェルを実行しようとしたときに、戻り場所にロードするアドレスがわかりません。

ALSRに対する攻撃は通常、総当たりです。エクスプロイトを複数回実行し、毎回異なるリターンアドレスを試します。これは、ランダム化の範囲が制限されているため、32ビットシステムでのALSRの効果が64ビットシステムよりも大幅に低く、ブルートフォース攻撃が比較的高速になることを意味します。

ASLRとNXを組み合わせると、ある程度の保護が提供されますが、絶対にはなりません。

5
tylerl