堆棧溢出對于我們軟件開發人員來說,最嚴重的后果是破壞了內存中的指針,及其造成的一系列難查的bug。
在當前網絡與分布式系統安全中,被廣泛利用的50%以上都是緩沖區溢出,其中最著名的例子是1988年利用fingerd漏洞的蠕蟲。而緩沖區溢出中,最為危險的是堆棧溢出,因為入侵者可以利用堆棧溢出,在函數返回時改變返回程序的地址,讓其跳轉到任意地址,帶來的危害一種是程序崩潰導致拒絕服務,另外一種就是跳轉并且執行一段惡意代碼,比如得到shell,然后為所欲為。我在這里演示一下堆棧溢出的原理。
堆棧有關概念
首先,介紹一下,與堆棧有關的一些概念:動態內存有兩種,堆棧(stack),堆(heap)。堆棧在內存上端,堆在內存下端,當程序執行時,堆棧向下朝堆增長,堆向上朝堆棧增長。通常,局部變量,返回地址,函數的參數,是放在堆棧里面的。
低地址
局部變量
舊的基指針
返回地址
函數的參數(左)
函數的參數(。。。)
函數的參數(右)
高地址
我們可以寫一個小程序測試:
Copycode
#include“string.h”
voidtest(char*a);
intmain(intargc,char*argv[])
{
chara[]=“hello”;
test(a);
return0;
}
voidtest(char*a)
{
char*j;
charbuf[5];
strcpy(buf,a);
printf(“&main=%p\n”,&main);
printf(“&buf=%p\n”,&buf);
printf(“&a=%p\n”,&a);
printf(“&test=%p\n”,&test);
for(j=buf-8;j《((char*)&a)+8;j++)
printf(“%p:0x%x\n”,j,*(unsignedchar*)j);
}
Main定義一個字符串hello,然后調用test函數,在test函數中,有一個長度為5的局部字符串變量buf,然后把復制參數a復制到buf中,這里因為沒有a的長度小于等于buf的長度,所以并沒有溢出buf。然后顯示各個函數,參數,局部變量的地址,以及局部字符串變量buf和參數a之間的地址,我們看到:
Quote:
&main=0040100A
&buf=0012FF14
&a=0012FF28
&test=00401005
0012FF0C:0xcc
0012FF0D:0xcc
0012FF0E:0xcc
0012FF0F:0xcc
0012FF10:0xcc
0012FF11:0xcc
0012FF12:0xcc
0012FF13:0xcc
0012FF14:0x68 h 這里就是buf了!
0012FF15:0x65 e
0012FF16:0x6c l
0012FF17:0x6c l
0012FF18:0x6f o
0012FF19:0x0 \0
0012FF1A:0xcc
0012FF1B:0xcc
0012FF1C:0x1c 這里是
0012FF1D:0xff 兩個
0012FF1E:0x12 舊的
0012FF1F:0x0 基指針,不管他
0012FF20:0x80
0012FF21:0xff
0012FF22:0x12
0012FF23:0x0
0012FF24:0x34 這個就是
0012FF25:0xb8 返回地址了
0012FF26:0x40 和main的地址很
0012FF27:0x0 接近吧!
0012FF28:0x78 這個是
0012FF29:0xff 參數a,即
0012FF2A:0x12
a字符串的
0012FF2B:0x0 地址
0012FF2C:0xe
0012FF2D:0x0
0012FF2E:0x0
0012FF2F:0x0
由于c編譯器不會自己做邊界檢查的,所以,如果buf中的內容足夠長,而不是hello,那么很有可能覆蓋掉原來的返回地址,那么程序就會跳轉到其他地方,為了試驗,我們定義一個簡單的函數echo,讓test返回的時候跳轉到echo。
用printf(“&echo=%p\n”,&echo);我已經知道了echo的地址是0x0040100f,從上面的例子已經知道從0012ff14到0012ff27要覆蓋多少數據,數一下就知道了?改寫如下:
Copycode
#include“string.h”
voidtest(char*a);
voidecho();
intmain(intargc,char*argv[])
{
chara[16];
inti;
for(i=0;i《16;i++)a[i]=‘x’; //覆蓋不重要的部分,為了達到返回地址
a[16]=0xf; //在這里改寫了返回地址
a[17]=0x10;
a[18]=0x40;
a[19]=0x00; //一方面高字節正好是00,同時00又是字符串的結尾
test(a);
return0;
}
voidtest(char*a)
{
char*j;
charbuf[5];
strcpy(buf,a); //分配的緩沖區只有5,結果卻有19,溢出了!
printf(“&main=%p\n”,&main);
printf(“&buf=%p\n”,&buf);
printf(“&a=%p\n”,&a);
printf(“&echo=%p\n”,&echo);
printf(“&test=%p\n”,&test);
for(j=buf-8;j《((char*)&a)+8;j++)
printf(“%p:0x%x\n”,j,*(unsignedchar*)j);
}
voidecho()
{
printf(“haha!\n”);
printf(“haha!\n”);
printf(“haha!\n”);
printf(“haha!\n”);
}
結果,我們看到地址顯示完以后,出現了echo函數里面的haha!\nhaha!\nhaha!\nhaha!\n,說明溢出跳轉成功,但是,在結束的時候出現了程序崩潰,這是因為echo找不到它的返回地址導致的。但是今天我們的目的,利用溢出執行其他代碼的任務已經實現了
1988年,世界上第一個緩沖區溢出攻擊——Morris蠕蟲在互聯網上泛濫,短短一夜的時間全世界6000多臺網絡服務器癱瘓或半癱瘓,不計其數的數據和資料被毀。造成一場損失近億美元的空前大劫難!
緩沖區
計算機程序一般都會使用到一些內存,這些內存或是程序內部使用,或是存放用戶的輸入數據,這樣的內存一般稱作緩沖區。
溢出
溢出是指盛放的東西超出容器容量而溢出來了,在計算機程序中,就是數據使用到了被分配內存空間之外的內存空間。
緩沖區溢出
緩沖區溢出簡單的說就是計算機對接收的輸入數據沒有進行有效的檢測(理想的情況是程序檢查數據長度并不允許輸入超過緩沖區長度的字符),向緩沖區內填充數據時超過了緩沖區本身的容量,而導致數據溢出到被分配空間之外的內存空間,使得溢出的數據覆蓋了其他內存空間的數據。
緩沖區溢出是一種非常危險的漏洞,在各種操作系統、應用軟件中廣泛存在。利用緩沖區溢出攻擊,可以導致程序運行失敗、系統宕機、重新啟動等后果。更為嚴重的是,可以利用它執行非授權指令,甚至可以取得系統特權,進而進行各種非法操作。
緩沖區溢出就好比一個杯子倒太多的水,灑出來,這叫溢出。它是一種非常普遍、非常危險的漏洞,最常出現在C/C++編寫的程序中。
早期,人們還不那么重視數據安全的時候,往往會使用像strcpy這類不安全函數,這種函數對用戶的輸入不作任何檢查,總之,來自不拒,那么,分配的內存空間是有限的,如果輸入超長的字符串,必然會導致溢出。
講了這么多,小伙伴會不會有點暈嗎?其實,緩沖區溢出很容易理解的。下面我們看一個具體的例子:
這段代碼存在一個經典的緩沖區溢出漏洞strcpy,為str變量分配了8個字節的空間,輸入小于等于8個字符,那沒問題:
但是當我們輸入超長的數據時,問題出現了
緩沖區溢出帶來的危害
在計算機安全領域,緩沖區溢出就好比給自己的程序開了個后門,這種安全隱患是致命的。緩沖區溢出在各種操作系統、應用軟件中廣泛存在。而利用緩沖區溢出漏洞實施的攻擊就是緩沖區溢出攻擊。
緩沖區溢出攻擊,可以導致程序運行失敗、系統關機、重新啟動,或者執行攻擊者的指令,比如非法提升權限。假如問題出現在常用的一些軟件,比如操作系統、瀏覽器、通訊軟件等,那后果不堪想象。
緩沖區溢出中,最為危險的是堆棧溢出,因為入侵者可以利用堆棧溢出,在函數返回時改變返回程序的地址,讓其跳轉到任意地址,帶來的危害一種是程序崩潰導致拒絕服務,另外一種就是跳轉并且執行一段惡意代碼,比如得到系統權限,然后為所欲為。
有可能小伙伴你會對病毒/蠕蟲沒有概念,那我們來舉一個簡單的例子吧。平時玩電腦的時候,你正在聽歌,突然間收到未知的“朋友”發給你的一個m3u音頻文件,并引導你進行電擊——“這首歌很好聽呢,你也聽聽”。
湊巧的是,你電腦剛好了就裝了VUPlayer.exe 這個播放器,不多想,就打開這個音樂想聽聽看,奇怪的是,軟件閃了一下就退出了,你以為是文件損壞了了吧。其實,這個時候,你已經被黑了,沒錯!這就是那個播放器。
很普通的播放器,但是它存在致命緩沖區溢出漏洞。我們構造10000個A的m3u文件,然后用調試器附加VUPlayer.exe,打開文件:
我們看到下面的內存全部被字符A覆蓋了,其實就是使用了不安全的lstrcpyA函數導致溢出,這個是棧溢出,還有一種是堆溢出,但本質是一樣的。
證明有溢出漏洞之后,往往需要進一步分析,不同的人目的不竟相同,但需要注意的是,也有很多“黑市”的不法分子會攻擊政府機構或企業的系統,盜取機密文件等,這是非常危險的。
如何預防
緩沖區溢出是代碼中固有的漏洞,除了在開發階段要注意編寫正確的代碼之外,對于用戶而言,一般的防范措施為
1、關閉端口或服務,管理員應該知道自己的系統上安裝了什么,并且哪些服務正在運行。
2、安裝軟件廠商的補丁,需要及時安裝漏洞補丁。
3、在防火墻上過濾特殊的流量,無法阻止內部人員的溢出攻擊。
4、檢查關鍵的服務程序,看看是否有可怕的漏洞。
5、以所需要的最小權限運行軟件。
6、可借助安防軟件提升操作系統安全級別,比如優炫操作系統安全增強系統。
評論
查看更多