零. 課程要點(diǎn):
- 緩沖區(qū)溢出
- 緩沖區(qū)溢出攻擊
- 緩沖區(qū)溢出防范
學(xué)完了數(shù)據(jù)的內(nèi)存結(jié)構(gòu),以及函數(shù)調(diào)用的底層過程,我們就來看一下在各種操作系統(tǒng)和應(yīng)用軟件中廣泛存在的漏洞:緩沖區(qū)溢出展鸡。
一. 緩沖區(qū)溢出
首先,緩沖區(qū)是什么埃难?
緩沖區(qū)是一塊連續(xù)的計(jì)算機(jī)內(nèi)存區(qū)域莹弊,可以是堆棧(自動變量)、堆(動態(tài)內(nèi)存)和靜態(tài)數(shù)據(jù)區(qū)(全局或靜態(tài))涡尘。在C語言中忍弛,通常使用字符數(shù)組和內(nèi)存分配函數(shù)實(shí)現(xiàn)緩沖區(qū)。如下圖所示:
那么什么是緩沖區(qū)溢出考抄?
緩沖區(qū)溢出就是指當(dāng)計(jì)算機(jī)程序向緩沖區(qū)內(nèi)填充的數(shù)據(jù)位數(shù)超過了緩沖區(qū)本身的容量细疚,溢出的數(shù)據(jù)覆蓋在其它的數(shù)據(jù)或受保護(hù)空間中。
由于C語言沒有數(shù)組越界檢查機(jī)制川梅,當(dāng)向局部數(shù)組緩沖區(qū)里寫入的數(shù)據(jù)超過為其分配的大小時(shí)疯兼,就會發(fā)生緩沖區(qū)溢出。舉個(gè)例子:
double fun(int i)
{
volatile double d[1] = {3.14};
volatile long int a[2];
a[i] = 1073741824;
return d[0];
}
當(dāng)調(diào)用這個(gè)函數(shù)時(shí)贫途,我們發(fā)現(xiàn)結(jié)果如下:
fun(0) = 3.14
fun(1) = 3.14
fun(2) = 3.1399998664856
fun(3) = 2.00000061035156
fun(4):Segmentation fault
上面就是一個(gè)典型的對數(shù)組的越界訪問吧彪,為什么會出現(xiàn)上面的結(jié)果?只需要看一下棧中的數(shù)據(jù)內(nèi)容就能明白:
當(dāng)i=0
和i=1
時(shí)丢早,1073741824存儲于正確的位置姨裸,但是當(dāng)i=2
時(shí),a[2] = 1073741824
,雖然我們認(rèn)為沒有定義a[2]
傀缩,但是根據(jù)前面的知識那先,我們知道數(shù)組元素可以使用指針來訪問,因此對數(shù)組的引用沒有邊界約束扑毡,因此數(shù)據(jù)覆蓋了d[0]
的地方胃榕,造成了錯(cuò)誤盛险。如果i
更大瞄摊,那就有可能在受保護(hù)的地方寫入了數(shù)據(jù),造成程序崩潰甚至被劫持苦掘。
二. 緩沖區(qū)溢出攻擊
造成緩沖區(qū)溢出的原因是沒有對棧中作為緩沖區(qū)的數(shù)組的訪問進(jìn)行越界檢查换帜。緩沖區(qū)溢出是一種非常普遍、非常危險(xiǎn)的漏洞鹤啡,在各種操作系統(tǒng)惯驼、應(yīng)用軟件中廣泛存在。利用緩沖區(qū)溢出攻擊递瑰,可導(dǎo)致程序運(yùn)行失敗祟牲、系統(tǒng)關(guān)機(jī)、重新啟動等后果抖部。
在前面的學(xué)習(xí)中我們知道在函數(shù)調(diào)用過程中说贝,會把返回地址入棧,并在函數(shù)調(diào)用結(jié)束后取出并跳轉(zhuǎn)慎颗。如果利用緩沖區(qū)溢出將函數(shù)返回地址修改為指向一段精心安排的惡意代碼乡恕,從而改變程序正常流向,則可達(dá)到危害系統(tǒng)安全的目的俯萎。最常見的手段是通過制造緩沖區(qū)溢出使程序運(yùn)行一個(gè)用戶shell傲宜,再通過shell執(zhí)行其它命令。若該程序有root或suid執(zhí)行權(quán)限夫啊,則攻擊者就獲得一個(gè)有root權(quán)限的shell函卒,進(jìn)而可對系統(tǒng)進(jìn)行任意操作。
舉個(gè)例子:
上面的代碼原意是想打印出第一個(gè)參數(shù)撇眯,但是只分配了16個(gè)字節(jié)的大小报嵌。當(dāng)main函數(shù)調(diào)用outputs時(shí),會先保存返回地址叛本,然后保存EBP舊值沪蓬,重新形成棧底。然后分配16個(gè)字節(jié)的變量来候,并將參數(shù)入棧跷叉,準(zhǔn)備調(diào)用strcpy函數(shù)。
若strcpy復(fù)制了25個(gè)字符到buffer中,并將hacker首址置于結(jié)束符‘\0’前4個(gè)字節(jié)云挟,則在執(zhí)行strcpy后梆砸,hacker代碼首址被置于main棧幀返回地址處,當(dāng)執(zhí)行outputs代碼的ret指令時(shí)园欣,便會轉(zhuǎn)到hacker函數(shù)實(shí)施攻擊帖世。
即假定hacker首址為0x08048411,那么我們構(gòu)造一個(gè)字符串作為參數(shù)傳入:
char code[]=
"0123456789ABCDEFXXXX"
"\x11\x84\x04\x08"
"\x00";
int main(void) {
char *argv[3];
argv[0]="./test";
argv[1]=code;
argv[2]=NULL;
execve(argv[0],argv,NULL);
return 0;
}
就可以轉(zhuǎn)到hacker函數(shù)實(shí)施攻擊沸枯。
三. 緩沖區(qū)溢出防范
函數(shù) | 危險(xiǎn)性 | 解決方案 |
---|---|---|
gets | 最高 | 禁用gets(buf)日矫,改用fgets(buf, size, stdin) |
strcpy | 高 | 檢查目標(biāo)緩沖區(qū)大小,或改用strncpy绑榴,或動態(tài)分配目標(biāo)緩沖區(qū) |
strcat | 高 | 改用strncat |
sprintf | 高 | 改用snprintf哪轿,或使用精度說明符 |
scanf | 高 | 使用精度說明符,或自己進(jìn)行解析 |
sscanf | 高 | 使用精度說明符翔怎,或自己進(jìn)行解析 |
fscanf | 高 | 使用精度說明符窃诉,或自己進(jìn)行解析 |
vfscanf | 高 | 使用精度說明符,或自己進(jìn)行解析 |
vsprintf | 高 | 改為使用vsnprintf赤套,或使用精度說明符 |
vscanf | 高 | 使用精度說明符飘痛,或自己進(jìn)行解析 |
vsscanf | 高 | 使用精度說明符,或自己進(jìn)行解析 |
streadd | 高 | 確保分配的目標(biāo)參數(shù)緩沖區(qū)大小是源參數(shù)大小的四倍 |
strecpy | 高 | 確保分配的目標(biāo)參數(shù)緩沖區(qū)大小是源參數(shù)大小的四倍 |
strtrns | 高 | 手工檢查目標(biāo)緩沖區(qū)大小是否至少與源字符串相等 |
getenv | 高 | 不可假定特殊環(huán)境變量的長度 |
realpath | 高(或稍低容握,實(shí)現(xiàn)依賴) | 分配緩沖區(qū)大小為PATH_MAX字節(jié)宣脉,并手工檢查參數(shù)以確保輸入?yún)?shù)和輸出參數(shù)均不超過PATH_MAX |
syslog | 高(或稍低,實(shí)現(xiàn)依賴) | 將字符串輸入傳遞給該函數(shù)之前唯沮,將所有字符串輸入截成合理大小 |
getopt | 高(或稍低脖旱,實(shí)現(xiàn)依賴) | 將字符串輸入傳遞給該函數(shù)之前,將所有字符串輸入截成合理大小 |
getopt_long | 高(或稍低介蛉,實(shí)現(xiàn)依賴) | 將字符串輸入傳遞給該函數(shù)之前萌庆,將所有字符串輸入截成合理大小 |
getpass | 高(或稍低,實(shí)現(xiàn)依賴) | 將字符串輸入傳遞給該函數(shù)之前币旧,將所有字符串輸入截成合理大小 |
getchar | 中 | 若在循環(huán)中使用該函數(shù)践险,確保檢查緩沖區(qū)邊界 |
fgetc | 中 | 若在循環(huán)中使用該函數(shù),確保檢查緩沖區(qū)邊界 |
getc | 中 | 若在循環(huán)中使用該函數(shù)吹菱,確保檢查緩沖區(qū)邊界 |
read | 中 | 若在循環(huán)中使用該函數(shù)巍虫,確保檢查緩沖區(qū)邊界 |
bcopy | 低 | 確保目標(biāo)緩沖區(qū)不小于指定長度 |
fgets | 低 | 確保目標(biāo)緩沖區(qū)不小于指定長度 |
memcpy | 低 | 確保目標(biāo)緩沖區(qū)不小于指定長度 |
snprintf | 低 | 確保目標(biāo)緩沖區(qū)不小于指定長度 |
strccpy | 低 | 確保目標(biāo)緩沖區(qū)不小于指定長度 |
strcadd | 低 | 確保目標(biāo)緩沖區(qū)不小于指定長度 |
strncpy | 低 | 確保目標(biāo)緩沖區(qū)不小于指定長度 |
vsnprintf | 低 | 確保目標(biāo)緩沖區(qū)不小于指定長度 |