私は @ anger32 と会話しました。物理メモリページフレームをバックするページを別のプロセスに渡すときに物理メモリページフレームをゼロにすることは、WindowsやLinuxなどのOSの責任ではないと述べています(ただし、彼らはこれが起こることを保証しません)、しかしそれが機密情報で動作することを許可する証明書を持つOSの責任。
別の(おそらくより特権的な)プロセスに対して次の攻撃を行うことは可能ですか?
十分なメモリページをマップし、十分なCPU時間の消費を開始して、最低限の優先度を持つ(少なくともWindowsでは)ゼロ化スレッドがCPU時間を取得しないようにします。
別のプロセスが機密データをメモリに配置する
コンテキストスイッチが発生する
oSにメモリページを要求すると、OSはそのプロセスのページを削除し、同じページフレームに基づく新しいページをゼロにせずに提供します。
秘密のページをスキャンします。
彼はまた、Linuxでmmap
、そのフラグ、および物理アドレスを乱用している別のプロセスのメモリを読み取る方法があると述べています。何でも知ってますか? Linuxで別のプロセスのメモリ(たとえば、別のユーザーまたはSELinuxドメインのプロセスのメモリ)を取得することは本当に可能ですか?それらは非常に危険な脆弱性のように見えます。
私は一度これに興味があり、使用可能なすべてのメモリをmallocしてディスクにダンプする小さなプログラムをLinuxで書きました。
私のアプリケーションに渡される前に、すべてゼロにされたことがわかりました。
その後、カーネルコードもチェックし、それがカーネルであることが確認できました。
-
古いデータが別のプロセスで利用可能にならないようにすることはOSの責任であるということは完全に理にかなっていると思います。そのようなセキュリティ対策を他にどこに実装しますか?
編集:
SWAPメモリに対してテストしたかどうか覚えていません。ディスクIOは、割り当てられたディスクスペース(メモリ)をゼロにするために必要なため)の実装が異なる場合があります。
Linuxでは、次のいずれかの条件が当てはまる場合、プロセスは別のプロセスメモリを読み取ることができます。
/proc/$PID/mem
_または_/dev/mem
_を読み取ることができます。デフォルトでは、_/proc/$PID/mem
_および_/dev/mem
_はrootのみがアクセスできますfork()
/clone()
を実行できますプロセスは、昇格された特権で実行されない限り、任意の無関係なプロセスのメモリに対して読み取りまたは書き込みを行うことはできません。他のすべての場合では、親プロセスになるか、ターゲットプロセスが共有メモリ領域を意図的にセットアップする必要がありました。
親プロセスが子プロセスのメモリにアクセスできることは、ほとんどのUnixシステムの特徴です。 Unixシステム(Linuxを含む)では、fork()
システムコールを使用して新しいプロセスが作成されます。 fork()
は、OSプロセステーブルに新しいエントリを作成して既存のプロセスのコピーを作成し、親の仮想メモリの書き込み時にコピーとして新しいプロセスの仮想メモリを設定します。これは、新しいプロセスが親のメモリを読み取ることができることを意味しますが、この時点では、新しいプロセスはまだ親のコードを実行しているため、これはセキュリティの問題ではありません。次に、新しいプロセスはexec*()
システムコールの1つを呼び出して、新しい実行可能ファイルを独自のメモリに再マップし、その実行可能ファイルの開始記号にジャンプします。再マッピングは、新しいプロセス仮想メモリのページングテーブルの唯一のエントリが新しい実行可能ファイルであることを意味します。新しいプロセスが再マップ領域への読み取り/書き込みを試みると、ページフォールトが発生し、OSはexec*()
edされた実行可能ファイルの対応する部分をメモリにページングします。新しいプロセスがマップされていないメモリ領域への読み取り/書き込みを試みると、セグメンテーション違反が発生します。 forkとexecのより高度な使用法では、プロセスはforkしてから、子のメモリのすべてまたは一部にexec*()
の後に親がアクセスできるように、子プロセスのメモリをマップできます。
Linuxでは、プロセスがOSから(たとえば、mallocを使用して)メモリを割り当てると、プロセスはmmap()
を呼び出して匿名マップを割り当てます。匿名マップは、RAMまたはswapから提供されます。 匿名mmapはゼロで埋められます プロセスが_MAP_UNINITIALIZED
_を要求しない限り、カーネルによって提供されます。パフォーマンス上の理由から非常に制約のある組み込みシステムでは、これを可能にするためにカーネルをコンパイルする必要がありました。
さらに、高セキュリティシナリオの場合、Linuxでは、mlockやMAP_LOCKEDを使用して、プロセスがメモリのすべてまたは一部をスワップ不能に要求できるようにします。ロックされたメモリは常にRAMから提供され、通常、暗号化キーと復号化に使用されるメモリが永続的なストレージにスワップされるのを防ぐために使用されます。
あなたが説明する攻撃は、Windowsでは機能しません。ページゼロ化スレッドを空にすることはゼロ化を妨げず、それを遅らせるだけです。ページ解放のバックグラウンドタスクの存在は、パフォーマンスの最適化です。
基本的に、プライバシーが保証された単純なメモリマネージャは次のように機能します。
最適化されたバージョンは次のようになります。
ゼロ化がまだ行われていないため、ゼロ化スレッドを空にすると、割り当てが遅くなります。データ構造はゼロ化された残りのページを分離したままであり、アロケーターが事前にゼロ化されたページを使い果たすと、ジャストインタイムでゼロ化を行わなければならないため、データ漏洩は発生しません。
別のプロセスのメモリを読み取ることは絶対に可能ですが、これは管理者権限ともちろん、OSは、プロセスがそのプロセスに割り当てられていないメモリのスペースにアクセスすることを許可しません。
管理ユーザーの場合、これはもちろん可能です。たとえばWindowsでは、この機能はデフォルトでプロセスをデバッグするために実装されています。説明されている here のようにタスクマネージャを使用してこれを行うことができます。
ただし、すべてのプロセスと、その時点でメモリに格納されているすべてを含むメモリ全体をダンプすることもできます。 Windowsシステムはデフォルトでこの機能を提供していないため、これはもう簡単ではありません。これを行うには、アプリケーションが独自のメモリドライバーを読み込み、OSを介さずに直接メモリにアクセスできるようにします。
古いLinuxシステムでは、/dev
パーティションのメモリデバイスを介してメモリを直接ダンプできます。これはもう不可能ですが、メモリ全体をダンプできるカーネルモジュールもあります。これにはルート権限も必要です。説明されている here のような1つのプロセスに対してこれを手動で行うこともできます。
// EDIT:これについて40年の経験を持つ上級開発者にたずねました。答えは:それは様々な要因に依存しています。彼はC++でJava変数が初期化されていると言いました。つまり、C++またはJavaで書かれたアプリケーションは、古い情報を取得できません。その変数を初期化することで上書きされますが、Cはこれを行わないので、可能かもしれませんが、それでもOSに依存しています。
技術的には、オペレーティングシステムは、同じセキュリティコンテキストを持つプロセスからページをリサイクルできます。これは、新しいプロセスがそこから収集できるすべての情報にも、プロセスが直接アクセスできるためです。
ただし、これは完全に非現実的です。プロセスのセキュリティコンテキストは時間の経過とともに変化する可能性があり、特権が削除されると(これは一般的なパターンです)、プロセスに含まれるデータは、オリジナル特典セット。
特権もかなりきめ細かい場合があるため、最初にスクラブせずにページがどのプロセスに付与されているかを追跡する作業は、オペレーティングシステムに返されるすべてのページを単にクリアするよりも、特にコンピュータメモリアーキテクチャが非常に有利である場合よりも大幅に高くなります。大規模な順次書き込み。
一部の組み込みアーキテクチャでは、このタスクにDMAコントローラも使用します。これにより、CPU時間を数サイクルに減らしてコントローラをセットアップし、完了を確認します。
プロセスが新しくマッピングされたページがクリアされたと想定するかどうかは、そのプロセスとオペレーティングシステムの間の規約ですが、通常これは想定されていません。また、プロセスが内部のデータを追跡する必要があるため、通常、そうする理由はほとんどありません。彼らが有効と考える彼らのアドレス空間、そして彼らは本当に情報を保持しているものに対してのみそうするでしょう。
タスクがページをすばやくマップ、変更、およびマップ解除すると、システムの負荷が増加し、優先度の低いプロセスがページをクリアするのを待機している間にプロセスが中断される場合があります。オペレーティングシステムは、ここで 優先順位の逆転 を誤って導入しないように注意する必要があります。これは、ゼロ化タスクの優先順位を、ページをマップしようとする最高優先順位のタスクの優先順位に一時的に上げることによります。
ほとんどのOSは certified である必要があり、特定の目的/特定の組織で使用されます。それらのほとんどは、異なる保証レベルに対して Common Criteria フレームワークを使用しており、一部のレベルでは、OSが別のプロセスに渡す前にページをクリアする必要があります。この要件への 間接参照 。
ゼロで初期化されたページが必要な理由の1つは、Common Criteriaなどのさまざまなセキュリティ要件を満たすためです。ほとんどのCommon Criteriaプロファイルでは、ユーザーモードプロセスに初期化されたページフレームを与えて、前のプロセスのメモリの内容を読み取れないようにする必要があると規定しています。したがって、メモリマネージャーは、ページがバッキングストアから読み込まれていない限り、ユーザーモードプロセスにゼロ化されたページフレームを提供します。その場合、メモリマネージャーは、ゼロ以外のページフレームを使用し、ディスクまたはリモートストレージのデータで初期化します。
(Windows Internals、第2部第6版から。MarkE. Russinovich、David A. Solomon、Alex Ionescu。著作権©David SolomonおよびMark Russinovich)
このような機能を有効にするには、ほとんどの場合、メモリ管理アーキテクチャを適切に設計する必要があります。この機能を「民間人」/安全でないバージョンで除外して、明らかでないパフォーマンスを向上させることは意味がありません。
具体的には、カーネルページや他のユーザーのページが非特権プロセスにリークされないようにすることが重要です。そのため、これらのページは何らかの段階でクリアする必要があります。また、ページが実際に境界を超えない限り(ページが同じプロセス/カーネルに再度割り当てられる場合)、ページをゼロにすることも非効率的です。そのため、それを実行するための唯一の賢明な時間は割り当て時であり、受信者がこれを実行することを信頼できないため、OSが責任を負う必要があります。 (もちろん、実際のOSは、あらゆる種類の最適化を採用して、ページ割り当ての遅延を減らします。)