DLL劫持漏洞解析
黑客手中的全新武器
艾瑞2012年初發布的中國2011年下半年個人網絡安全報告中,包含這么一個數據:2011年下半年十大熱點木馬中,其中5個是利用DLL劫持漏洞來對系統進行破壞的。DLL劫持漏洞到底是何方神圣,竟然占領了木馬技術的半壁江山?

圖1 艾瑞安全報告中利用DLL劫持技術的熱門木馬截圖
DLL(Dynamic Link Library),全稱動態鏈接庫,是Windows系統上程序正常運行必不可少的功能模塊,是實現代碼重用的具體形式。簡單的說,可以把DLL理解成幫助程序完成各種功能的組件。
DLL劫持漏洞(DLL Hijacking Exploit),這個名字緣起微軟在2010年8月23日發布的2269637號安全公告。這個漏洞是通過一些手段來劫持或者替換正常的DLL,欺騙正常程序加載預先準備好的惡意DLL的一類漏洞的統稱。利用DLL劫持漏洞,病毒木馬可以隨著文檔的打開(或者其他一些程序正常行為)而激活自身,進而獲得系統的控制權。
我們來看看DLL劫持漏洞到底是何方神圣。#p#
高危害利用形式的爆發
DLL劫持漏洞伴隨著Windows而存在,但是一直并未得到大家的足夠重視。
直到2010年8月份,有黑客在安全論壇上公布了一種危害極高的DLL劫持漏洞的利用形式,迅速引起強烈的反應。隨后,著名安全組織exploit-db公布了一系列存在DLL劫持漏洞的軟件列表,其中可以發現大家十分熟悉的應用軟件,包括:AutoCAD 2007、Daemon Tools、Winamp、Media Player Classic、Mozilla Thunderbird、Microsoft Office、Adobe Photoshop、Adobe Dreamweaver CS5、Skype、Snagit10、Safari、WinDVD、Opera、Firefox等等。

圖2 PCHOME針對DLL劫持漏洞的新聞截圖
簡單的講,安裝了上述軟件的電腦,當用戶在打開一個圖片、音樂、視頻、BT種子、網頁文件都有可能感染病毒。當攻擊者利用DLL劫持漏洞構造一個特殊的DLL文件,將這個DLL文件和一些JPG、PPT、MP3、MPG、HTML文件共同打包,用戶解壓后雙擊打開這些文檔,病毒即被立即觸發。也就是說,不需要其他漏洞,不需要可執行文件,只需要鼠標雙擊打開別人發給你的音樂、視頻或者圖片文件,就可能感染病毒!難怪國外安全公司authentium在官方博客中描述DLL劫持漏洞時,甚至用"The world is going to end!"做標題。
不過此次風波,隨著網民們的關注和各個產品漏洞的逐步修復,逐漸平息下去。#p#
演變和進化
DLL劫持漏洞的生命力絕不僅限于打開文件這種觸發這種形式。通過這種形式的啟發,黑客們逐漸找到了DLL劫持漏洞的更大活力。二年之后的今天,當我們再看這個漏洞時,它已搖身一變更具威脅了。
了解客戶端安全的同學應該都知道,現在的安全軟件大都會采取白名單機制,把一些大公司的程序放在安全軟件的白名單中,白名單中的程序不會進行監控并默認信任,以減少誤報率。
都說安全性和易用性是相悖的。白名單這個特性是使安全軟件和受信任程序的使用都方便了不少,但是,同時也更加方便了廣大黑客。我們都知道,軟件工作時都需要這樣那樣的DLL功能組件,那么在安全軟件白名單信任策略支持下,即使“安全軟件”要加載的DLL是被替換掉的惡意DLL,安全軟件都會被認為是合法、正常的行為。因此,黑客們就開始利用各大公司軟件中存在的DLL劫持漏洞,堂而皇之的在安全軟件眼皮底下加載早已準備好的惡意代碼,入侵用戶的計算機。至此,更具“劫持”特性的廣義DLL劫持漏洞正式誕生。
本文開頭艾瑞報告中提到的那5個DLL劫持木馬,都屬于這種漏洞形式。是的,你沒有聽錯,就是它,占領木馬激活技術的半壁江山。由于效果極好,是黑客們殺人放火,打家劫舍的必備大殺傷性武器。#p#
刨根到底看原理
DLL劫持漏洞之所以被稱為漏洞,還要從負責加載DLL的系統API LoadLibrary來看。熟悉Windows代碼的同學都知道,調用LoadLibrary時可以使用DLL的相對路徑。這時,系統會按照特定的順序搜索一些目錄,以確定DLL的完整路徑。根據MSDN文檔的約定,在使用相對路徑調用LoadLibrary(同樣適用于其他同類DLL LoadLibraryEx,ShellExecuteEx等)時,系統會依次從以下6個位置去查找所需要的DLL文件(會根據SafeDllSearchMode配置而稍有不同)。
1. 程序所在目錄;
2. 系統目錄;
3. 16位系統目錄;
4. Windows目錄;
5. 當前目錄;
6. PATH環境變量中的各個目錄。
而所謂的劫持的,就發生在系統按照順序搜索這些特定目錄時。只要黑客能夠將惡意的DLL放在優先于正常DLL所在的目錄,就能夠欺騙系統優先加載惡意DLL,來實現“劫持”。
微軟的“設計缺陷”?
從上面的原理來看,LoadLibrary API并不會去檢查即將要加載進來的DLL是好人還是壞人,不管是李逵還是李鬼都有酒喝、有肉吃。這點我們能理解,畢竟判斷DLL是好是壞不是系統的事情,而是安全軟件的事情。
同時,當使用相對路徑調用這個API加載DLL時,就會觸發上一節所述的神奇的動態鏈接庫搜索邏輯。由于系統搜索的位置非常多,而且其中的許多位置都能輕易被黑客劫持和控制,給了DLL劫持漏洞很大的利用空間。
但是,微軟認為以上這些屬于系統特性,不屬于安全漏洞而不做修改,微軟官方的介紹及安全建議請參考這里:Dynamic-Link Library Search Order,Dynamic-Link Library Security。
比較有意思的是,從Windows 7的KB2533623補丁開始,微軟給我們帶來了三個解決DLL劫持問題的新API:SetDefaultDllDirectories,AddDllDirectory,RemoveDllDirectory。這幾個API配合使用,可以有效的規避DLL劫持問題。可惜的是,這些API只能在打了KB2533623補丁的Windows7,2008上使用。#p#
Satellite DLL Hijacking
微軟從VS2003/MFC7.0開始就提供對Satellite DLL的增強支持,主要用于創建針對多種語言進行本地化的應用程序。Satellite DLL屬于純資源DLL,它包含應用程序針對特定語言進行本地化的資源。當應用程序開始執行時,MFC會自動加載最適合于當前運行環境的本地化資源,比如在中文系統上,應用程序會優先使用中文資源,若是英文系統,則使用英文資源。Satellite DLL的命名規范為ApplicationNameXXX.dll,其中ApplicationName是使用MFC的*.EXE或者*.DLL的名稱,而XXX為由資源語言的三個字母組成,比如中文為CHS,英文為ENU。由于經MFC編譯的應用程序會自動加載相應語言的本地化資源(即Satellite DLL),且對加載的資源文件的合法性未作有效檢測,因此可能被惡意程序利用DLL劫持來實現任意代碼執行。
MFC主要按照以下順序加載每種語言的資源DLL,找到一個后將停止加載下面的其它DLL:
1. 當前用戶的默認界面語言(UI Language)(適用于 Windows 2000 或更高版本),該語言從 GetUserDefaultUILanguage() Win32 API 返回;
2. 當前用戶的默認界面語言(適用于Windows 2000 或更高版本),沒有任何特定子語言(即 ENC [加拿大英語] 變成 ENU [美國英語]);
3.系統的默認界面語言(適用于Windows 2000 或更高版本),這是從 GetSystemDefaultUILanguage() API 返回的語言。 在其他平臺上,這是操作系統本身的語言;
4. 系統的默認界面語言,沒有任何特定的子語言;
5. 具有 3 個字母代碼 LOC 的“虛設”語言。
比如使用MFC的應用程序Example.exe,系統的用戶界面語言是ENU(美國英語),而當前用戶的用戶界面語言為FRC(加拿大法語),那么MFC將按照以下順序查找資源DLL文件:
1. ExampleFRC.dll(當前用戶的界面語言,此例為加拿大法語);
2. ExampleFRA.dll(當前用戶的界面語言,不包含子語言,此例為法語(法國));
3. ExampleENU.dll(系統默認的用戶界面語言,此例為美國英語);
4. ExampleLOC.dll。
如果未找到上面DLL中任何一個,則MFC會使用Example.exe自帶的資源。當應用程序自帶有界面語言資源DLL文件時,它也會按照上述順序來加載。比如在Win7簡體中文系統上,應用程序Example.exe擁有自己的UI資源文件MainUI.dll,那么就會首先去加載MainUICHS.dll。
因此,惡意程序如果在程序相當目錄下,比如上面MainUI.dll所在目錄下,再放置一個包含惡意代碼的MainUICHS.dll后,打開主程序Example.exe后,就會優先加載包含惡意代碼的MainUICHS.dll,而非MainUI.dll,從而執行任意代碼,如下圖所示:

由于多數MFC程序默認會動態鏈接mfc70.dll、mfc80.dll、mfc90.dll等dll文件(具體文件名與MFC版本號相關),因此在中文系統下,如果在相應的程序目錄下放置mfc**chs.dll或者mfc**loc.dll都有可能達到DLL劫持的目的。#p#
DLL加載安全之道
首先,對于會打開圖片、音樂等各種類型文件的程序,分為以下三種情況處理:
A. 對于外部第三方DLL和自己的DLL:
1. 使用LoadLibrary API加載DLL時使用絕對路徑,類似的情況還包括其他API如LoadLibraryEx, CreateProcess, ShellExecute等;
2. 將所有需要使用到的DLL放在應用程序所在的目錄,不放到系統目錄或者其他目錄。
B. 對于系統共享的DLL(如user32.dll, mfc80loc.dll等),不能放到程序目錄下時,應該:
1. 使用絕對路徑加載;
2. 對于無法準確預測絕對路徑的情況,可以使用相對路徑加載。
C. 程序啟動時調用API SetDllDirectory(L"")將當前目錄從DLL加載順序中移除.
其次,對于廣義DLL劫持漏洞,我們只有一種有效的處理辦法:加載任何DLL前先校驗文件的簽名,簽名沒有問題的才能加載。
檢測方案
DLL劫持漏洞的檢測比較簡單,可以在虛擬環境將程序運行起來,對其中的DLL加載操作進行審計,找出加載順序中可能被劫持的點;還可以為程序構建一個“劫持現場”(自己做一個劫持并轉發給正常DLL的DLL文件看是否執行其中代碼),并檢查程序能否真正規避DLL劫持的威脅。