バッファオーバーフローを防ぐために、カナリア値、ASLR、DEP、NXの使用など、いくつかの保護が利用できます。しかし、意志があるところには道があります。攻撃者がこれらの保護スキームを迂回する可能性があるさまざまな方法について調査しています。明確な情報が提供されている場所は1つもないようです。これらは私の考えの一部です。
Canary-攻撃者はカナリア値を把握し、それをバッファインジェクションで使用して、スタックガードがエクスプロイトを検出しないようにする可能性があります
DEP、NX-VirtualAlloc(), VirtualProtect()
への呼び出しがある場合、攻撃者はこれらの関数にコードをリダイレクトし、DEP、NXを無効にする可能性があります彼が任意のコードを挿入したいページに。
[〜#〜] aslr [〜#〜]-手がかりなし。 ASLRとDEPはどのように機能しますか?
Canary
スタックカナリアは、すべての関数のプロローグ領域とエピローグ領域を変更して、それぞれスタックに値を配置してチェックすることで機能します。そのため、メモリのコピー操作中にスタックバッファが上書きされると、エラーが通知されますbefore実行はコピー関数から戻ります。これが発生すると、例外が発生し、それが最終的にOSのデフォルトの例外ハンドラーに到達するまで、例外ハンドラーの階層に渡されます。スタック内の既存の例外ハンドラー構造を上書きできる場合は、独自のコードを指すようにすることができます。これは構造化例外処理(SEH)エクスプロイトであり、カナリアチェックを完全にスキップできます。
DEP/NX
DEPとNXは、本質的にメモリ内の重要な構造を非実行可能としてマークし、それらのメモリ領域を実行しようとするとハードウェアレベルの例外を強制します。これにより、スタックが実行不可能であるため、eip
をesp+offset
に設定してシェルコードをすぐに実行すると、通常のスタックバッファオーバーフローが発生します。 DEPとNXをバイパスするには、 Return-Oriented Programming と呼ばれるクールなトリックが必要です。
ROPは基本的に、プログラム(ガジェットと呼ばれます)から既存のコードスニペットを見つけ、それらにジャンプすることで、目的の結果を生成します。コードは正当な実行可能メモリの一部であるため、DEPとNXは関係ありません。これらのガジェットは、エクスプロイトペイロードを含むスタックを介してチェーン化されます。スタックの各エントリは、次のROPガジェットのアドレスに対応しています。各ガジェットはinstr1; instr2; instr3; ... instrN; ret
の形式であるため、ret
は命令の実行後にスタック上の次のアドレスにジャンプし、ガジェットをチェーンします。多くの場合、チェーンを正常に完了するためにスタックに追加の値を配置する必要があります。これは、他の方法では邪魔になる命令のためです。
トリックは、VirtualProtect
などのメモリ保護関数を呼び出すためにこれらのROPをチェーンして、スタックを実行可能にするために使用されるため、jmp esp
または同等のものを介してシェルコードを実行できますガジェット。 mona.py
のようなツールを使用して、これらのROPガジェットチェーンを生成したり、一般的にROPガジェットを検索したりできます。
[〜#〜] aslr [〜#〜]
ASLRをバイパスする方法はいくつかあります。
jmp esp
を介してシェルコードを実行するだけで済みます。推奨読書:
カナリアおよびその他の揮発性物質しない防止オーバーフロー; が発生したオーバーフローの結果に対処しようとするだけです。カナリアは、スタックフレームのリターンアドレスを上書きしたオーバーフローのケースを検出しようとします。 DEPはさらに一歩進んでおり、戻りアドレスが上書きされ、その後に続くと想定し、実行がジャンプする可能性のある領域を制限します。 ASLRはさらに一歩進んだもので、実行が許可されている領域を「シャッフル」します。
歴史的に、バッファオーバーフローは、スタックのリターンアドレスを上書きするために悪用され、バッファオーバーフローに使用されたデータそのものに実行がジャンプするようにします。カナリアはジャンプする前にそれを検出しようとし、DEPはスタックスペースを実行不可能にするために使用されます。 DEPは、ヒープ内のバッファーをオーバーフローするときにも機能します(カナリアはスタックバッファーオーバーフローにのみ使用できますが、ヒープにはバッファー、および関数へのポインターなどの上書きする機密データも含めることができます-特に=のコンテキストでOOP C++などの言語)。DEPとカナリアを回避するために、攻撃者は関数へのポインタを上書きできるオーバーフローを探し始め、実行を標準ライブラリコード。これは、必ず「存在」し、実行可能でもあります。そのため、ASLRが発明されました。そのようなゲームをより難しくするためです。ASLRは、幸運であることによって、依然として敗北する可能性があります。ASLR以降ページアライメント(x86では4 kB)、大きすぎないアドレス空間(通常、32ビットx86では2 GB未満)を維持する必要がありますが、ターゲットコードが存在する場所は多くありません(多くても半分)ミリオン)。攻撃のコンテキストと攻撃者のスクリプトが試行できる頻度に応じて、これは低すぎて快適ではない場合があります。
ここでの重要なテーマは、カナリア、DEP、ASLR自体がオーバーフローを無効にするのではなく、従来から採用されている一般的なオーバーフローエクスプロイトメソッドを対象とすることです。どのアプリケーションでも、非ポインターデータを上書きするオーバーフローは、リモートシェルの悪用と同じくらい致命的です(たとえば、「authenticated_user_name
」という文字列フィールドを変更するオーバーフローを想像してください)。攻撃側と防御側の間の武器競争は専門化しすぎており、私の意見では、ますます要点を逃しています。一般的には、オーバーフローが発生しないようにする方がはるかに優れています。つまり、問題のあるプロセス/スレッドをブロック/キルbeforeしてから、ターゲットバッファ。これは、ほぼすべてのまともなプログラミング言語(Java、C#、VB.NET、Python、Ruby、Node.js、OCaml、PHP ...などで起こります)です。
保護の基本レベルはASLR + DEPです。
これらの両方を使用しない場合、バッファーオーバーランを悪用する強力な手法が多数あります(たとえば、リターン指向のコンピューティング、ヒープスプレー、繰り返し推測)。たとえば、リターン指向のコンピューティングを使用すると、DEPだけを無効にすることができます。また、ASLRだけでは、ヒープスプレーと繰り返し試行することで無効にすることができます。
ただし、ターゲットがASLR + DEPの両方を使用する場合、悪用は著しく困難になります。上記の手法は、ASLR + DEPを倒すには不十分です。 ASLR + DEPは、攻撃者の生活をはるかに困難にする1対2のパンチのようなものです。 ASLR + DEPの組み合わせを打ち負かすことは不可能ではありませんが、はるかに巧妙になります。
ASLR + DEPを無効にする方法の私のお気に入りの例は、スライドデッキ インタープリターの活用:ポインターの推論とJITスプレー で説明されています。そこで、著者は彼がフラッシュのメモリ安全エラーをどのように悪用したかについて説明します。 ASLR + DEPが存在するにもかかわらず、Flash JITのプロパティを利用して、コードインジェクション攻撃を開始できるようにメモリを調整しました。 JITはジャストインタイムコンパイラであることを思い出してください。 Flashバイトコードをネイティブコードにコンパイルします。ネイティブコードはメモリのどこかに保存され、Flash JITはそれを実行可能としてマークします(DEPにもかかわらず)。著者は、コンパイル時に、悪意のあるシェルコード(バイトでオフセットされた)を埋め込んだ一連のバイトを生成するFlashバイトコードを生成する方法を見つけました。次に、ヒープスプレーテクニックを使用して、メモリにこのコピーが多数あることを確認しました。最後に、彼はメモリセーフティバグを悪用して、プログラムを別のアドレスにジャンプさせました。 ASLRにより、これはランダムなアドレスにジャンプするようなものでしたが、多くのコピーにより、高い確率でシェルコードにジャンプすることが保証されました。このようにして、彼はASLRとDEPの両方をバイパスしました-気の利いた偉業です。
最後に、64ビットアーキテクチャではASLRがmuchより効果的であることを言及する価値があります。 32ビットアーキテクチャでは、ASLRは多くの場合、単純に複数回試行するだけで無効にできます。 32ビットプラットフォームでは、十分なランダム性を導入するのに十分な自由度がないため、32ビットプラットフォームでは、攻撃者がばかげて成功する可能性が高すぎます。最も強力な防御策として、64ビットプラットフォームを使用してください。