Windows Vista Service Pack 1 (SP1) のサポートは 2011 年 7 月 12 日に終了します。 Windows 用のセキュリティ更新プログラムを継続して入手するには、Windows Vista Service Pack 2 (SP2) を実行していることを確認してください。 詳細については、次のマイクロソフト Web ページを参照してください: Windows の一部バージョンのサポート終了のお知らせ。
アプリケーションが完全修飾パスを指定せずにダイナミック リンク ライブラリ (DLL) を動的に読み込むと、Windows は適切に定義された一連のディレクトリを検索して DLL を見つけようとします。 攻撃者がディレクトリの 1 つを制御した場合、アプリケーションに、想定していた DLL ではなく、悪意のある DLL のコピーを強制的に読み込む可能性があります。 これらの攻撃は、"DLL プリロード攻撃" として知られており、共有 DLL ライブラリの動的な読み込みをサポートするすべてのオペレーティング システムに共通しています。 このような攻撃の影響は、攻撃者がアプリケーションを実行しているユーザーのコンテキストでコードを実行できる可能性があります。 アプリケーションが管理者として実行されている場合、これによりローカルで特権が昇格する可能性があります。 これらの攻撃への関心が再び高まっていることはわかっています。 この問題が共通のお客様に与える影響を最小限に抑えるために、このドキュメントを開発者コミュニティに公開して、この問題について認識し、アプリケーションで問題に対処するために必要なツールを用意できるようにします。
概要
DLL プリロード攻撃の説明
LoadLibrary ベースの攻撃
アプリケーションが完全修飾パスを指定せずに DLL を動的に読み込む場合、Windows は DLL 検索順序と呼ばれる明確に定義された一連のディレクトリを線形検索してこの DLL を見つけようとします。 Windows が DLL の検索順序内で DLL を見つけると、その DLL が読み込まれます。 ただし、Windows が DLL 検索順序のどのディレクトリにも DLL が見つからない場合は、DLL の読み込み操作にエラーが返されます。 DLL を動的に読み込むために使用される LoadLibrary 関数と LoadLibraryEx 関数の DLL 検索順序を次に示します。
- アプリケーションが読み込まれたディレクトリ
- システム ディレクトリ
- 16 ビット システム ディレクトリ
- Windows ディレクトリ
- 現在の作業ディレクトリ (CWD)
- PATH 環境変数に一覧表示されているディレクトリ
たとえば次のようなシナリオを考えてみます。
- アプリケーションは、アプリケーションの CWD で見つかると想定される完全修飾パスを指定せずに DLL を読み込みます。
- アプリケーションは、DLL が見つからないときにそのケースを処理する準備が完全に整っています。
- 攻撃者はアプリケーションに関するこの情報を知っており、CWD を制御します。
- 攻撃者は、CWD 内の独自の特別に細工されたバージョンの DLL をコピーします。 これは、攻撃者がこれを行うアクセス許可を持っていることを前提としています。
- Windows は DLL 検索順序でディレクトリを検索し、アプリケーションの CWD で DLL を見つけます。
このシナリオでは、特別に細工された DLL がアプリケーション内で実行され、現在のユーザーの特権を取得します。
推奨事項
この攻撃を防ぐために、アプリケーションは空の文字列 ("") を使用して SetDllDirectory API を呼び出すことで、DLL 検索パスから現在の作業ディレクトリ (CWD) を削除できます。 アプリケーションが現在のディレクトリから DLL の読み込みに依存している場合は、現在の作業ディレクトリを取得し、それを使用して LoadLibrary の完全修飾パスを渡してください。
また、一部の開発者は、ユーザーが実行している Windows のバージョンを判別するために、LoadLibrary を使用して特定の DLL が存在するかどうかを検証していることも把握しています。 これにより、アプリケーションが脆弱になる可能性があることに注意してください。 影響を受けるライブラリが、アプリケーションが実行されている Windows リリースに実際に存在しない場合、攻撃者は同じ名前のライブラリを CWD に導入する可能性があります。 この方法は使用しないことを強くお勧めします。 代わりに、MSDN の資料「システム バージョンの取得」に記載されている推奨される方法を使用してください。
サードパーティのプラグインを読み込み、LoadLibrary 呼び出しに修飾パスを使用するようにプラグインに強制できないアプリケーションは、SetDllDirectory("") を呼び出して CWD を削除し、SetDllDirectory("plugin install location") を呼び出してプラグインのインストール ディレクトリを DLL 検索パスに追加する必要があります。
SearchPath ベースの攻撃
アプリケーションが SearchPath API を使用して DLL を検索し、 SearchPath から返されたパスを動的に読み込む場合にも、同様の攻撃が存在します。 SearchPath API の既定の検索順序を次に示します。
- アプリケーションが読み込まれたディレクトリ
- 現在の作業ディレクトリ (CWD)
- システム ディレクトリ
- 16 ビット システム ディレクトリ
- Windows ディレクトリ
- PATH 環境変数に一覧表示されているディレクトリ
このパターンはセキュリティで保護されていないため、お勧めしません。 出力の使用目的が LoadLibrary 関数の呼び出しにある場合は、.dll ファイルを検索する方法として SearchPath 関数を使用しないことをお勧めします。 これにより、SearchPath 関数の検索順序が LoadLibrary 関数で使用される検索順序と異なるため、誤った .dll ファイルが見つかる可能性があります。 .dll ファイルを検索して読み込む必要がある場合は、LoadLibrary 関数を使用します。
ShellExecute と CreateProcess
開発者が ShellExecute や CreateProcess などの同様の関数を呼び出して外部実行可能ファイルを読み込む場合にも、これらの問題のバリエーションが存在する可能性があります。 開発者がバイナリを読み込むときは注意し、完全修飾パスを指定することをお勧めします。 このため、ライブラリではなくバイナリを読み込むときに複雑さが軽減されます。
ソフトウェア開発者向けの推奨手順
開発者は次の操作を行うことをお勧めします。
セキュリティで保護されていないライブラリの読み込みのインスタンスについてアプリケーションを検証します (それぞれの例は、この記事の後半で示します)。 これらには、以下が含まれます。
- SearchPath を使用してライブラリまたはコンポーネントの場所を識別します。
- オペレーティング システムのバージョンを識別するための LoadLibrary の使用。
LoadLibrary、CreateProcess、および ShellExecute へのすべての呼び出しには、可能な限り完全修飾パスを使用します。
SetDllDirectory の呼び出しを空の文字列 ("") で実装して、必要に応じて既定の DLL 検索順序から現在の作業ディレクトリを削除します。 SetDllDirectory がプロセス全体に影響を与えることに注意してください。 そのため、これは LoadLibrary の呼び出しの前後ではなく、プロセスの初期段階で 1 回行う必要があります。 SetDllDirectory はプロセス全体に影響するため、複数のスレッドが異なる値で SetDllDirectory を呼び出すと、未定義の動作が発生する可能性があります。 さらに、プロセスがサード パーティの DLL を読み込むように設計されている場合は、プロセス全体の設定を行うことで非互換性が発生するかどうかをテストする必要があります。 既知の問題として、アプリケーションが Visual Basic for Applications に依存する場合、プロセス全体の設定によって非互換性が発生する可能性があります。
SetSearchPathMode 関数を使用して、プロセスのセーフ プロセス検索モードを有効にします。 これにより、プロセスの有効期間中、現在の作業ディレクトリが検索パス検索リストの最後の場所に移動します。
セーフ検索モードが有効になっている場合でも、完全修飾パスを指定せずに SearchPath を使用して DLL の存在をチェックすることは避けてください。これは、DLL プリロード攻撃につながる可能性があるためです。
セキュリティで保護されていないライブラリ読み込みの識別に関するガイダンス
ソース コードでは、セキュリティで保護されていないライブラリの読み込みの例を次に示します。
次のコード例では、最もセキュリティの低い検索パスを使用して "schannel.dll" を検索します。 攻撃者が schannel.dll を CWD に配置できる場合、アプリケーションが Windows ディレクトリで適切なライブラリを検索する前にでも読み込まれます。
DWORD retval = SearchPath(NULL, "schannel", ".dll", err, result, NULL); HMODULE handle = LoadLibrary(result);次のコード例では、アプリケーションは LoadLibrary() 呼び出しのために、このドキュメントの冒頭で説明したさまざまなアプリケーションとオペレーティング システムの場所からライブラリをロードしようとします。 ファイルが存在しないというリスクがある場合、アプリケーションは現在の作業ディレクトリからファイルを読み込もうとする場合があります。 このシナリオは、前の例よりもわずかに危険性が低くなります。 ただし、環境が完全に予測できない場合、アプリケーション ユーザーは依然としてリスクにさらされます。
HMODULE handle = LoadLibrary("schannel.dll");
より優れた、より安全なライブラリ読み込みの例を次に示します。
次のコード例では、完全修飾パスを使用してライブラリが直接読み込まれます。 アプリケーションのターゲット ディレクトリへの書き込みアクセス許可を既に持っている場合を除き、攻撃者が悪意のあるコードを持ち込むリスクはありません。
HMODULE handle = LoadLibrary("c:\\windows\\system32\\schannel.dll");注: システム ディレクトリを確認する方法の詳細については、以下のリソースを参照してください。
GetSystemDirectory
http://msdn.microsoft.com/en-us/library/ms724373%28VS.85%29.aspx SHGetKnownFolderPath
http://msdn.microsoft.com/en-us/library/bb762188%28v=VS.85%29.aspx次のコード例では、LoadLibrary を呼び出す前に、現在の作業ディレクトリが検索パスから削除されます。 これにより、攻撃者が DLL のプリロード攻撃を使用するために、アプリケーション ディレクトリ、Windows ディレクトリ、またはユーザーのパスに指定されているディレクトリのいずれかを制御する必要があるため、リスクは大幅に軽減されます。
SetDllDirectory (""); HMODULE handle = LoadLibrary("schannel.dll");セキュリティ更新プログラム 963027 ( MS09-014 に記載) がインストールされているすべてのシステムで、次のコードにより、CWD は検索順序の最後の位置に永続的に移動します。 それ以降、そのプロセス内から検索モードを変更しようとする SetSearchPathMode 関数の呼び出しは失敗します。
SetDllDirectory (""); HMODULE handle = LoadLibrary("schannel.dll");次のコード例では、LoadLibrary を呼び出す前に、現在の作業ディレクトリが検索パスから削除されます。 これにより、攻撃者が DLL プリロード攻撃を使用するために、アプリケーション ディレクトリ、Windows ディレクトリ、またはユーザーのパスに指定されているディレクトリのいずれかを制御する必要があるため、リスクが大幅に軽減されます。
SetSearchPathMode (BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE | BASE_SEARCH_PATH_PERMANENT ); HMODULE handle = LoadLibrary("schannel.dll");
プロセス モニターを使用してセキュリティで保護されていない負荷を動的に検出する
Microsoft では、Process Monitor という名前のツールを公開しています。 このツールを使用することで、開発者と管理者は実行中のプロセスの動作を綿密に追跡できます。 Process Monitor を使用すると、アプリケーションの 1 つがこの種の問題に対して脆弱であるかどうかを動的に検出できます。
Process Monitor をダウンロードするには、次のマイクロソフト Web ページにアクセスしてください。
http://technet.microsoft.com/en-us/sysinternals/bb896645.aspx特定のディレクトリに設定された CWD を使用して、アプリケーションの起動を試みます。 たとえば、拡張子を持つファイルをダブルクリックし、そのファイル ハンドラーがアプリケーションに割り当てられている場合などです。
次のフィルターを使用して Process Monitor を設定します。
脆弱なパスがヒットしている場合は、次のような内容が表示されます。
リモート ファイル共有を呼び出して DLL を読み込むことは、これが脆弱なプログラムであることを示します。
追加情報
詳細については、次のマイクロソフト Web ページを参照してください。
ダイナミック リンク ライブラリの検索順序
http://msdn.microsoft.com/en-us/library/ms682586(VS.85).aspx SearchPath 関数に関する MSDN ドキュメンテーション
http://msdn.microsoft.com/en-us/library/aa365527(VS.85).aspx LoadLibrary 関数に関する MSDN ドキュメンテーション
http://msdn.microsoft.com/en-us/library/ms684175(VS.85).aspx SetDllDirectory 関数に関する MSDN ドキュメンテーション
http://msdn.microsoft.com/en-us/library/ms686203(VS.85).aspx SetSearchPathMode 関数に関する MSDN ドキュメンテーション
http://msdn.microsoft.com/en-us/library/dd266735(VS.85).aspx David Leblanc 氏 (Microsoft Office のプリンシパル セキュリティ エンジニア) によるブログ投稿
http://blogs.msdn.com/b/david_leblanc/archive/2008/02/20/dll-preloading-attacks.aspx DLL プリロード攻撃に関する MSRC エンジニアリング チームの Andrew Roths 氏によるブログ投稿