本文原創做者:絲綢之路html
DLL劫持漏洞已是一個老生常談,毫無新鮮感的話題了。DLL劫持技術也已是黑客們殺人越貨,打家劫舍必備的武器。那麼,隨着Win10的誕生,微軟是否已經修復了此漏洞?同時在當前的安全環境下,DLL劫持漏洞是否又有新的利用方式和價值?git
本文將對DLL劫持漏洞的原理,漏洞實現,漏洞挖掘,漏洞利用,漏洞防護等方面進行分析。github
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,就能夠查看導出函數的符號名即函數名稱和函數在導出函數表中的標識號。ide
應用程序導入函數與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劫持漏洞自己不算高危漏洞可是若是將此漏洞與其餘一些攻擊技術進行捆綁組合,危害程度將會大幅提高。
* 做者:絲綢之路,本文屬FreeBuf原創獎勵計劃文章,未經許可禁止轉載。
jpg 改 rar