DLL プリロード攻撃を防ぐためのライブラリの安全な読み込み

Windows Vista Service Pack 1 (SP1) のサポートは 2011 年 7 月 12 日に終了します。 Windows 用のセキュリティ更新プログラムを継続して入手するには、Windows Vista Service Pack 2 (SP2) を実行していることを確認してください。 詳細については、「Microsoft Web ページ: 一部のバージョンの Windows でサポートが終了する」を参照してください。

アプリケーションが完全修飾パスを指定せずにダイナミック リンク ライブラリ (DLL) を動的に読み込む場合、Windows は、適切に定義されたディレクトリのセットを検索して DLL の検索を試みます。 攻撃者がディレクトリの 1 つを制御すると、予期していた DLL ではなく DLL の悪意のあるコピーをアプリケーションに強制的に読み込む可能性があります。 これらの攻撃は"DLL プリロード攻撃" と呼ばれ、共有 DLL ライブラリの動的な読み込みをサポートするすべてのオペレーティング システムに共通しています。 このような攻撃の影響は、攻撃者がアプリケーションを実行しているユーザーのコンテキストでコードを実行できることです。 アプリケーションを管理者として実行すると、ローカルの特権が昇格する可能性があります。 私たちは、これらの攻撃に対する新たな関心について知っています。 この問題が相互のお客様に及ぼす影響を制限するために、このドキュメントを開発者コミュニティにリリースして、この問題について理解し、アプリケーションの問題に対処するために必要なツールを用意しています。

概要

DLL プリロード攻撃の説明

LoadLibrary ベースの攻撃

アプリケーションが完全修飾パスを指定せずに DLL を動的に読み込む場合、Windows は DLL Search Order と呼ばれる、適切に定義されたディレクトリのセットを直線的に検索することで、この DLL の検索を試みます。 Windows が DLL 検索順序内で DLL を検索すると、その DLL が読み込まれます。 ただし、DLL 検索順序のどのディレクトリにも DLL が見つからない場合、DLL の読み込み操作に失敗が返されます。 DLL を動的に読み込むために使用される LoadLibrary 関数と LoadLibraryEx 関数の DLL 検索順序を次に示します。

  1. アプリケーションの読み込み元のディレクトリ
  2. システム ディレクトリ
  3. 16 ビット システム ディレクトリ
  4. Windows ディレクトリ
  5. 現在の作業ディレクトリ (CWD)
  6. PATH 環境変数に一覧表示されるディレクトリ

                
たとえば次のようなシナリオを考えてみます。

  • アプリケーションは、アプリケーションの CWD で見つけることが予想される完全修飾パスを指定せずに DLL を読み込みます。
  • アプリケーションは、DLL が見つからない場合のケースを処理するために完全に準備されています。
  • 攻撃者はアプリケーションに関するこの情報を認識し、CWD を制御します。
  • 攻撃者は、独自の特別に細工されたバージョンの DLL を CWD にコピーします。 これは、攻撃者がこれを行うアクセス許可を持っていることを前提としています。
  • Windows では、DLL 検索順序内のディレクトリを検索し、アプリケーションの CWD で DLL を検索します。

このシナリオでは、特別に細工された DLL がアプリケーション内で実行され、現在のユーザーの特権が取得されます。

推奨事項

この攻撃を防ぐために、アプリケーションは、空の文字列 ("") を使用して SetDllDirectory API を呼び出すことによって、現在の作業ディレクトリ (CWD) を DLL 検索パスから削除できます。 アプリケーションが現在のディレクトリから DLL を読み込むことに依存している場合は、現在の作業ディレクトリを取得し、これを使用して LoadLibrary の完全修飾パスを渡してください。

また、一部の開発者は LoadLibrary を使用して、特定の DLL が存在するかどうかを検証して、ユーザーが実行している Windows のバージョンを判断することも認識しています。 これにより、アプリケーションが脆弱になる可能性があることに注意してください。 影響を受けるライブラリが、アプリケーションが実行されている 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

これらの問題のバリエーションは、開発者が ShellExecuteCreateProcess などの同様の関数を呼び出して外部実行可能ファイルを読み込む場合にも存在する可能性があります。 開発者がバイナリを読み込み、完全修飾パスを指定するときは注意することをお勧めします。 これにより、ライブラリではなくバイナリを読み込む際の複雑さが少なくなります。

開発者は次のことを行うことをお勧めします。

  • セキュリティで保護されていないライブラリの読み込みのインスタンスのアプリケーションを検証します (それぞれの例については、この記事の後半で説明します)。 これらには、以下が含まれます。

    • ライブラリまたはコンポーネントの場所を識別するための SearchPath の使用。
    • LoadLibrary を使用してオペレーティング システムのバージョンを識別します。
  • LoadLibrary、CreateProcess、ShellExecute のすべての呼び出しに完全修飾パスを使用できます。

  • 空の文字列 ("") を使用して SetDllDirectory への呼び出しを実装し、必要な既定の DLL 検索順序から現在の作業ディレクトリを削除します。 SetDllDirectory はプロセス全体に影響を与える点に注意してください。 そのため、これは、LoadLibrary の呼び出しの前後ではなく、プロセスの初期化の早い段階で 1 回行う必要があります。 SetDllDirectory はプロセス全体に影響するため、値が異なる SetDllDirectory を呼び出す複数のスレッドによって未定義の動作が発生する可能性があります。 さらに、プロセスがサード パーティ製 DLL を読み込むよう設計されている場合は、プロセス全体の設定を行うと非互換性が発生するかどうかを判断するためのテストが必要になります。 既知の問題は、アプリケーションがVisual Basic for Applicationsに依存している場合、プロセス全体の設定が非互換性を引き起こす可能性があるということです。

  • SetSearchPathMode 関数を使用して、プロセスのセーフ プロセス検索モードを有効にします。 これにより、現在の作業ディレクトリが、プロセスの有効期間の SearchPath 検索リストの最後の場所に移動されます。

  • SearchPath を使用して、完全修飾パスを指定せずに DLL の存在をチェックしないでください。セーフ検索モードが有効になっている場合でも、DLL プリロード攻撃が引き続き発生する可能性があるためです。

安全でないライブラリの読み込みの識別に関するガイダンス

ソース コードでは、セキュリティで保護されていないライブラリの読み込みの例を次に示します。

  • 次のコード例では、アプリケーションは、最も安全な検索パスを使用して "schannel.dll" を検索します。 攻撃者が CWD に schannel.dll を配置できる場合、アプリケーションが 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 は、プロセス モニターという名前のツールを発行します。 このツールを使用すると、開発者と管理者は実行中のプロセスの動作を厳密に追跡できます。 プロセス モニターを使用すると、アプリケーションの 1 つがこの種の問題に対して脆弱であるかどうかを動的に検出できます。

  • プロセス モニターをダウンロードするには、次の Microsoft Web ページにアクセスします。
    http://technet.microsoft.com/en-us/sysinternals/bb896645.aspx

  • 特定のディレクトリに設定された CWD を使用して、アプリケーションを起動してみてください。 たとえば、ファイル ハンドラーがアプリケーションに割り当てられている拡張子を持つファイルをダブルクリックします。

  • 次のフィルターを使用してプロセス モニターを設定します。

    371495f2-14de-f99c-c55a-f75d31fe9ca8

  • 脆弱なパスがヒットしている場合は、9acdd1ae-29b9-e499-9de9-8bc665b95e76 のようになります。

     DLL を読み込むためのリモート ファイル共有の呼び出しは、これが脆弱なプログラムであることを示します。

追加情報

詳細については、次の Microsoft 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 Microsoft Office のプリンシパル セキュリティ エンジニア、David Leblanc によるブログ投稿

http://blogs.msdn.com/b/david_leblanc/archive/2008/02/20/dll-preloading-attacks.aspx DLL プリロード攻撃に関する MSRC エンジニアリング チーム、Andrew Roths によるブログ投稿

http://blogs.technet.com/b/srd/archive/2009/04/14/ms09-014-addressing-the-safari-carpet-bomb-vulnerability.aspx

その他のリソース