最近在分析惡意代碼的過程中,遇到了基于管道的后門,于是就學習了一下基于管道的shell后門原理,自己動手寫了一個簡單的shell后門。分享一下,供大家交流,如有錯誤之處,歡迎指出。聲明:本內(nèi)容僅供用于分析惡意代碼時參考相關原理,請自覺遵守相關法律,嚴禁使用相關技術(shù)進行任何非法目的。否則,自行承擔后果。
原理
本次實現(xiàn)的是一個正向的shell,被控者作為服務器,在本地監(jiān)聽一個端口,hacker作為客戶端,通過網(wǎng)絡來連接。整個原理如下圖所示:
hacker通過網(wǎng)絡來發(fā)送和接收數(shù)據(jù),箭頭在這里表示數(shù)據(jù)流向,首先數(shù)據(jù)從hacker這里通過網(wǎng)絡套接字,傳入被控者的buffer區(qū),然后buffer區(qū)通過一個管道寫入,CMD程序從該管道的另一端讀取,并作為CMD程序的輸入。
CMD程序執(zhí)行完結(jié)果,將輸出寫入另一個管道,buffer區(qū)再從該管道的另一端讀取輸出,然后通過網(wǎng)絡套接字發(fā)送到hacker。
其中,CMD程序通過CreateProcess這個函數(shù)API來調(diào)用,在設置的時候,可以將程序的輸入輸出自行指定。
相關API
socket相關
關于socket相關的API,相信大家都很熟悉了,這里就簡單介紹一下創(chuàng)建TCP服務端程序的函數(shù)調(diào)用流程如下:
WSAStartup()->socket()->bind()->listen()->accept()->send()/recv()->closesocket()->WSACleanup()。
首先使用WSAStartup()來初始化Winsock庫,使用完畢后要調(diào)用WSACleanup()來釋放Winsock庫。然后使用socket()創(chuàng)建套接字,使用完畢后要調(diào)用closesocket()關閉套接字。對于WSAStartup()/WSACleanup()和socket()/closesocket()這樣的函數(shù),最好在寫完一個函數(shù)后,就寫出另外一個函數(shù),避免遺忘。創(chuàng)建完套接字后,就可以使用bind()、listen()、accept()、send()和recv()。其中為bind()函數(shù)指定地址和端口時,還涉及到sockaddr_in結(jié)構(gòu)體,以及將主機字節(jié)序轉(zhuǎn)為網(wǎng)絡字節(jié)序的htons函數(shù)等。這些都是固定的流程,就不過多贅述了。
管道相關操作
管道是一種進程之間通信的技術(shù),可以分為命名管道和匿名管道,匿名管道只能實現(xiàn)本地機器上兩個進程間的通信,常用來在一個父進程和子進程之間傳遞數(shù)據(jù)。我們這里使用匿名管道即可,因為匿名管道比命名管道相對簡單。
首先需要CreatePipe()創(chuàng)建管道,該函數(shù)的定義如下:
hReadPipe指向一個用來接收管道的讀取句柄的變量;
hWritePipe指向一個用來接收管道寫入句柄的變量;
lpPipeAttributes指向一個SECURITY_ATTRIBUTES結(jié)構(gòu)的指針,它決定了返回的句柄是否可以由子進程繼承。如果lpPipeAttributes為NULL,則該句柄不能繼承。這里我們要將其設置為可繼承。SECURITY_ATTRIBUTES結(jié)構(gòu)體比較簡單可以自行查閱MSDN設置。
nSize指定管道的緩沖區(qū)大小,以字節(jié)為單位。大小只是一個建議;系統(tǒng)使用值來計算一個適當?shù)木彌_機制。如果此參數(shù)為零,則系統(tǒng)使用默認緩沖區(qū)大小。這里我們賦值為0即可。
向管道讀取或者寫入數(shù)據(jù),直接調(diào)用ReadFile和WriteFile即可。在讀取數(shù)據(jù)前,可以先調(diào)用PeekNamePipe()查看管道中是否有數(shù)據(jù),其定義如下:
hNamedPipe這個參數(shù)可以是一個命名管道實例的句柄,也可以是可以是一個匿名管道的讀取端的句柄。其他參數(shù)詳情可以查閱MSDN。
新建進程
相信大家對CreateProcess都不陌生,這里簡單回顧一下,函數(shù)定義如下:
在這里需要重點關注的是,設置lpStartupInfo結(jié)構(gòu)體中的內(nèi)容。該結(jié)構(gòu)體如下:
重點是需要將hStdInput、hStdOutput、hStdError進行設置。設置為對應管道的讀寫句柄。
在本例中,hStdInput為管道1的讀句柄,hStdOutput、hStdError都設置為管道2的寫句柄。
編碼實現(xiàn)
創(chuàng)建套接字:
這里監(jiān)聽的端口時888,任意IP地址都可連接。
創(chuàng)建管道:
創(chuàng)建子進程CMD:
設置死循環(huán)不斷的通過ReadFile()讀取管道中的內(nèi)容,即CMD程序的執(zhí)行結(jié)果,通過send()發(fā)送給hacker。然后不斷的通過recv()接收hacker發(fā)來的指令,通過WriteFile()寫入管道傳遞給CMD程序。
測試效果
hacker與buffer之間,不要直接用telnet,只能一次傳送一個字符。要通過netcat.exe發(fā)送數(shù)據(jù):
先在被控端主機上運行,888端口已經(jīng)監(jiān)聽:
在另外一臺主機上使用nc連接:
連接成功后輸入dir,發(fā)現(xiàn)目錄已經(jīng)發(fā)現(xiàn)改變,從D:\hacker變成了D:\受害者,列出的文件也是受害者主機上的,說明我們已經(jīng)能夠成功在受害者的CMD程序執(zhí)行命令了。
總結(jié)
總的來說,這次實驗用到了管道和socket等技術(shù),重點在于處理好整個邏輯過程,這些Windows的API相對都不難。