[email protected]JavaScript BackDoor中提到了利用rundll32.exe執行一段JavaScript代碼即可反彈一個Http Shell,這里將之前看到的對其原理進行分析的文章翻譯和大家分享。
links: http://thisissecurity.net/2014/08/20/poweliks-command-line-confusion/
最近,hFireF0X在逆向工程論壇kernelmode.info上發表了對Win32/Poweeliks惡意軟件詳細調查的文章。這個惡意軟件的特別之處就是它存在于window注冊表中并且使用rundll32.exe來執行JavaScript代碼。
我發現很有趣的是我顯然不是唯一一個發現可以通過rundll32來執行一些JavaScript代碼的人。
當我們第一次發現命令行執行JavaScript,我們很好奇它是如何工作的。
在接下來的文章中,我們會分析JavaScript代碼時如何執以及為什么會在調用這樣簡單的命令行代碼時執行:
#!bash
rundll32.exe javascript:"\..\mshtml,RunHTMLApplication ";alert(‘foo’);
關于rundll32.dll的使用在MSDN上有專門的文檔;它往往被用來調用一個DLL文件的輸出函數,可以通過以下的命令來實現:
#!bash
RUNDLL32.EXE <dllname>,<entrypoint> <optional arguments>
entrypoint就是輸出函數;它的函數原型應該是如下所示:
#!cpp
void CALLBACK EntryPoint(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow);
參數lpszCmdLine是由rundll32.exe命令語句中的<optional arguments>
值確定的。
我們會嘗試指出rundll32.exe是如何能夠調用被mshtml.dll輸出的函數RunHTMLApplication,以及“Javascript”前綴是如何被用來執行實際的JavaScript代碼的。
a.參數
rundll32.exe做的第一件事就是使用內置的ParseCommand命令來對命令進行解析。這個函數會查找一個逗號(‘,’,0x2c)來定位dll的名稱和一個空格(‘ ’,0x20)來定位入口點名稱
當使用我們的樣本命令時,ParseCommand會返回javascript:"\..\mshtml
作為DLL名稱,返回RunHTMLApplication
作為入口點。
rundll32.exe會嘗試幾種方法來從最初的“JavaScript:"\..\mshtml
”加載實際的DLL。
第一次測試使用了函數GetFileAttributes("javascript:"\..\mshtml")
。這個函數最終會接近C:\Windows\system32\mshtml
。如果這個文件沒有被發現,那么這個函數就會返回-1。
接下來SearchPath
會被調用來確定DLL的名稱。這個函數會讀取注冊表鍵值HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\SafeProcessSearchMode
。微軟對于這個鍵值的定義是:
當這個REG_DWORD類型的注冊表鍵值被設為1,SearchPath會首先搜索系統路徑中指定的文件夾,然后會搜索當前工作的文件夾。當這個注冊表鍵值被設為0時,電腦首先會搜索當前工作的文件夾,然后再去搜索系統路徑中指定的文件夾。系統對于這個注冊表鍵的默認值是0。
在默認情況下,這個注冊表鍵值是不存在的(在win xp、7、8下),所以SearchPath
會優先嘗試在rundll32.dll的當前目錄中加載文件mshtml,其次才會嘗試在系統路徑中進行定位。
如果所有的嘗試都失敗了,那么rundll32.exe就會進行下一步。GetFileAttribute
將再次被調用來給模塊查找manifest文件:javascript:"\..\mshtml.manifest
。
若所有的之前的步驟都失敗了,rundll32.exe最終會調用Loadlibrary("javascript:"\..\mshtml")
。
LoadLibrary
只是位于ntdll.dll中的一個對LdrLoadDLL的簡單封裝。在內部,LdrLoadDll會添加默認的擴展dll并且把結果字串(javascript:”\..\mshtml.dll
)作為路徑來解析。“..
”代表再向上一層文件夾:它就解析成了mshtml.dll
。
當mshtml.dll
已經被確定了,LdrLoadDll就可以加載系統目錄中的lib庫了。
rundll32.exe接下來會使用之前提取的入口點名稱RunHTMLApplication
調用GetProcAddress
。
這個時候我們可以發現,javascript:
前綴看起來毫無用處:LoadLibrary("foobar:\"\..\mshtml")
工作的很好。所以,為什么要加這么一個前綴呢?
一旦入口地址已經被解析出來,rundll32.dll會調用函數mshtml.dll!RunHTMLApplication
。
即使沒有被記錄,實際的RunHTMLApplication也能從c:\windows\system32\mshta.exe
的調用中推斷出來(這個應用專門來啟動一個.hta文件)。
#!cpp
HRESULT RunHTMLApplication(
HINSTANCE hinst,
HINSTANCE hPrevInst,
LPSTR szCmdLine,
int nCmdShow
);
這和期望的rundll32.exe的入口點地址很相似:
#!cpp
void CALLBACK EntryPoint(
HWND hwnd,
HINSTANCE hinst,
LPSTR lpszCmdLine,
int nCmdShow
);
RunHTMLApplication接受一個窗口的句柄而不是一個模塊的句柄作為第一個參數。這個參數會在mshml登記一個窗口類或者為一個類創建窗口時被使用。傳遞一個同實際的實例并不相一致的值并不會給user32帶來很大影響。
第二個參數完全沒有被使用,所以這個不匹配并不重要。
最后一個參數,nCmdShow
,被RunHTMLApplication
函數用來展示代管這個HTML應用的主機窗口。rundll32經常使用值SW_SHOWDEFAULT
來調用入口點函數來指示任何可能打開的窗口使用窗口默認配置。
在我們的例子中,可能更讓人感興趣的參數是 lpszCmdLine (";alert('foo'))
。
這顯然會導致問題,因為這并不是一個有效的JavaScript語句(注意在語句末尾丟掉的雙引號)。但是它仍然有效,因為RunHTMLApplication
忽略給定的參數,并且更傾向于從windows API GetCommandLine
中重新請求原始的命令(封裝在GetCmdKLine
函數的一個調用中)。
完整的命令包含了可執行文件和參數的名稱:GetCmdLine
通過整理可執行規范來提取參數。
在這之后,RunHTMLApplication
會調用CreateURLMonitor
:
這也是需要字符串“javascript”的地方。
CreateURLMonitor
解析命令行來提取char":"(0x3A)之前的字符串:“javascript”。
CreateUrlMoniker
會爬取注冊表鍵值HKCR\SOFTWARE\Classes\PROTOCOLS\Handler\
。這些鍵值和一個協議集以及它們的CLSID有關。
CreateUrlMoniker
會發現一個合適的協議處理器來對Javascript的協議進行處理(HKCR\SOFTWARE\Classes\PROTOCOLS\Handler\javascript
):
CLSID{3050F3B2-98B5-11CF-BB82-00AA00BDCE0B}
符合微軟“Microsoft HTML Javascript Pluggable Protocol”規范。
這也是為什么字符串“javascript”必須在參數的開始部分的原因。
相同的機制在人們在IE的導航欄輸入javascript:alert('alert')時也會起作用。
位于“:”分隔符之后的字符串會被JavaScript URL moniker解釋成JavaScript指令:
#!js
"\..\mshtml,RunHTMLApplication ";alert(‘foo’);
這是一個帶有字符串"\..\mshtml,RunHTMLApplication "
和函數(alert)
的無效JavaScript語句(因為雙引號會跳過所有之前的步驟!)。
最終RunHTMLApplication會調用CHTMLApp::Run
,這條JavaScript語句也會被執行:
從安全的角度來看,通過rundll32來執行JavaScript就像執行一個HTML應用。
換句話說,我們可以使用IE的全部權利。當區域安全被關閉,允許跨域腳本訪問,我們就可以擁有讀寫客戶端機器文件和注冊表的權力。
通過這個技巧,JavaScript可以在IE外部被執行,并且腳本沒有任何安全概念的約束,比如保護模式\沙盒。
按照我們的理解,這個技術語序繞過一些信任內置的rundll32的行為的安全產品。