視頻鏈接:https://drive.google.com/open?id=1hT8f4Iq_9_DVjKqAkR5JpvevjQOxXLyQ
課程編寫 |
||
類別 |
內容 |
|
實驗課題名稱 |
整數溢出實驗 |
|
實驗目的與要求 |
了解整數及整數溢出的基本概念 了解整數溢出的常見類型 掌握整數溢出的基本原理 通過編寫代碼,體驗整數溢出 |
|
實驗環境 |
VPC1(虛擬PC) |
Windows XP操作系統 |
軟件描述 |
命令行窗口 實驗代碼 |
|
預備知識 |
1、整數及整數溢出 關於整數的概念,應該說我們在上中學的時候就學過了。這里我們需要了解的是:整數分為無符號和有符號兩類,其中有負符號整數最高位為 1,正整數最高位為 0,無符號整數無此限制;此外,常見的整數類型有 8 位(布爾、單字節字符等)、16 位(短整型、Unicode等)、32 位(整型、長整型)以及 64 位(__int64)等等。對於本文來說,了解這些就基本足夠了。 關於整數溢出,簡而言之,就是往存儲整數的內存單位中存放的數據大於該內存單位所能存儲的最大值,從而導致了溢出。歸根到底,造成整數溢出漏洞的根本原因還是編程人員由於自身疏忽而對整數進行了錯誤操作引起的。 2、導致漏洞的幾種整數誤操作 一般說來,主要有三類整數操作可以導致安全性漏洞,下面列出每類的典型例子: 2.1.無符號整數的下溢和上溢 無符號整數的下溢問題是由於無符號整數不能識別負數所導致的。示例代碼如下: BOOL fun(size_tcbSize) { if(cbSize> 1024) rerurn FALSE; char *pBuf = new char[cbSize – 1]; //未對 new 的返回直進行檢查 memset(pBuf, 0x90,cbSize – 1); …… return TRUE; } 在上面代碼中,在調用 new 分配內存后,程序未對調用結果的正確性進行檢測。如果cbSize 為 0 的話,則 cbSize – 1 為-1。但是 Memset 中第 3 個參數本身是無符號數,因此會將-1 視為正的 0xffffffff,函數執行之后程序當然就只有崩潰了。無符號整數的上溢問題也不難理解,示例代碼如下: BOOL fun(char *s1,size_t len1, char *s2,size_t len2) { if(len1 + len2 + 1 > 1024) return FALSE; charpBuf = new char[len1 + len2 + 1]; if(buf == NULL) return FALSE; memcpy(buf, s1, len1); memcpy(buf + len1, s2, len2); //可能造成程序崩潰 …… return TRUE; } 本例子中代碼看起來沒什么問題,該檢測的地方也都檢測了。但這段代碼卻可能出現整數上溢問題,len1 和 len2 都是無符號整數,如果 len1 = 8,len2 = 0xffffffff,由於加操作的限制,8+0xffffffff+1 產生的結果是 8。也就是說,new 操作只分配 8 字節的內存,而后面卻要進行長達 0xffffffff 的串拷貝工作,結果肯定也是程序崩潰。 2.2.符號的問題 符號問題可以是多種多樣的,但有幾點是應該注意的:有符號整數之間的比較;有符號整數的運算;無符號整數和有符號整數的對比。這里舉一個典型的例子: intcopy_something(char *buf,intlen) { charszBuf[800]; if(len>sizeof(szBuf)) /* [1] */ { return -1; } return memcpy(szBuf,buf,len); /* [2] */ } 上面代碼的問題在於 memcpy 使用無符號整數作為 len 參數,但是在之前的數據邊界檢測使用了有符號整數。假設提供一個負數的 len,這樣可以繞過[1]的檢測,但是這個值同樣被使用在[2]的 memcpy 函數的參數里面,len 可能被轉換成一個非常大的正整數,導致 kbuf緩沖區后面的數據被重寫,進而使得程序崩潰。 2.3.截斷的問題 截斷問題主要發生在大范圍整數(如 32 位)拷貝給小范圍整數(如 16 位)的時候可能產生的。同樣來看一個例子: BOOL fun(byte *name, DWORD cbBuf) { unsigned short cbCalculatedBufSize = cbBuf; byte *buf = new byte[cbCalculatedBufSize]; if (buf == NULL) return FALSE; memcpy(buf, name,cbBuf); …… return TRUE; } 如果 cbBuf 是 0x00010020,又會如何呢?cbCalculatedBufSize 只有 0x20,因為只從0x00010020 復制了低 16 位。因此,僅分配了 0x20 個字節,並且 0x00010020字節復制到新分配的目標緩沖區中。如果整數溢出發生,之后的所有相關操作的結果都將發生變化。與緩沖區溢出不同的是,整數溢出發生時不會馬上發生異常,即使程序執行結果與預期的不同,也很不容易發現問題所在。前面提到,整數溢出在很多時候會導致緩沖區溢出漏洞的發生,包括堆棧溢出和堆溢出。但並不是所有由整數溢出導致的緩沖區溢出都是可以利用的。相反,大多數情況是不能利用的,原因就在於其中涉及到諸如近乎 4G這樣的大內存操作,會發生段錯誤。 |
|
實驗內容 |
介紹與整數溢出相關的基本概念 掌握各種常見整數溢出的原理 編程模擬不同類型的整數溢出 |
|
實驗步驟 |
1、打開控制台,進入虛擬環境。
2、 編程模擬 Widthness 整數溢出 步驟 1:“開始”“運行”,對話框中輸入“cmd”進入命令行。 步驟 2:可查看d:\tools\51elab5008B中width1.c的代碼如下: /* width1.c - exploiting a trivial widthness bug */ #include <stdio.h> #include<stdlib.h> #include <string.h> int main(int argc, char *argv[]) { unsigned short s; int i; char buf[80]; if(argc< 3){ return -1; } i = atoi(argv[1]); s = i; if(s >= 80){ /* [w1] */ printf("Oh no you don't!\n"); return -1; } printf("s = %d\n", s); memcpy(buf, argv[2], i); //給 s 賦值 65536 時會在這里發生整數溢出 buf[i] = '\0'; printf("buf : %s\n", buf); return 0; } 步驟 3:在命令行中輸入如下內容,查看運行結果:
3、模擬整數運算(Arithmetic) 溢出 步驟 1:“開始”“運行”,對話框中輸入“cmd”進入命令行。 步驟 2:可查看d:\tools\51elab5008B中arithoverflow.c的代碼如下: /* arithoverflow.c - exploiting a trivial widthness bug */ #include <stdio.h> #include<stdlib.h> #include <string.h> int main(int argc, char *argv[]) { unsigned int len1,len2; char mybuf[256]; len1 = 0x104; len2 = 0xfffffffc; if(argc< 2){ return -1; } if((len1 + len2) > 256){ /* [3] */ printf("len1 + len2 > 256 !\n"); return -1; } memcpy(mybuf, argv[1], len1); /* [4] */ memcpy(mybuf + len1, argv[2], len2); printf("mybuf: %s\n", mybuf); return 0; } 步驟 3:在命令行中輸入如下內容,查看運行結果: 4、模擬符號問題導致的緩沖區溢出 步驟 1:“開始”“運行”,對話框中輸入“cmd”進入命令行。 步驟 2:可查看d:\tools\51elab5008B中sigoverflow.c的代碼如下: #include<stdio.h> #include<stdlib.h> #include<string.h> int main(int argc, char *argv[]) { char szBuf[800]; int len = 0xfffffffe; if(len>sizeof(szBuf)) /* [1] */ { return -1; } memcpy(szBuf, argv[1], len); /* [2] */ printf("szBuf:%s\n",szBuf); return 0; } 步驟 3:編譯運行,結果如下所示:
|