安全載入函式庫以防止 DLL 預載攻擊

對 Windows Vista Service Pack 1 (SP1) 的支援已於 2011 年 7 月 12 日終止。 若要繼續收到 Windows 的安全性更新,請確定您執行的是 Windows Vista with Service Pack 2 (SP2)。 欲了解更多資訊,請參閱這個 Microsoft 網頁: 部分 Windows 版本的支援即將結束

當應用程式動態載入動態連結函式庫 (DLL) ,卻未指定完整限定路徑時,Windows 會嘗試透過搜尋一組明確定義的目錄來定位該 DLL。 如果攻擊者取得其中一個目錄的控制權,他們可以強制應用程式載入惡意的 DLL 副本,而非預期的 DLL。 這些攻擊被稱為「DLL 預載攻擊」,且在所有支援動態載入共享 DLL 函式庫的作業系統中都很常見。 這類攻擊的效果可能是攻擊者能在執行應用程式的使用者情境中執行程式碼。 當應用程式以管理員身份執行時,可能會導致本地權限提升。 我們知道這些攻擊的興趣重新燃起。 為了減少此問題對我們共同客戶的影響,我們將此文件發布給開發者社群,確保他們了解此問題並擁有必要的工具,能在他們的應用程式中解決此問題。

摘要

DLL預載攻擊的說明

基於 LoadLibrary 的攻擊

當應用程式動態載入 DLL 但未指定完整限定路徑時,Windows 會嘗試透過一組明確定義的目錄(稱為 DLL 搜尋順序)線性搜尋來定位該 DLL。 如果 Windows 在 DLL 搜尋順序中找到該 DLL,它就會載入該 DLL。 然而,如果 Windows 在 DLL 搜尋順序中找不到任何目錄中的 DLL,將會回傳 DLL 載入操作失敗。 以下是 LoadLibraryLoadLibraryEx 函式的 DLL 搜尋順序,這些函式用於動態載入 DLL:

  1. 應用程式載入的目錄
  2. 系統目錄
  3. 16 位元系統目錄
  4. Windows 目錄
  5. 目前CWD工作目錄 ()
  6. PATH 環境變數中列出的目錄

                
請考慮以下案例:

  • 應用程式載入 DLL 時,未指定預期在應用程式 CWD 中能找到的完整合格路徑。
  • 當應用程式未找到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 函式,我們不建議使用 SearchPath 函式作為尋找 .dll 檔案的方法。 這可能導致錯誤的 .dll 檔案,因為 SearchPath 函式的搜尋順序與 LoadLibrary 函式所使用的搜尋順序不同。 如果你需要找到並載入 .dll 檔案,請使用 LoadLibrary 函式。

ShellExecute 與 CreateProcess

當開發者呼叫類似函式(如 ShellExecuteCreateProcess )來載入外部執行檔時,也可能出現這些問題的變體。 我們建議開發者在載入二進位檔時要小心,並指定完整限定的路徑。 當你載入二進位檔而非函式庫時,這應該會帶來較簡單的複雜度。

我們建議開發者採取以下步驟:

  • 驗證他們的應用程式是否存在非安全函式庫載入 (本文後面會提供各項範例) 。 包括下列各項:

    • 使用 SearchPath 來識別函式庫或元件的位置。
    • 使用 LoadLibrary 來識別作業系統版本。
  • 盡可能使用完整合格的路徑來呼叫 LoadLibrary、CreateProcess 和 ShellExecute。

  • 實作對 SetDllDirectory 的呼叫,並使用空字串 (“”) ,在需要時將目前的工作目錄從預設 DLL 搜尋順序中移除。 請注意,SetDllDirectory 會影響整個流程。 因此,你應該在程序初始化初期做一次,而不是在呼叫 LoadLibrary 前後。 由於 SetDllDirectory 影響整個程序,多個執行緒以不同值呼叫 SetDllDirectory 可能會導致未定義的行為。 此外,若流程設計為載入第三方 DLL,則需測試以判斷建立整個流程的設定是否會導致不相容。 一個已知問題是,當應用程式依賴 Visual Basic for Applications 時,整個流程的設定可能導致不相容性。

  • 使用 SetSearchPathMode 功能來啟用該程序的安全搜尋模式。 此時,目前的工作目錄會移至搜尋路徑搜尋清單中,該程序在其整個生命週期中最後一個位置。

  • 避免使用 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。 這大幅降低風險,因為攻擊者必須控制應用程式目錄、Windows 目錄,或使用者路徑中指定的目錄,才能使用 DLL 預載攻擊。

    SetDllDirectory ("");
    HMODULE handle = LoadLibrary("schannel.dll");
    
  • 在所有安裝 MS09-014) 中所述安全更新963027 (的系統中,以下程式碼會永久將 CWD 移至搜尋順序的最後位置。 任何從該程序內部嘗試更改搜尋模式的 SetSearchPathMode 函式後續呼叫都會失敗。

    SetDllDirectory ("");
    HMODULE handle = LoadLibrary("schannel.dll");
    
    
  • 在以下程式碼範例中,目前的工作目錄會先從搜尋路徑中移除,然後再呼叫 LoadLibrary。 這大幅降低風險,因為攻擊者必須控制應用程式目錄、Windows 目錄,或使用者路徑中指定的目錄,才能使用 DLL 預載攻擊。

    SetSearchPathMode (BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE | BASE_SEARCH_PATH_PERMANENT );
    HMODULE handle = LoadLibrary("schannel.dll");
    
    
    

利用程序監控器動態偵測非安全負載

Microsoft 發布了一款名為 Process Monitor 的工具。 此工具使開發者與管理員能密切追蹤執行中的程序行為。 Process Monitor 可以用來動態偵測你的應用程式是否可能受到這類問題的影響。

  • 要下載 Process Monitor,請造訪以下 Microsoft 網頁:
    http://technet.microsoft.com/en-us/sysinternals/bb896645.aspx

  • 試著用 CWD 設定在特定目錄啟動你的應用程式。 例如,雙擊一個檔案副檔名,該檔案的檔案處理器是指派給你的應用程式的。

  • 請設置流程監控,並搭配以下篩選條件:

    371495f2-14de-f99c-c55a-f75d31fe9ca8

  • 如果有一條脆弱路徑被攻擊,你會看到類似以下的情況: 9acdd1ae-29b9-e499-9de9-8bc665b95e76

     呼叫遠端檔案共享載入 DLL 表示這是一個有漏洞的程式。

更多資訊

欲了解更多資訊,請造訪以下 Microsoft 網頁:

動態連結庫搜尋順序

http://msdn.microsoft.com/en-us/library/ms682586 (VS.85) .aspx MSDN 關於 SearchPath 函數的文件

http://msdn.microsoft.com/en-us/library/aa365527 (VS.85) .aspx MSDN 關於 LoadLibrary 函式的文件

http://msdn.microsoft.com/en-us/library/ms684175 (VS.85) .aspx MSDN 關於 SetDllDirectory 函式的文件

http://msdn.microsoft.com/en-us/library/ms686203 (VS.85) .aspx MSDN 文件中關於 SetSearchPathMode 函式的說明

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 MSRC 工程團隊 Andrew Roths 關於 DLL 預載攻擊的部落格文章

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

其他資源