(本文純屬虛構,如有雷同,實屬巧合)
大名鼎鼎的AspxSpy,罹患間歇性木馬病而被白道追殺。大權在握的系統管理員,冷笑著把這位老兄打入了十八層地獄。拒絕訪問,組件錯誤……連最寶貴的命令執行都要封殺。是可忍孰不可忍!拿上小饅頭,我閉關數月。
國慶悄然而至,滿街地小姐們穿著爆乳裝,齊P小短裙(學名硬得快)擠來擠去,中國首位本土科學家獲得了諾貝爾獎,我靜靜地呆在家中,把這漫長的歷練一一道來,以饗讀者。
非專業術語,僅供娛樂:
Shell:這里指類似于命令提示符的,一個用于運行可執行文件的C#頁面。
本機代碼:與托管代碼對應。
和* 外真是有緣,這不,最近又偶遇* 外的服務器,規模約莫二十上下。向來服務器的安全檢測,也有不少無法獲得最高權限的情形,但這次最令人困惑的,是無論如何也無法在Shell中執行命令。管理員,你在挑逗我么?不想讓我知道其中的奧妙,為何又留下了一個漏洞?給管理員戴好可愛的綠帽后,我搜遍系統,終于發現裝神弄鬼的,是一個小小驅動——*外殺馬。
圖1 *外殺馬
圖2 IDA中的*外殺馬
一個名為NotifyRoutine的函數引起了我的興趣。這是一個回調函數,它的偽代碼如下:
void __stdcall NotifyRoutine(HANDLE ParentId, HANDLE ProcessId, BOOLEAN Create)
{
// …
ProcessHandle = ProcessId;
v66 = ProcessId;
if ( Create && PsLookupProcessByProcessId(ParentId, &Object) >= 0 )
{
v3 = (const char *)PsGetProcessImageFileName(Object);
if ( !stricmp(v3, "w3wp.exe") || (v4 = (const char *)PsGetProcessImageFileName(Object), !stricmp(v4, "php-cgi.exe")) )
{
v5 = 1;
PsLookupProcessByProcessId(ProcessId, &TokenHandle);
// …
圖3 NotifyRoutine
DriverEntry注冊NotifyRoutine的偽代碼如下:
int __stdcall sub_407000(int a1, int a2)
{
// …
DbgPrint("加載*外驅動.\n");
PsSetCreateProcessNotifyRoutine(NotifyRoutine, 0);
// …
return dword_405248(dword_4056BC, a1, a2, &v3, &v7, 0);
}
圖4 PsSetCreateProcessNotifyRoutine
NotifyRoutine的流程很簡單,最重要的分支如下:
(1) 判斷是否創建了進程
(2) 是,則判斷是否由w3wp.exe所創建
(3) 是,則與白名單比較。
(4) 中止所有不在白名單上的進程
(5) 給用戶返回“拒絕訪問”的提示
白名單為硬編碼,主要放行了.Net編譯器的命令,否則源代碼就無法在線編譯執行了。它允許的命令:
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\csc.exe
C:\\WINDOWS\\Microsoft.NET\\Framework\\v4.0.30319\\csc.exe”
……
它還允許System用戶(SID為S-1-5-18)執行C:\WINDOWS\system32\cmd.exe
等命令。
所有命令都帶有完整路徑,如果擁有足夠的權限(如提權成功后),可將存在于白名單且系統中一般不存在的命令替換為你的工具,如:
C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\csc.exe
C:\\WINDOWS\\Microsoft.NET\\Framework\\v3.5\\csc.ex
…
替換后的命令在Shell中可正常執行,僅會在C盤根目錄下的日志中留下一條NotKill記錄。
判斷系統中是否存在*外殺馬,一看C盤根目錄是否存在 7i24_com_FreeHostKill_vxxx.txt的日志文件,二看system32\drivers下是否存在FreeHostKill.sys,三看注冊表。
總之,*外殺馬不僅僅殺馬,它是什么人都殺。它只是一個極其原始的防火墻,通過監控所有進程的創建,在白名單的控制之下,哪怕位高權重的Administrators組用戶也無法在Shell中執行命令。
道高一尺,魔高一丈,既然明白了原理,就來嘗試如何破解吧。
我不知道是否有人在低權限的腳本環境中有辦法躲過驅動的監控,如果有,那真要頂禮膜拜了。
我不是圣人,還是按照通常的思路來完成我的Shell:
(1) 不創建進程
(2) 不創建線程
(3) 直接使用Shell代碼將用戶提升至最高權限
前途是光明的,道路是曲折的。只要有人嘗試了這條路,很快就會出現更多的探索者,期待更多的奇思妙想的出現。
許多漏洞Exploit都采用了vc++編寫,因此最初的做法是弄清漏洞的原理后,使用C#語法逐行重寫這些源代碼。十多天磨練下來,唯一的感受就是——痛苦,九九八十一重的煉獄也不過如此。
重寫內容 | 對人的摧殘度 | 說明 |
API | 中 | DllImport API、結構、常數,PInvoke站點提供查詢 |
C++指針 | 小 | IntPtr |
托管內存與非托管內存的數據交換 | 小 | Marshal類支持 |
內嵌匯編 | 大 | ShellCode,召喚請叫我雷鋒的人 |
好想開個遵義會議,讓人指出一條明路。日夜google中,終于一個家伙說,使用dll封裝你的代碼吧。神啊,如此有創意的點子,為何不是我想出來的呢?
醍醐灌頂之后,本人的血壓暫時飆升到160,刷刷刷地把main函數改名導出,兩分鐘內dll封裝完畢,真是無本生意的典范。看導演的安排,接下的劇情無非就是千里狙敵,褲襠藏雷,開著21世紀的R2概念車去欺負剛擺脫叢林文化的小膏藥旗了。
可惜天亮了,美夢成空,為了讓大伙不再走我的老路,我把那些令人欲仙欲死的問題總結如下:
省略幾千字,我把它放到了本系列篇(三)中加以詳述。
在本機上正常加載的DLL,天殺的虛擬機卻提示無法找到模塊。這是因為操作系統缺少DLL所依賴的運行庫文件。若不想把自己的寶貝工具弄得像超生游擊隊,只有想辦法把庫代碼直接封裝進DLL中。我使用兩種方式來解決:
(1)右鍵打開項目的屬性,C/C++ - 代碼生成 – 運行庫 – 將/MD選項改為/MT(或/MTd)選項。再次編譯,立刻發現生成的dll臃腫了不少,問題解決。
(2)把源代碼放到vc6下編譯。我首先嘗試利用vs2015環境來輔助vc6的編譯。不曾想捅了一個危險的馬蜂窩,彈出了一大串與頭文件有關的警告和錯誤,想來微軟也懶得保持那代價高昂的兼容性了。前后折騰了半小時,乖乖放棄。接著切換到vs2010環境,無須任何的修改,vc6順利完成編譯,問題解決。
在vc6中如何編譯64位程序?山寨某前輩的經驗給懶漢們參考一下:(以vs2010為例)
分為3個步驟:
在開始菜單下點擊“Visual Studio 2010 – Visual Studio Tools – Visual Studio x64 兼容工具命令提示(2010)”,然后在此命令提示符中運行VC6:
D:\vc\vc6common\MSDev98\Bin\MSDEV.EXE /useenv
圖5 x64 兼容工具命令提示
復制Release或Debug的配置,起名xx64,激活該配置。(1、Build – Configurations – Add…
;2、Build – Set Active Configuration…
)
圖6 新建Win32 Debug64配置
1、在“Project Settings”對話框中, 點擊“General”標簽. 在“Output directories”, 在“Intermediate files” 和“Output files”輸入框中, 鍵入“Debug64”(無引號)
圖7 設置輸出路徑
2、在“C/C++”標簽上, “Debug info”下拉列表中, 選擇“Program database”(參數選項對應是 /Zi)
圖8 設置調試信息
3、在“Link”標簽上, “Project options”的輸入框中, 將“/machine:I386”改為“/machine:AMD64”(命令中將出現兩個不同的/machine)
圖9 設置編譯指令
編譯后成功得到大腹便便的64位dll。
我的破壞力還蠻大的。把本機調試通過的程序放到服務器上運行,瞬間弄崩了網站,重啟IIS后再執行幾次,服務器藍屏死機。
代碼基本未變,我只是把它封裝在DLL中,由w3wp.exe加載運行,怎么就崩潰了呢。長夜漫漫,無心睡眠,在迷糊中把IIS 7應用程序池從集成模式調整為經典模式后,異常莫明地消失了。比爾,你想玩死人么 ?集成與經典模式到底是怎么一回事?
圖10 集成模式處理管道
八股文常常引用上圖說明IIS7的集成模式。IIS 7集成了ASP.NET運行庫,使用統一的管道來處理請求。經典模式兼容了IIS 6,如下圖所示,處理ASP.NET動態請求的只是IIS的一個插件——ISAPI模塊。同理,PHP插件只處理PHP請求,它們各司其職,互不干涉。兩種模式的差別涇渭分明。
圖11 經典模式處理流程
還有一些值得閱讀的入門文檔,如《ASP.NET Application Life Cycle Overview for IIS 7.0》,概述了ASP.NET應用程序在生命周期里發生了什么事情。《IIS Modules Overview》主要說明模式及其配置方式。從IIS 7的模塊界面可以看到,集成模式使用托管代碼模塊來處理.aspx頁面的請求,而經典模式使用本機代碼模塊。
圖12 請求處理模塊
書讀了不少,按流行俚語,“然而這并沒有什么ruan用”,還缺少某種催化劑,看來我只能再次求助于Windbg了。
使用kp命令查看異常發生時的調用棧,前后幾十個調用,秘密就隱藏在它們中間。仔細觀察兩種模式,為了運行我的代碼,最后都從托管環境轉入了非托管環境,非托管環境下的調用完全相同。完蛋了,究竟要從哪里下手哇。咬咬牙,開始跟吧,十多天下來,人比黃花瘦。
今天是個好日子,我升級了。我察覺自己又犯了老毛病,沒有認真檢查關鍵語句的運行結果。
if (x)
{
// y
}
經典模式下x為真,這符合預期,然而在操蛋的集成模式下為假,稍不留神就忽略了。再追溯x的來源,終于發現集成模式似乎由于緩存的影響,對本機DLL的變化不能做出實時的更新。
解決起來簡單而粗暴,加個else直接返回錯誤提示。我并不介意多運行三兩次,誰讓它們不是我的機子呢,等集成模式睡醒了,更新了它的緩存,權限也到手了。
經過第三階段的苦戰,我的代碼終于可以穩定地運行了,但又出現了一個奇怪的現象。
在提升權限之前,查詢應用程序池的用戶:
圖13 提權前用戶
此時,75804號進程,即w3wp.exe擁有IIS_IUSRS用戶權限。緊接著提升權限,頁面顯示了執行結果:
圖14 Shell代碼提權
請忽略若干無用的調試信息,我們需要關注的是最后三行。w3wp.exe的進程號仍為75804(也可能會創建新的w3wp.exe),用戶權限已提升為SYSTEM。
讓我們再次查看此時應用程序池的用戶:
圖15 提權成功后用戶未變
怪哉?應用程序池的用戶為何沒有改變呢?
晚飯時間到了,補充足夠的能量后,我已經想到了一種可能。IIS啟用了模擬功能后,即使w3wp.exe以SYSTEM權限運行,應用程序池用戶仍保持不變。
為了驗證這種可能性,讓我們亮劍,它就是用來中斷模擬的API——RevertToSelf。美女現身,Beautiful!
圖16 強制中斷IIS模擬恢復用戶身份
西元201x年,001號大殺器研制成功,為紀念cctv,我把它命名為cv51。
圖17 未能進入內核無權讀寫致出錯
首次執行出錯,不必理會,再次嘗試執行。
圖18 提權成功
cv51的兄弟cvbb此時運行在SYSTEM權限下,順利添加用戶tingting。
圖19 其他工具共享同一個w3wp.exe的權限
登錄服務器。任務管理器中,w3wp.exe(PID:22324)在提升權限后的身份可能顯示為SYSTEM,也可能保持不變。
圖20 模擬使任務管理器不能真實顯示Shell的權限
查看管理員的密碼,統計是否有使用強密碼的習慣,獲得的密碼僅用于學習與研究,將在24小時內刪除。上傳mimikatz,在命令提示符下執行如下命令,所有登錄用戶盡在日志中:(mimikatz是開源工具,請到官方站點下載,不吃毒蘋果)
mimikatz log privilege::debug sekurlsa::logonpasswords
圖21 mimikatz破解曾經登錄的用戶信息
以上測試在Windows 2003、2008及R2版本中通過。