老樹開新花:DLL劫持漏洞新玩法


 

 

DLL劫持漏洞已經是一個老生常談,毫無新鮮感的話題了。DLL劫持技術也已經是黑客們殺人越貨,打家劫舍必備的武器。那么,隨着Win10的誕生,微軟是否已經修復了此漏洞?同時在當前的安全環境下,DLL劫持漏洞是否又有新的利用方式和價值?

本文將對DLL劫持漏洞的原理,漏洞實現,漏洞挖掘,漏洞利用,漏洞防御等方面進行分析。

0×01 DLL劫持漏洞原理

在Windows系統中,為了節省內存和實現代碼重用,微軟在Windows操作系統中實現了一種共享函數庫的方式。這就是DLL(Dynamic Link Library),即動態鏈接庫,這種庫包含了可由多個程序同時使用的代碼和數據。

每個DLL都有一個入口函數(DLLMain),系統在特定環境下會調用DLLMain。在下面的事件發生時就會調用dll的入口函數:

1.進程裝載DLL。

2.進程卸載DLL。

3.DLL在被裝載之后創建了新線程。

4.DLL在被裝載之后一個線程被終止了。

另外,每個DLL文件中都包含有一個導出函數表也叫輸出表(存在於PE的.edata節中)。使用一些PE文件查看工具如LoadPE,就可以查看導出函數的符號名即函數名稱和函數在導出函數表中的標識號。

應用程序導入函數與DLL文件中的導出函數進行鏈接有兩種方式:隱式鏈接(load-time dynamic linking)也叫靜態調用和顯式鏈接(run-time dynamic linking)也叫動態調用。隱式鏈接方式一般用於開發和調試,而顯式鏈接方式就是我們常見的使用LoadLibary或者LoadLibraryEx函數(注:涉及到模塊加載的函數有很多)來加載DLL去調用相應的導出函數。調用LoadLibrary或者LoadLibraryEx函數時可以使用DLL的相對路徑也可以使用絕對路徑,但是很多情況下,開發人員都是使用了相對路徑來進行DLL的加載。那么,在這種情況下,Windows系統會按照特定的順序去搜索一些目錄,來確定DLL的完整路徑。關於動態鏈接庫的搜索順序的更多詳細資料請參閱MSDN。根據MSDN文檔的約定,在使用了DLL的相對路徑

調用LoadLibrary函數時,系統會依次從下面幾個位置去查找所需要調用的DLL文件。

1.程序所在目錄。

2.加載 DLL 時所在的當前目錄。

3.系統目錄即 SYSTEM32 目錄。

4.16位系統目錄即 SYSTEM 目錄。

5.Windows目錄。

6.PATH環境變量中列出的目錄

微軟為了防止DLL劫持漏洞的產生,在XP SP2之后,添加了一個SafeDllSearchMode的注冊表屬性。注冊表路徑如下:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session

Manager\SafeDllSearchMode

當SafeDllSearchMode的值設置為1,即安全DLL搜索模式開啟時,查找DLL的目錄順序如下:

1.程序所在目錄

2.系統目錄即 SYSTEM32 目錄。

3.16位系統目錄即 SYSTEM 目錄。

4.Windows目錄。

5.加載 DLL 時所在的當前目錄。

6.PATH環境變量中列出的目錄。

但是經過筆者測試發現,XP系統之后發布的Windows操作系統中,默認情況下並未開啟安全DLL搜索模式。但是這是否意味着DLL劫持漏洞就可以輕易被利用呢?微軟為了更進一步的防御系統的DLL被劫持,將一些容易被劫持的系統DLL寫進了一個注冊表項中,那么凡是此項下的DLL文件就會被禁止從EXE自身所在的目錄下調用,而只能從系統目錄即SYSTEM32目錄下調用。注冊表路徑如下:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs

下面是不同的Windows系統中默認情況下的KnownDLLs注冊表項的截圖。

Win2k3:

 

Win7:

 

Win8.1:

 

Win2K8:

 

Win10:

 

通過上面的截圖我們可以發現,以前經常使用的一些劫持DLL已經被加入了KnownDLLs注冊表項,這就意味着使用諸如usp10.dll,lpk.dll,ws2_32.dll去進行DLL劫持已經失效了。

Windows操作系統通過“DLL路徑搜索目錄順序”和“KnownDLLs注冊表項”的機制來確定應用程序所要調用的DLL的路徑,之后,應用程序就將DLL載入了自己的內存空間,執行相應的函數功能。

不過,微軟又莫名其妙的允許用戶在上述注冊表路徑中添加“ExcludeFromKnownDlls”注冊表項,排除一些被“KnownDLLs注冊表項”機制保護的DLL。也就是說,只要在“ExcludeFromKnownDlls”注冊表項中添加你想劫持的DLL名稱就可以對該DLL進行劫持,不過修改之后需要重新啟動電腦才能生效。

在上述描述加載DLL的整個過程中,DLL劫持漏洞就是在系統進行安裝“DLL路徑搜索目錄順序”搜索DLL的時候發生的。無論安全DLL搜索模式是否開啟,系統總是首先會從程序所在目錄加載DLL,如果沒有找到就按照上面的順序依次進行搜索。那么,利用這個特性,攻擊者就可以偽造一個相同名稱,相同導出函數表的一個“假”DLL,並將每個導出函數轉向到“真”DLL。將這個“假”DLL放到程序的目錄下,當程序調用DLL中的函數時就會首先加載“假”DLL,在“假”DLL中攻擊者已經加入了惡意代碼,這時這些惡意代碼就會被執行,之后,“假”DLL再將DLL調用流程轉向“真”DLL,以免影響程序的正常執行。

0×02  如何編寫一個劫持的DLL?

在挖掘DLL劫持漏洞之前,需要明白如何通過編程編寫一個用於劫持的DLL。

編寫一個用於劫持指定DLL的DLL文件,需要兩個步驟:

1.查看被劫持的DLL的導出函數表。

2.編程實現劫持DLL向原DLL的導出函數的轉發,並加入你的“惡意代碼”。

使用DLL_Hijacker.py腳本可以一鍵生成劫持指定DLL的CPP源碼文件。對這個CPP文件進行編譯就生成了相應的劫持DLL文件。

 

0×03 劫持Windows系統的DLL

要分析一個應用程序是否存在劫持系統DLL的漏洞,需要這么幾個步驟:

1.啟動應用程序

2.使用Process Explorer等類似軟件查看該應用程序啟動后加載的動態鏈接庫。

3.從該應用程序已經加載的DLL列表中,查找在上述“KnownDLLs注冊表項”中不存在的DLL。

4.編寫第三步中獲取到的DLL的劫持DLL。

5.將編寫好的劫持DLL放到該應用程序目錄下,重新啟動該應用程序,檢測是否劫持成功。

接下來,以微信PC客戶端為例,測試系統為Win8.1:

運行微信PC客戶端后,使用Process Explorer軟件查看已加載的DLL如下:

 

與Win8.1的“KnownDLLs注冊表項”進行對比:

 

以msls31.dll為例,使用DLL_Hijacker.py生成源碼CPP文件,編譯后,將生成好的msls31.dll放到微信PC客戶端的目錄下:

 

啟動微信PC客戶端,可以發現劫持DLL中的“惡意代碼”已經執行了。從這個消息框的圖標也能看出來,微信PC客戶端進程已經加載了這個被用於劫持msls31.dll的DLL文件。點擊確定之后,微信主窗口就會加載顯示,並不影響原應用程序的執行。

 

在實際的測試當中,並不是每一個被宿主進程加載但不在“KnownDLLs注冊表項”中的DLL能夠成功劫持,如ntdll.dll等系統核心dll已經被微軟做了保護。其他的dll可能是宿主進程在調用LoadLibrary函數時使用了“絕對路徑”,另外,個別系統內置的DLL文件的導出表中會有額外的附加數據,要對這類DLL進行劫持,必須對這些額外的附加數據也做處理才能成功劫持,最常見的情況是DLL能夠成功劫持,但是會影響原應用程序的正常執行。

為了實現自動化測試DLL劫持漏洞,我編寫了一個PowerShell腳本,可以檢測出宿主進程在啟動后加載的DLL模塊,並分析出在“KnownDLLs注冊表項”中不存在的已加載的DLL,當然,這個腳本只能粗略的分析出哪些DLL可能存在劫持漏洞,要實現完全自動精准的分析工具,只需要把DLL_Hijacker.py的功能和DLLHijack_Detecter.ps1的功能結合在一起即可。

 

 

0×04  劫持應用程序的DLL

和劫持應用程序的DLL相比,劫持系統本身的DLL通用性更強,例如,筆者測試發現msimg32.dll依舊可以劫持大多數應用程序。這個DLL已經多次被黑客用於DLL劫持執行惡意代碼。但是劫持應用程序的DLL比劫持系統本身的DLL隱蔽性更好,安全軟件難以判斷這種“劫持行為”。

劫持應用程序的DLL的步驟與劫持系統本身的DLL類似,這里就不再贅述,只是要注意有些DLL是在功能執行中動態加載的並非啟動了應用程序時就會加載。

這里依舊使用微信PC客戶端為例,觀察微信安裝目錄的DLL:

 

 

根據個人經驗結合應用程序的一些功能和DLL文件名可以判斷出部分DLL的功能,例如:

PrScrn.dll是提供截圖功能的DLL,查看其導出函數只有一個PrScrn且不需要參數就可以調用,使用如下命令可以驗證:

Rundll32.exe [微信安裝目錄]\PrScrn.dll,PrScrn

接下來,以PrScrn.dll為例,進行DLL劫持。使用DLL_Hijacker.py生成DLL源碼后對下面的代碼進行修改,例如將PrScrn.dll修改為PrScrn_Origial.dll。

 

編譯后,將原來的PrScrn.dll改名為PrScrn_Origial.dll並將生成的PrScrn.dll放到微信安裝目錄。

 

登錄微信,點擊截圖的圖標,就可以發現彈框了。

 

對於類似於PrScrn.dll這樣導出函數無參數的DLL還可以使用常規的一個DLL動態調用另外一個DLL的方式來劫持,主要代碼如下:

#include "stdafx.h"

extern "C" __declspec(dllexport) void PrScrn();

 

BOOL APIENTRY DllMain(HANDLE hModule,

DWORD  ul_reason_for_call,

LPVOID lpReserved

)

{

    //MessageBox(NULL, "DLL Hijack! by DLLHijacker!", ":)", 0);

    return TRUE;

}

 

void PrScrn()

{

    MessageBox(NULL, "DLL Hijack! by DLLHijacker!", ":)", 0); //your evil code

    HINSTANCE hDllInst = LoadLibrary("PrScrn_Origial.dll");

    if (hDllInst)

    {

        typedef DWORD(WINAPI *EXPFUNC)();

        EXPFUNC exportFunc = NULL;

        exportFunc = (EXPFUNC)GetProcAddress(hDllInst, "PrScrn");

        if (exportFunc)

        {

            exportFunc();

        }

        FreeLibrary(hDllInst);

    }

    return;

}

0×04 DLL劫持漏洞的防御

對於DLL劫持漏洞產生的原因,並不能單一的歸咎於微軟,只能說這是微軟的一個“設計缺陷”,要從根本上防御DLL劫持漏洞,除了微軟提供的“安全DLL搜索模式”和“KnownDLLs注冊表項”機制保護DLL外,開發人員必須要做更多來保護應用程序自身。開發過程中,調用LoadLibrary,LoadLibraryEx等會進行模塊加載操作的函數時,使用模塊的物理路徑作為參數。在程序調用DLL時使用“白名單”+ “簽名”進行DLL的驗證。或者在開發應用程序時,在代碼開頭調用SetDllDirectory函數,把當前目錄從DLL的搜索順序列表中刪除,不過這個API只能在打了KB2533623補丁的Windows7,2008上使用。-_-!

不過即使使用了這些防御措施,DLL劫持漏洞依舊可能會存在,更何況目前很多廠商對於DLL劫持漏洞都是持“忽略”的態度。

0×05 總結

從DLL劫持漏洞產生的原理可以分析出,由於微軟的這個“設計缺陷”以及開發人員的不規范編碼,使得此漏洞可以在很多情況下都會產生,其危害程度根據不同類型的劫持方式也有不同,最典型危害最直接的莫過於存在“文件后綴關聯”的應用程序的DLL劫持,只要將相關聯的后綴文件和制作好的劫持DLL放在同一目錄,打開關聯后綴的文件時應用程序就會加載制作好的劫持DLL。只要執行了攻擊者的惡意代碼,那么攻擊者就有權限做很多事情。

另外,在挖掘此類漏洞時也要注意到不存在劫持漏洞的DLL如果調用了一個或多個其他DLL,那么依舊有可能會產生DLL劫持漏洞。如果宿主進程已經取得了UAC權限,那么DLL劫持也是另外一種獲取UAC權限的方式,同時,由於DLL劫持漏洞的隱蔽性,安全軟件難以捕捉檢測,很多惡意軟件使用此漏洞作為后門的通道或者自啟動技術。在此次測試中,筆者發現國內很多用戶使用率較高的應用程序均存在DLL劫持漏洞。由此看來,DLL劫持漏洞本身不算高危漏洞但是如果將此漏洞與其他一些攻擊技術進行捆綁組合,危害程度將會大幅提升。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM