作者:0x7F@知道創宇404實驗室
時間:2021年9月16日
0x00 前言
2020年12月,SolarWinds 攻擊事件引發全球的關注(https://us-cert.cisa.gov/ncas/alerts/aa20-352a),攻擊團隊在 2020年上旬通過對 SolarWinds Orion 產品實現供應鏈攻擊,導致諸多廠商被攻擊,造成了不可估量的損失。這種國家間的 APT 攻擊包含了大量的技術細節,其中供應鏈攻擊的實現,也就是 SUNBURST 后門植入這一塊引起了我極大的興趣。
2021年1月,網上公開了 SUNBURST 后門植入的分析,后續又有安全研究者對植入細節進一步的優化,根據這些內容我展開了對 dll 劫持的學習和研究。本文對 dll 劫持進行了詳細的介紹,并模仿 SUNBURST 后門植入的方法,嘗試對 C 編譯器實現"供應鏈攻擊"。
本文測試環境為 Windows7 x64 sp1,開發環境為:MinGW-x64(gcc-8.1.0) + Python3.6。
0x01 后門植入和優化
我們可以簡單看看 SUNBURST 后門植入的過程(https://www.crowdstrike.com/blog/sunspot-malware-technical-analysis/):由一個名為 taskhostsvc.exe 的程序進行完成,該程序通過計劃任務設置隨主機啟動運行。
taskhostsvc.exe 啟動后通過創建互斥體保證只有一個實例在運行,然后每秒從進程中搜索 MsBuild.exe 進程(Microsoft Build Engine),找到后通過讀取 MsBuild.exe 的內存,從命令行參數中獲取構建項目的目錄路徑;
隨后在項目目錄下尋找 Orion 產品的 InventoryManager.cs 源碼文件,并使用包含有惡意代碼的源碼文件進行替換,等待 MsBuild.exe 編譯完成,最后再還原該文件,完成后門的植入;如下圖
當然這個過程中還需要很多技術細節來保證后門與原始項目代碼之間的兼容性,以及植入過程的隱蔽性等等;后續就有安全研究者說到在 APT 攻擊中上文中的植入過程不夠完美,比如計劃任務和周期性的進程掃描很容易暴露攻擊行為,其次監控 MsBuild.exe 運行到最終替換源碼文件,這中間的執行時間可能影響后門植入的成功率。
安全研究者提出了使用 dll 劫持來優化后門植入的過程(https://www.a12d404.net/ranting/2021/01/17/msbuild-backdoor.html),其研究過程發現 MSBuild.exe 啟動過程中會去優先加載指定目錄的 dll,如下:
如果我們將惡意 dll 重命名并放置在這些 "load-not-found-dll" 的路徑下,就可以實現 dll 劫持,執行我們的惡意代碼,原文作者根據這種方式編寫代碼進行了演示。相比于 taskhostsvc.exe,使用這種方式就不需要額外的進程來進行監控了,并且 dll 在程序執行前加載、在程序執行后釋放,這個時間點也很適合用于對程序進行控制和清理。
0x02 dll劫持概要
dll(動態鏈接庫)作為 windows 的函數庫,有助于促進代碼的模塊化、代碼重用、有效的內存使用并減少磁盤空間;一個應用程序運行時可能需要依賴于多個 dll 的函數才能完成功能,如果控制其中任一 dll,那么便可以控制該應用程序的執行流程。
要學習 dll 劫持,那必須先了解 dll 的搜索順序,這也是攻防的兵家必爭之地,微軟近年來也不斷的在這一塊進行加固,對于桌面程序(非UWP應用)目前默認 dll 的搜索順序為(https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order):
- 應用程序加載的目錄
- 系統目錄,使用 GetSystemDirectory 獲取該路徑
- 16 位系統目錄
- Windows 目錄,使用 GetWindowsDirectory 獲取該路徑
- 當前目錄
- PATH 環境變量中列出的目錄
默認情況下
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode處于開啟狀態;如果手動設置為 0,關閉該安全選項,搜索順序為:在以上順序基礎上,將5.當前目錄修改至2.系統目錄的位置,其他順移。
應用程序加載 dll 時如果僅指定 dll 名稱時,那么將按照以上順序搜索 dll 文件;不過在加載之前還需要滿足以下兩條規范:
- 當內存中已加載相同模塊名稱的 dll 時,系統將直接加載該 dll,不會進行搜索;除非設置了 dll 重定向選項
- 如果要加載的 dll 模塊屬于 Known DLLs,系統直接加載系統目錄下的該 dll,不會進行搜索;Known DLLs 列表:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs
由于 dll 迭代更新可能出現不兼容的問題,微軟提出 dll 重定向解決方案,以便應用程序可以自定義選擇加載 dll
在了解以上基礎內容后,在 dll 搜索路徑上對文件進行替換,那么便可以實現 dll 劫持。
微軟在 dll 這一塊所做的安全加固詳情可以參考:https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-security
0x03 dll函數轉發
使用惡意 dll 替換原文件,應用程序便可以加載我們的 dll 并執行惡意代碼,但是應用程序運行依賴于 dll 提供的函數,惡意 dll 必須提供相同的功能才能保證應用程序的正常運行;所以我們先來了解下 dll 函數轉發。
1.手動轉發
當 dll 的導出函數比較少時,我們可以按照正常的 dll 開發流程,逐個定義函數名稱,然后在函數內部使用 LoadLibrary() 函數調用原 dll 的對應函數完成功能,如下:
2.def文件
當 dll 的導出函數太多時,我們就不能手動轉發了,使用模塊定義(.def)文件編寫導出函數的信息,由鏈接器自動實現函數轉發,細節可以參考 https://docs.microsoft.com/en-us/cpp/build/reference/module-definition-dot-def-files?view=msvc-160。
我們嘗試生成 version.dll 的惡意 dll:在 test.c 文件中編寫惡意代碼插入到 DllMain 的執行流程中,并在 test.def 中編寫函數轉發規則:
隨后進行編譯生成 dll 文件,gcc 編譯如下:
gcc -Wall -shared test.c test.def -o version.dll
在 .def 文件中我們將原始的文件命名為 version_origin.dll,當應用程序運行時將加載我們惡意的 version.dll,當調用函數時,將由惡意的 version.dll 進行函數轉發:
something.exe => version.dll (malware) => version_origin.dll
這里我編寫了個 Python 腳本可以根據 dll 自動生成模塊定義文件dllproxy_def_generate.py,使用如下:
部分 dll 導出函數沒有導出名稱,只有導出序號,Gcc 和 Tcc 不支持按序號導出的函數轉發,讀者遇到的話可以使用 VisualStdio
3.pragma預處理
除了上文使用模塊定義文件來實現函數轉發,還可以使用 pragma 來實現,這一塊細節可以參考 https://docs.microsoft.com/en-us/cpp/preprocessor/comment-c-cpp?view=msvc-160,同樣以 version.dll 為例,函數轉發的源碼如下:
不過
pragma關鍵詞只有 Microsoft 編譯器提供。
0x04 路徑劫持
根據以上知識,我們可以自由的生成惡意 dll 文件,并且通過函數轉發使其調用原始的 dll 函數,完全不會影響應用程序的正常運行。除此之外,惡意代碼我們一般可以添加到 DllMain 中,這樣在加載 dll 時便可以觸發代碼,或者添加到指定函數中,精確劫持程序流程,這取決于實際的場景。到這里我們的惡意 dll 就已經準備就緒了。
根據 dll 的類型,我們可以將劫持大致可以分為兩種方式:
1.自定義dll
有些應用程序使用了自定義 dll,這個 dll 是該應用程序特有的,只會被該程序加載和使用。該 dll 可能放置在應用程序同目錄下,或者 PATH 環境變量中,或者特定目錄通過 LoadLibrary([路徑]) 來加載。
這種我們使用惡意 dll 替換目標文件,然后再將原始 dll 重命名并放置在應用程序同目錄下(以便函數轉發可以順利進行),當應用程序啟動時就可以加載我們的惡意 dll。
2.公共dll
當然我們還可以對公共 dll 進行劫持(比如系統 dll user32.dll),但是公共 dll 一般都會提前被其他進程加載,當新的應用程序需要加載時,將直接從內存進行加載和調用,如果我們使用惡意 dll 替換了公共 dll,需要通過重啟才能生效。
比如下面是我們使用惡意 dll 替換 msvcrt.dll,惡意 dll 在加載時輸出應用程序路徑,重啟后可以看到:
由于是公共 dll,那么所有的程序都會加載惡意 dll,這種方法可以用于監控、蜜罐等場景。
1.替換系統dll,可以用普通用戶修改文件為擁有者,然后再設置讀寫權限,就可以修改和替換文件了
2.不能劫持ntdll.dll / kernel32.dll等非常底層的 dll,因為這些 dll 實現了程序裝載、函數轉發等功能
0x05 dll重定向劫持
在我們的學習過程中發現,有些應用程序只依賴了系統 dll,并且這些 dll 已經被其他程序加載了,比如 MinGW(gcc) 只依賴了 kernel32.dll 和 msvcrt.dll,除了上文公共 dll 劫持,還有其他更好的辦法嗎?
在「0x02 dll劫持概要」中我們還提到一種特例:dll 重定向(https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-redirection),當模塊名稱相同的 dll 已經被其他應用程序加載到內存中時,可以使用該方法強制加載指定的 dll 文件。通過這種方式,也可以實現 dll 劫持。
dll重定向默認為關閉狀態,我們在注冊表中 HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options 添加 DevOverrideEnable (DWORD) 字段并設置為 1,來開啟該功能,重啟后生效。
我們有兩種方式來使用 dll 重定向:
1. .local
在應用程序同目錄下,創建 AppName.exe.local 的目錄,應用程序啟動時將優先從該目錄下加載 dll 文件。
我們編寫了個 HelloWorld 的 C 程序,生成惡意的 msvcrt.dll 進行演示,目錄結構為:
.
├── test.exe.local
│?? ├── msvcrt.dll
│?? └── msvcrt_origin.dll
├── helloworld.c
└── test.exe
運行演示如下:
2.manifest
還可以使用 manifest 配置文件(xml文件),優先級高于 .local,詳細可以參考 https://docs.microsoft.com/en-us/windows/win32/sbscs/application-manifests,構建目錄結構如下:
.
├── helloworld.c
├── msvcrt_origin.dll
├── msvcrt.dll
├── msvcrt.dll.manifest
├── test.exe
└── test.exe.manifest
其中 manifest 文件內容為:
同樣可以加載惡意 dll 文件。
0x06 Tcc劫持實現
Tcc(Tiny C Compiler) 是一個相當小的 C 編譯器,我們從簡單的開始對 C 編譯器進行"供應鏈攻擊"。
我這里使用了 Tcc 0.9.27 版本,結合逆向分析可以確定 tcc.exe 依賴了同目錄下的 libtcc.dll 文件,直接替換該 dll 文件即可。
我們模仿 SUNBURST 后門植入的方法,編寫惡意代碼 demo 如下(libtcc.c + libtcc.def):
編譯后使用惡意 dll 替換 libtcc.dll,并將原始文件重命名為 libtcc_origin.dll,運行演示如下:
可以看到通過 Tcc 編譯的程序,執行時觸發了惡意代碼 backdoor。
0x07 Gcc劫持實現
我們再來嘗試下 Gcc(MinGW),通過分析發現他只依賴了 kernel32.dll 和 msvcrt.dll,那么這里我們劫持 msvcrt.dll 文件,使用 dll 重定向的方法讓 Gcc 加載惡意 dll 文件。
使用 Tcc 中的測試代碼進行編譯,然后在 Gcc 目錄下添加 gcc.exe.local 文件夾,并將 msvcrt.dll / msvcrt_origin.dll 放在文件夾下,如下:
運行演示如下:
0x08 總結
在這里感謝 DAWU@知道創宇404實驗室 小伙伴在我學習研究過程中提供的幫助。本文從 dll 劫持的基礎出發,逐步講解和演示 dll 劫持,對 dll 劫持的部分場景和利用進行說明,最后模仿了 SUNBURST 后門植入的方法實現了對 C 編譯器的"供應鏈攻擊"。
實際上文中提到的對編譯器進行"供應鏈攻擊"的方法還可以進一步優化,因為我們的方法會重新寫入文件,從而會修改文件的寫入時間,可能會暴露攻擊行為;我們還可以通過逆向分析編譯器的執行流程,更加精確的劫持讀文件的函數,在內存中植入惡意代碼,讀者可以自行嘗試下。
不過 dll 劫持的攻防對抗已經發展很長時間了,微軟在保證功能的前提下,已經提供了較為完善的防御措施;對于上文介紹的劫持方法和場景,防御時可以按照文中的技術細節如:路徑、注冊表、文件進行排查。
References:
https://us-cert.cisa.gov/ncas/alerts/aa20-352a
https://www.crowdstrike.com/blog/sunspot-malware-technical-analysis/
https://www.a12d404.net/ranting/2021/01/17/msbuild-backdoor.html
https://en.wikipedia.org/wiki/2020_United_States_federal_government_data_breach
https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order
https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-redirection
https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-security
https://docs.microsoft.com/en-us/cpp/build/reference/module-definition-dot-def-files?view=msvc-160
https://docs.microsoft.com/en-us/cpp/preprocessor/comment-c-cpp?view=msvc-160
https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-redirection
https://docs.microsoft.com/en-us/windows/win32/sbscs/application-manifests
https://www.exploit-db.com/docs/english/13140-api-interception-via-dll-redirection.pdf
https://github.com/erocarrera/pefile
https://github.com/tothi/dll-hijack-by-proxying
https://stackoverflow.com/questions/37252457/is-there-a-way-to-make-windows-7-x64-load-ntdll-dll-from-local-directory-not-sy
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1713/
暫無評論