整型溢出的原理及三個(gè)實(shí)例

字號(hào):

一、前言
    因?yàn)樽罱鼛啄暌恢倍荚诟櫨W(wǎng)絡(luò)安全領(lǐng)域的新漏洞,所以特別強(qiáng)烈的感覺到出現(xiàn)的系統(tǒng)漏洞有越來越難利用的趨勢(shì)。以IIS的溢出漏洞為例,從前幾年的.htr映射的溢出漏洞、.printer映射的溢出以及后來的.ida/idq溢出,一直到去年的.asp溢出,真的是一次比一次難以利用了,攻擊成功的概率越來越小。究其原因,就是因?yàn)檐浖S商的安全意識(shí)越來越強(qiáng),像用strcpy直接拷貝buffer造成溢出的這種低級(jí)漏洞幾乎是再也不可能出現(xiàn)了。
    即使是這樣,也并不意味著以后再也不會(huì)出現(xiàn)漏洞了,因?yàn)楣艏夹g(shù)也是在不斷發(fā)展的。像最近整型溢出又是比較熱門的東西,因?yàn)樽罱恍┍容^嚴(yán)重的漏洞都是由于整型溢出造成的,例如IIS ASP溢出、Apache分塊編碼溢出、openssh響應(yīng)機(jī)制溢出等等。雖然這種溢出本身并沒有什么高深之處,但是也有必要進(jìn)行總結(jié),因?yàn)榻窈笤谶@方面很可能會(huì)產(chǎn)生相當(dāng)多的漏洞。
    二、整型溢出與傳統(tǒng)溢出的共性和區(qū)別
    首先應(yīng)該明確所謂“溢出”的概念,根據(jù)我的理解,應(yīng)該是程序外部的數(shù)據(jù)大小超出了原數(shù)據(jù)類型所能表達(dá)的范圍,造成某些錯(cuò)誤的操作。這里所提到的數(shù)據(jù)類型不僅僅是字符串類型,也包括整型、無符號(hào)整型甚至布爾類型等一切由程序外部所提供的數(shù)據(jù)。從這個(gè)角度來說,格式化字符串漏洞應(yīng)該不屬于溢出的范圍。
    傳統(tǒng)的溢出一般都是發(fā)生在字符串類型數(shù)據(jù)上面,用戶提交的數(shù)據(jù)超出程序分配的字符串大小,造成覆蓋系統(tǒng)數(shù)據(jù)結(jié)構(gòu),最終導(dǎo)致程序流程改變。而整型溢出顧名思義,就是由用戶提交的整型數(shù)據(jù)超出程序內(nèi)部對(duì)整型數(shù)的安全要求,造成違反原來的程序限制,導(dǎo)致其他類型的溢出。一般來說,整型溢出并不能直接導(dǎo)致改變程序流程,它是由整型溢出造成字符串類型的溢出,從而導(dǎo)致覆蓋系統(tǒng)數(shù)據(jù)結(jié)構(gòu),改變程序流程。
    值得注意的就是程序內(nèi)部對(duì)整型數(shù)的安全數(shù)據(jù)范圍要求,而不是僅僅字符串?dāng)?shù)據(jù)范圍的要求,這是整型溢出的根本原因,下面將詳細(xì)介紹。
    三、什么是整型數(shù)溢出
    1.計(jì)算機(jī)中的整數(shù)概念
    在計(jì)算機(jī)中,整型的概念就是一個(gè)特定的變量類型。在不同的CPU系統(tǒng)上被編譯處理后,整型和指針的尺寸一般是相同的。例如,在32位的系統(tǒng)中,比如x86,一個(gè)整數(shù)是32位;而在64位的系統(tǒng)中,比如SPARC,一個(gè)整數(shù)是64位長(zhǎng)。本文中所談到的例子是在32位的系統(tǒng)環(huán)境和32位的整數(shù),并且用10進(jìn)制來表示它。
    但是僅僅是這樣還不夠,因?yàn)檫@樣還無法表示負(fù)數(shù),所以就需要一種機(jī)制僅僅用位來代表負(fù)數(shù),通過一個(gè)變量的位來決定正負(fù)。如果位置1,這個(gè)變量就被解釋為負(fù)數(shù);如果置0,這個(gè)變量就解釋為正整數(shù)。因此通常的高級(jí)語言中都有有符號(hào)整型(int)和無符號(hào)整型(unsigned int)之分,下面我們就要討論這兩種整型數(shù)所帶來的安全問題。
    2.對(duì)整型數(shù)的安全要求
    一般程序員寫程序,對(duì)于整型數(shù)僅僅考慮使用范圍,而不考慮它的安全要求。對(duì)于不同用途的整型數(shù),其安全要求也不相同。例如最容易出問題的是由用戶提交的用作長(zhǎng)度變量的整型數(shù),對(duì)其他部分?jǐn)?shù)據(jù)的安全要求往往集中在上面。用作長(zhǎng)度的變量一般要求使用無符號(hào)整型數(shù)。在32位系統(tǒng)中,無符號(hào)整型數(shù)(unsigned int)的范圍是從0-0xffffffff。不僅要保證用戶提交的數(shù)據(jù)在此范圍內(nèi),還要保證對(duì)用戶數(shù)據(jù)進(jìn)行運(yùn)算并存儲(chǔ)后仍然在此范圍內(nèi)。
    四、整型數(shù)溢出的成因分類
    整型數(shù)溢出從造成溢出原因的角度來說可以分為三大類:存儲(chǔ)溢出、計(jì)算溢出和符號(hào)問題。下面就分別來談?wù)勊麄冎g的共性和區(qū)別:
    1.存儲(chǔ)溢出
    存儲(chǔ)溢出是最簡(jiǎn)單的一類,也很容易理解。簡(jiǎn)單說就是使用不同的數(shù)據(jù)類型來存儲(chǔ)整型數(shù)造成的。例如下面程序所示:
    int len1 = 0x10000;
    short len2 = len1;
    由于len1和len2的數(shù)據(jù)類型長(zhǎng)度不一樣,len1是32位,而len2是16位,因此進(jìn)行賦值操作后,len2無法容納len1的全部位,導(dǎo)致了與預(yù)期不一致的結(jié)果,即len2等于0。
    看到這里讀者可能會(huì)想,只要不把長(zhǎng)類型的變量賦給短類型變量就行了。其實(shí)并不是那么簡(jiǎn)單,把短類型變量賦給長(zhǎng)類型變量同樣存在問題,例如如下代碼:
    short len2 = 1;
    int len1 = len2;
    上面代碼的執(zhí)行結(jié)果并非總是如預(yù)期的那樣使len1等于1,在很多編譯器編譯的程序中結(jié)果是使len1等于0xffff0001,實(shí)際上就是一個(gè)負(fù)數(shù)。這是因?yàn)楫?dāng)len1的初始值等于0xffffffff,而把short類型的len2賦值給len1時(shí)只能覆蓋掉其低16位,這就造成了安全隱患。