緩存溢出(Buffer overflow),是指在存在緩存溢出安全漏洞的計算機中,攻擊者可以用超出常規長度的字符數來填滿一個域,通常是內存區地址。在某些情況下,這些過量的字符能夠作為“可執行”代碼來運行。從而使得攻擊者可以不受安全措施的約束來控制被攻擊的計算機。
下面讓我們了解一下緩存溢出的原理。眾說周知,c語言不進行數組的邊界檢查,在許多運用c語言實現的應用程序中,都假定緩沖區的大小是足夠的,其容量肯定大於要拷貝的字符串的長度。然而事實並不總是這樣,當程序出錯或者惡意的用戶故意送入一過長的字符串時,便有許多意想不到的事情發生,超過的那部分字符將會覆蓋與數組相鄰的其他變量的空間,使變量出現不可預料的值。如果碰巧,數組與子程序的返回地址鄰近時,便有可能由於超出的一部分字符串覆蓋了子程序的返回地址,而使得子程序執行完畢返回時轉向了另一個無法預料的地址,使程序的執行流程發生了錯誤。甚至,由於應用程序訪問了不在進程地址空間范圍的地址,而使進程發生違例的故障。這種錯誤其實是編程中常犯的。
http://hi.baidu.com/caterqiu/item/29598d475bcbf8af61d7b922 參考這哥們匯編調試。
組成部分
|
使用Buffer Overflow 方法來入侵目的主機是黑客們經常采用的一種手段,本文將幾篇介紹其機理的文章作了一些加工整理, 對它的機理作出了由淺入深的剖析.
本文分為下面幾個部分, 朋友們可以按照自己的興趣選擇不同的章節:
1.關於堆棧的基礎知識
2.Buffer Overflow 的原理
3.Shell Code 的編寫
4.實際運用中遇到的問題
5.附錄 I 若干操作系統/平台上的 Shell Code
6.附錄 II 通用 Buffer Overflow 攻擊程序
--------------------------------------------------------------------------------
1. 關於堆棧的基礎知識
一個應用程序在運行時,它在內存中的映像可以分為三個部分: 代碼段, 數據段和堆棧段(參見下圖). 代碼段對應與運行文件中的 Text Section ,其中包括運行代碼和只讀數據,這個段在內存中一般被標記為只讀,任何企圖修改這個段中數據的指令將引發一個 Segmentation Violation 錯誤.
數據段對應與運行文件中的 Data Section 和 BSS Section ,其中存放的是各種數據(經過初始化的和未經初始化的)和靜態變量.
下面我們將詳細介紹一下堆棧段. |--------| 虛存低端 | | | 代碼段 | | | |--------| | | | 數據段 | | | |--------| | | | 堆棧段 | | | |--------| 虛存高端
堆棧是什么?
如果你學過<<數據結構>>這門課的話, 就會知道堆棧是一種計算機中經常用到的抽象數據類型. 作用於堆棧上的操作主要有兩個: Push 和 Pop , 既壓入和彈出. 堆棧的特點是LIFO(Last in , First out), 既最后壓入堆棧的對象最先被彈出堆棧.
堆棧段的作用是什么?
現在大部分程序員都是在用高級語言進行模塊化編程, 在這些應用程序中,不可避免地會出現各種函數調用, 比如調用C 運行庫,Win32 API 等等. 這些調用大部分都被編譯器編譯為Call語句. 當CPU 在執行這條指令時, 除了將IP變為調用函數的入口點以外, 還要將調用后的返回地址放入堆棧. 這些函數調用往往還帶有不同數量的入口參數和局部變量, 在這種情況下,編譯器往往會生成一些指令將這些數據也存入堆棧(有些也可通過寄存器傳遞).
我們將一個函數調用在堆棧中存放的這些數據和返回地址稱為一個棧幀(Stack Frame).
棧幀的結構:
下面我們通過一個簡單的例子來分析一下棧幀的結構. void proc(int i) { int local; local=i; } void main() { proc(1); }
這段代碼經過編譯器后編譯為:(以PC為例)
|
2. Buffer Overflow 的機理
我們先舉一個例子說明一下什么是 Buffer Overflow : void function(char *str) { char buffer[16]; strcpy(buffer,str); }
void main() { char large_string[256]; int i;
for( i = 0; i < 255; i++) large_string[i] = 'A';
function(large_string); }
這段程序中就存在 Buffer Overflow 的問題. 我們可以看到, 傳遞給function的字符串長度要比buffer大很多,而function沒有經過任何長度校驗直接用strcpy將長字符串拷入buffer. 如果你執行這個程序的話,系統會報告一個 Segmentation Violation 錯誤.下面我們就來分析一下為什么會這樣?
首先我們看一下未執行strcpy時堆棧中的情況: 16 4 4 4 ...[buffer] [ebp] [ret地址] [large_string地址] esp ebp
當執行strcpy時, 程序將256 Bytes拷入buffer中,但是buffer只能容納16 Bytes,那么這時會發生什么情況呢? 因為C語言並不進行邊界檢查, 所以結果是buffer后面的250字節的內容也被覆蓋掉了,這其中自然也包括ebp, ret地址 ,large_string地址.因為此時ret地址變成了0x41414141h ,所以當過程結束返回時,它將返回到0x41414141h地址處繼續執行,但由於這個地址並不在程序實際使用的虛存空間范圍內,所以系統會報Segmentation Violation.
從上面的例子中不難看出,我們可以通過Buffer Overflow來改變在堆棧中存放的過程返回地址,從而改變整個程序的流程,使它轉向任何我們想要它去的地方.這就為黑客們提供了可乘之機, 最常見的方法是: 在長字符串中嵌入一段代碼,並將過程的返回地址覆蓋為這段代碼的地址, 這樣當過程返回時,程序就轉而開始執行這段我們自編的代碼了. 一般來說,這段代碼都是執行個Shell程序(如\bin\sh),因為這樣的話,當我們入侵一個帶有Buffer Overflow缺陷且具有suid-root屬性的程序時,我們會獲得一個具有root權限的shell,在這個shell中我們可以干任何事. 因此, 這段代碼一般被稱為Shell Code.
下面我們就來看一下如何編寫Shell Code.(待續)
|
3. Shell Code 的編寫 下面是一個創建Shell的C程序shellcode.c: (本文以IntelX86上的Linux為例說明) void main() { char *name[2];
name[0] = "/bin/sh"; name[1] = NULL; execve(name[0], name, NULL); }
我們先將它編譯為執行代碼,然后再用gdb來分析一下.(注意編譯時要用-static選項,否則execve的代碼將不會放入執行代碼,而是作為動態鏈接在運行時才鏈入.) ------------------------------------------------------------------------------ [aleph1]$ gcc -o shellcode -ggdb -static shellcode.c [aleph1]$ gdb shellcode GDB is free software and you are welcome to distribute copies of it under certain conditions; type "show copying" to see the conditions. There is absolutely no warranty for GDB; type "show warranty" for details. GDB 4.15 (i586-unknown-linux), Copyright 1995 Free Software Foundation, Inc... (gdb) disassemble main Dump of assembler code for function main: 0x8000130 <main>: pushl %ebp 0x8000131 <main+1>: movl %esp,%ebp 0x8000133 <main+3>: subl $0x8,%esp 0x8000136 <main+6>: movl $0x80027b8,0xfffffff8(%ebp) 0x800013d <main+13>: movl $0x0,0xfffffffc(%ebp) 0x8000144 <main+20>: pushl $0x0 0x8000146 <main+22>: leal 0xfffffff8(%ebp),%eax 0x8000149 <main+25>: pushl %eax 0x800014a <main+26>: movl 0xfffffff8(%ebp),%eax 0x800014d <main+29>: pushl %eax
中的某一位置,但沒有足夠空間時會發生緩沖區溢出。
下面對這種技術做一個詳細的介紹。
緩沖區是程序運行時計算機內存中的一個連續的塊,它保存了給定類
型的數據。問題隨着動態分配變量而出現。為了不用太多的內存,一個有
動態分配變量的程序在程序運行時才決定給他們分配多少內存。
如果程序在動態分配緩沖區放入太多的數據會有什么現象?它溢出了,
漏到了別的地方。一個緩沖區溢出應用程序使用這個溢出的數據將匯編語
言代碼放到計算機的內存中,通常是產生root權限的地方。
單單的緩沖區溢出,並不會產生安全問題。只有將溢出送到能夠以root
權限運行命令的區域才行。這樣,一個緩沖區利用程序將能運行的指令放
在了有root權限的內存中,從而一旦運行這些指令,就是以root權限控制
了計算機。
總結一下上面的描述。緩沖區溢出指的是一種系統攻擊的手段,通過
往程序的緩沖區寫超出其長度的內容,造成緩沖區的溢出,從而破壞程序
的堆棧,使程序轉而執行其它指令,以達到攻擊的目的。據統計,通過緩
沖區溢出進行的攻擊占所有系統攻擊總數的80%以上。
造成緩沖區溢出的原因是程序中沒有仔細檢查用戶輸入的參數。例如
下面程序:
example0.c
-----------------------------------------------------------
void function(char *str) {
char buffer[16];
strcpy(buffer,str);
}
-----------------------------------------------------------
上面的strcpy()將直接把str中的內容copy到buffer中。這樣只要str
的長度大於16,就會造成buffer的溢出,使程序運行出錯。存在象strcpy
這樣的問題的標准函數還有strcat(),sprintf(),vsprintf(),gets(),scanf(),
以及在循環內的getc(),fgetc(),getchar()等。
在C語言中,靜態變量是分配在數據段中的,動態變量是分配在堆棧
段的。緩沖區溢出是利用堆棧段的溢出的。