溢出漏洞

計算機程序缺陷

溢出漏洞是一種計算機程序的可更正性缺陷。溢出漏洞的全名:緩衝區溢出漏洞因為它是在程序執行的時候在緩衝區執行的錯誤代碼,所以叫緩衝區溢出漏洞。

簡介


它一般是由於編程人員的疏忽造成的。
具體的講,溢出漏洞是由於程序中的某個或某些輸入函數(使用者輸入參數)對所接收數據的邊界驗證不嚴密而造成。
根據程序執行中堆棧調用原理,程序對超出邊界的部分如果沒有經過驗證自動去掉,那麼超出邊界的部分就會覆蓋後面的存放程序指針的數據,當執行完上面的代碼,程序會自動調用指針所指向地址的命令。
根據這個原理,惡意使用者就可以構造出溢出程序。

溢出原理


其實溢出原理很簡單(我以前以為很難理解,太菜了,o(∩_∩)o…)。當然,這裡為了讓大家容易理解,會引用一些程序實例(如果沒有編程基礎的,可以略過程序不看,影響不大,還是能理解的),而且說得會比較通俗和簡單,不會太深入。
從書上找來找去,終於找到一個適合的程序(汗!要找符合的程序簡單啊,但是要找特級菜鳥覺得特別簡單的程序就不多了,55~~)。大家看看下面這段程序:
#include “stdafx.h”
#include “string.h”
#include “stdio.h”
char buf[255],pass[4];
int main (int argc,char* argv[ ])
{
printf(“請輸入您的密碼:”);
scanf(%s,buf);
strcpy(pass,buf);
if (strcmp(pass,”wlqs”)= =0)
printf (“輸入正確!”);
else printf(“輸入錯誤!);
return 0;
}
(註:“/*”中的中文是對程序的註解)
這是一段密碼驗證程序,與我們平時輸入密碼一樣,先讓用戶輸入密碼,然後在取得真正的密碼,與之對比,如果差異為0,則輸出密碼正確,否則輸出密碼錯誤。很多帳號登錄的程序都是這樣做的,看起來沒有非常合理,其實不然,它有一個致命缺陷!這個漏洞很容易就看出來了。那就是它給數據申請了4個位元組的儲存空間,但是萬一用戶輸入的數據不只4個位元組,那麼剩餘的位元組存放在哪裡?
先舉個例子,有一條一米長的木頭,有一張紅色紙條從尾巴往頭貼,上面寫有字,然後又有一張藍色紙條,上面也寫有字,要從木頭的頭往它的尾巴貼,但是貼了紅色紙條過後只剩4cm的長度,貼完後會有人讀出後面96cm的字,並且執行字條的命令,但是藍色紙條卻有10cm的長度,怎麼辦呢?只有把藍色紙條剩下的部分貼在紅色紙條上了。那麼紅色紙條的一些字就被覆蓋了。但是那個人還是會去讀那後面96cm的字,所以他就只有讀錯,前面讀的都是藍色字條的字。先前去執行的是藍色字條後面6cm的命令。
當然大家看了這個例子也不是很懂,下面來註解一下:
人——CPU
紅色字條上的字——CPU要執行的命令
4cm的長度——計算機為數據申請的內存空間
藍色字條上的字——要儲存的數據
可以看見藍色字條已經覆蓋了紅色字條上的字,然而那個人還是必須讀出後面96cm的字並執行。後面已經不是規定的命令了!他根本就不能執行,根本讀不懂!那麼他就不能執行了,並且報錯。
如圖系統只為我的密碼分配4個位元組的內存,那麼我輸入的密碼是“714718366”循環了6次的,不只4個位元組吧,其他剩下的字元將溢出!剩下的數字將佔用內存空間,那麼系統執行命令的時候將會執行佔用內存的數據,而不是執行原先寫好的命令了!這些數字系統根本就讀不懂,如何執行?那麼它只好報錯了!說此程序遇到問題需要關閉。那麼計算機上的程序將出錯而無法執行或關閉。

本地溢出


上面所說的本地計算機因數據溢出而關閉程序或無法執行就叫做本地溢出。輸入超長的數據已經把計算機要執行的代碼覆蓋掉了,可是,計算機不會管指令有沒有被更改,依舊取原先存放指令的空間里的數據來運行,取到“shujucuole!shujucuole!shujucuole!”這些不合法的溢出數據,它依舊會執行,可是在計算機里這樣的指令是非法指令,也就是不符合計算機邏輯的指令,用戶執行它的時候就會出錯,於是程序就被強行關閉了。
題外話:(想來想去,還是說一說o(∩_∩)o…我的愛好……損人利己的愛好)利用這樣的溢出漏洞可以關閉很多程序,比如各學校機房裡安裝的那些遠程教育系統,學生的計算機被教師的計算機所控制是因為學生機上安裝有一個學生端程序,教師機可以通過教師端來對學生端進行遠程控制,學生端沒有退出功能,學生所在的用戶組也沒有強行結束進程的許可權,當學生不想被老師控制的時候,可以打開學生端自帶的遠程消息功能,在消息里輸入很長的數據,比如幾百上千句“敢控制我!看我不宰了你!”,然後發送,就可以令學生端程序出錯而被系統強行關閉。這招對某些網吧的收費系統也有用的!^_^

遠程溢出


再舉個列子:
#include “stdafx.h”
#include
#pragma comment(lib,”ws2_32”)
int main(int argc,char* argv[ ])
{
char buf[255]=” ”,pass[4]=” ”; //聲明變數,讓計算機分配內存
//================================================================
//這節的代碼功能是初始化網路連接
//並偵聽1234埠等待連接
//沒有編程基礎的特級菜鳥可以略過不看
SOCKET sock1,sock2;
struct sockaddr_in addr1;
struct sockaddr_in addr2;
addr1 .sin_addr.s_addr=INADDR_ANY;
addr1 .sin_family=AF_INET;
addr1 .sin_port=htons(1234);
WSADATA * wsadatal=new WSADATA( );
WSAStartup(MAKEWORD(2,2),wsadatal1);
sock1=socket(AF_INET,SOCK_STREAM,0);
bind(sock1,(sockaddr *)&addr1,sizeof(struct sockaddr) );
listen(sock1,10);
int iSin=sizeof(struct sockaddr_in);
//=================================================================
if(sock2=accept(sock1,(sockaddr *)&addr2,&iSin)
{//有用戶連接進來
send(sock2,“請輸入密碼,密碼正確,則告訴你我的qq:”,36,0);
//發送提示用戶輸入密碼
if (recv(sock2,buf,255,0))
{//接受用戶發送過來的數據並保存在緩衝buf變數里
strcpy (pass,buf);//把緩衝buf變數里的數據複製到pass變數中
if(strcmp(pass,”wlqs”= =0)
//比較pass變數里的數據跟“wlqs”字元串之間的差異是否為0
{//差異為0,則說明兩者相等,密碼正確
send(sock2,”714718366”,9,0);//發送QQ號給用戶
}
else
{//否則就說明密碼錯誤
send (sock2,”密碼錯誤!”,10,0);
}
}
}
//=================[/ft]關閉網路連接並退出=======================
closesocket(sock2);
closesocket(sock1);
return 0;
}
這是一個伺服器程序,當有用戶連接的時候,它會先發送一句話,提示用戶輸入登錄密碼。其實它和前面說的本地溢出例子形似,問題也就處在把數據從緩存複製到內存的那句代碼里,如果遠程用戶輸入的密碼太長,那麼同樣出現溢出的現象。那麼程序就會出錯,服務端將被強行關閉。
比如騰訊公司的即時通訊軟體服務端程序就曾被黑客不停地攻擊導致服務端崩潰,不能正常提供服務,致使很多用戶都不能登陸,即使登陸成功也會在幾分鐘之內再次掉線,就是因為他們的服務端有這樣的漏洞存在,被別人利用了,這給他們以及他們的客戶造成了不可估計的損失。

相關資料


緩衝區溢出漏洞攻擊方式
緩衝區溢出漏洞可以使任何一個有黑客技術的人取得機器的控制權甚至是最高許可權。一般利用緩衝區溢出漏洞攻擊root程序,大都通過執行類似“exec(sh)”的執行代碼來獲得root 的shell。黑客要達到目的通常要完成兩個任務,就是在程序的地址空間里安排適當的代碼和通過適當的初始化寄存器和存儲器,讓程序跳轉到安排好的地址空間執行。
在程序的地址空間里安排適當的代碼
在程序的地址空間里安排適當的代碼往往是相對簡單的。如果要攻擊的代碼在所攻擊程序中已經存在了,那麼就簡單地對代碼傳遞一些參數,然後使程序跳轉到目標中就可以完成了。攻擊代碼要求執行“exec(‘/bin/sh’)”,而在libc庫中的代碼執行“exec(arg)”,其中的“arg”是個指向字元串的指針參數,只要把傳入的參數指針修改指向“/bin/sh”,然後再跳轉到libc庫中的響應指令序列就可以了。當然,很多時候這個可能性是很小的,那麼就得用一種叫“植入法”的方式來完成了。當向要攻擊的程序里輸入一個字元串時,程序就會把這個字元串放到緩衝區里,這個字元串包含的數據是可以在這個所攻擊的目標的硬體平台上運行的指令序列。緩衝區可以設在:堆棧(自動變數)、堆(動態分配的)和靜態數據區(初始化或者未初始化的數據)等的任何地方。也可以不必為達到這個目的而溢出任何緩衝區,只要找到足夠的空間來放置這些攻擊代碼就夠了。
控制程序轉移到攻擊代碼的形式
緩衝區溢出漏洞攻擊都是在尋求改變程序的執行流程,使它跳轉到攻擊代碼,最為基本的就是溢出一個沒有檢查或者其他漏洞的緩衝區,這樣做就會擾亂程序的正常執行次序。通過溢出某緩衝區,可以改寫相近程序的空間而直接跳轉過系統對身份的驗證。原則上來講攻擊時所針對的緩衝區溢出的程序空間可為任意空間。但因不同地方的定位相異,所以也就帶出了多種轉移方式。
(1)Function Pointers(函數指針)
在程序中,“void (* foo) ( )”聲明了個返回值為“void” Function Pointers的變數“foo”。Function Pointers可以用來定位任意地址空間,攻擊時只需要在任意空間里的Function Pointers鄰近處找到一個能夠溢出的緩衝區,然後用溢出來改變Function Pointers。當程序通過Function Pointers調用函數,程序的流程就會實現。
(2)Activation Records(激活記錄)
當一個函數調用發生時,堆棧中會留駐一個Activation Records,它包含了函數結束時返回的地址。執行溢出這些自動變數,使這個返回的地址指向攻擊代碼,再通過改變程序的返回地址。當函數調用結束時,程序就會跳轉到事先所設定的地址,而不是原來的地址。這樣的溢出方式也是較常見的。
(3)Longjmp buffers(長跳轉緩衝區)
C語言中包含了一個簡單的檢驗/恢復系統,稱為“setjmp/longjmp”,意思是在檢驗點設定“setjmp(buffer)”,用longjmp(buffer)“來恢複檢驗點。如果攻擊時能夠進入緩衝區的空間,感覺“longjmp(buffer)”實際上是跳轉到攻擊的代碼。像Function Pointers一樣,longjmp緩衝區能夠指向任何地方,所以找到一個可供溢出的緩衝區是最先應該做的事情。
植入綜合代碼和流程式控制制
常見的溢出緩衝區攻擊類是在一個字元串里綜合了代碼植入和Activation Records。攻擊時定位在一個可供溢出的自動變數,然後向程序傳遞一個很大的字元串,在引發緩衝區溢出改變Activation Records的同時植入代碼(權因C在習慣上只為用戶和參數開闢很小的緩衝區)。植入代碼和緩衝區溢出不一定要一次性完成,可以在一個緩衝區內放置代碼(這個時候並不能溢出緩衝區),然後通過溢出另一個緩衝區來轉移程序的指針。這樣的方法一般是用於可供溢出的緩衝區不能放入全部代碼時的。如果想使用已經駐留的代碼不需要再外部植入的時候,通常必須先把代碼做為參數。在libc(熟悉C的朋友應該知道,現在幾乎所有的C程序連接都是利用它來連接的)中的一部分代碼段會執行“exec(something)”,當中的something就是參數,使用緩衝區溢出改變程序的參數,然後利用另一個緩衝區溢出使程序指針指向libc中的特定的代碼段。
程序編寫的錯誤造成網路的不安全性也應當受到重視,因為它的不安全性已被緩衝區溢出表現得淋漓盡致了。