WebServer端口重定向后門的研究
前段時間有朋友問到了我一個關于“無端口可用”的問題。說在如下圖所示的內網環(huán)境中,firewall只允許Web Server的80端口建立網絡連接,并且Web Server上的80端口已經被IIS、Apache等軟件占用了的情況下,怎么建立一個RAT后門。
0x01
早些時候的著名后門byshell就考慮到了這個問題,于是使用了一種非常挫的方式去解決??蛻舳藢祿l(fā)送到80端口,服務端將IIS的進程打開,循環(huán)遍歷IIS進程的整個內存去尋找數據標記。顯然這種方法從效率和穩(wěn)定性上來講,都是不可取的。這里姑且不把這種方法當做方法。
過去有人提出過一種端口復用的方法去建立的后門,這個方法使用了setsockopt()這個API,這個API在MSDN里說是用來設置套接字的選項的。原型如下。
int setsockopt(
SOCKET s,
int level,
int optname,
const char FAR* optval,
int optlen
);
我們這里只關心它的第三個參數,這個參數用來設置套接字狀態(tài)。這個參數有一個取值為SO_REUSEADDR,MSDN對這個參數的解釋如下。
The state of the SO_REUSEADDR socket option determines whether the local transport address to which a socket will be bound is always shared with other sockets. This socket option applies only to listening sockets, datagram sockets, and connection-oriented sockets.
也就是說,當第三個參數的取值設置為SO_REUSEADDR時,套接字的端口是可以共享復用的。具體共享細節(jié)為后來居上,后建立該參數鏈接的套接字先拿到數據。此方法目前對Apache和IIS5.0及以下版本有效。那為什么IIS6.0及以上就不行了呢?之后會做解釋。
0x02
通過逆向和查閱開源代碼可以得知,Apache和IIS5.0及以下版本使用了應用層的IOCP模型進行通信,盡管框架比較復雜,但是依然在應用層創(chuàng)建了套接字。到這里你是否有新的想法呢?沒錯,我想到了可以使用遠程線程注入一個DLL進行Api Hook例如WSARecv()、WSASend()這樣的API獲取套接字和異步IO的緩沖區(qū)指針,再使用getpeername()函數對比客戶端信息,緊接著用套接字進行IO。
或者也可以使用更簡單粗暴的方法,直接使用SPI安裝一個LSP,也可以抓到數據,但是就比較難再做通信了。我們將這種在應用層建立套接字通信轉接通信過程的方法總結為下圖,紅線表示可以利用的地方。
這里可能就有同學會問到,為什么不能直接上Rootkit呢?如果從ring0層去考慮這個問題理論上是非常容易的。我們可以使用TDI或者NDIS的過濾驅動直接過濾所有IO網卡的流量,不過寫個這么重量級的后門,的確有“殺雞焉用宰牛刀”的意味。再者,也可以使用SSDT HOOK和對TCP驅動下IRP HOOK去解決問題。但是為什么不去做呢?因為使用驅動目前已經不是Windows木馬編程潮流所在。
0x03
還記得之前提到的IIS6.0和以上版本的問題嗎?這里一個新問題出現了,從IIS6.0開始,微軟可能考慮到了安全性和穩(wěn)定性以及數據處理的效率的問題,將網絡通信的過程封裝在了ring0層,使用了http.sys這個驅動來直接進行網絡通信。如下圖所示。
這樣一來,應用層就沒有了套接字,我們就不能使用上述方法去解決這個問題了。那怎么辦呢?難道應用層因此就再沒有可以利用的地方了嗎?這么想并不符合Geek的風格。于是,經過對W3wp.exe的初步的逆向,又發(fā)現了一些可以利用的地方。
這個過程中,ring0無法處理的HTTP請求,都下發(fā)由w3wp.exe進程處理。針對HTTP請求考慮,我想到了如下幾點可以轉接網絡通信過程的地方。
1.GET或者POST對硬盤中的文件做訪問,有可能需要通過應用層CreateFile()、ReadFile()、WriteFile()去完成。(API HOOK?)
2.對于HTTP這種大部分內容由ANSI字符串解析的協(xié)議,難道用不到標準字符串處理函數嗎?(還是API HOOK?)
3.對ASP、PHP、JSP腳本的解釋,可能需要將數據提交給解釋引擎去完成。(inline hook?)
0x04
基于以上幾點,我也一一做了驗證。這里逆向要注意了,OllyDbg對低權限進程的Attach調試穩(wěn)定性并不好,經常崩掉。所以我換了非常難看的Windbg。微軟自家的調試器調自家的程序非常穩(wěn)定。首先bp了CreateFileW(),得到了一個比較驚喜的效果。如圖所示。
Windbg攔下了CreateFileW(),并且在第一個參數里,我們看到了我們提交的“fuckyou1234”,這里我們就完全可以用if(wcsncmp(s1,L”fuckyou1234”,11))這樣取得后門命令了。
而我通過對多個字符串處理函數的bp,同樣也發(fā)現了可以利用的地方。例如wcsstr()函數的斷點,成功的抓到了cookies和一些其他與HTTP協(xié)議相關的信息。cookies可以比URL提交跟多內容,并且可以繞過一些log和過濾。如下圖所示。
這是對前兩個問題逆向的初步成果,至于第三個問題,暫時先留著好了?,F在遇到了一個新的問題,當拿到需要執(zhí)行的命令,我們至少不能讓他去w3wp.exe這個低權限的進程去實施。那么怎么讓他在高權限的進程去實施呢?還有,我們執(zhí)行完的結果,該怎么返回給客戶端呢?
0x05
這里我考慮到了留一個宿主進程,建立事件對象和郵槽或者管道接受由w3wp.exe發(fā)來的命令。至于把數據反饋到客戶端,我們可以重定向CreateFileW()的第一個參數,將他指向管道也好,指向某個硬盤里的輸出文件也好,當緊接著去ReadFile()的時候,就可以順利將我們的執(zhí)行結果反饋到客戶端去了。
這里涉及到高低權限進程通信的一個問題,高權限進程創(chuàng)建的內核對象必須設置安全屬性為低權限可繼承句柄,并且設置安全描述符和DACL。事件、郵槽、管道的等內核對象才能被低權限進程打開。具體代碼如下。最后,我用上述所訴內容,寫了如下的一個后門。
HANDLE secCreateEventPort(WCHAR* szNameEvent)
{
SECURITY_DESCRIPTOR SecDescriptor = {0};
SECURITY_ATTRIBUTES SecurityAttributes = {0};
if (InitializeSecurityDescriptor(&SecDescriptor,SECURITY_DESCRIPTOR_REVISION) == FALSE)
return INVALID_HANDLE_VALUE;
if(SetSecurityDescriptorDacl(&SecDescriptor,TRUE, NULL, FALSE) == 0)
return INVALID_HANDLE_VALUE;
SecurityAttributes.bInheritHandle = TRUE;
SecurityAttributes.lpSecurityDescriptor = &SecDescriptor;
SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
return CreateEvent(&SecurityAttributes,TRUE,FALSE,szNameEvent);
}