四、实战
由于Windows下的溢出对于本地利用来说没有多大意义,所以我们一个存在HEAP溢出漏洞的网络程序为例:
/*
win_heap_vul.c
Windows下存在HEAP溢出漏洞的服务端程序
*/
#define PORT 1500
#define BUFFLEN 32 //分配内存的大小
#define COPYLEN 64 //实际拷贝的大小
int main()
{
WSADATA wsd;
SOCKET sListen, sClient;
struct sockaddr_in local, client;
int iAddrSize;
HANDLE hHeap;
char *buf1, *buf2;
char buff[4096];
if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)
{
printf(\"Failed to load Winsock!\\n\");
return 1;
}
//建立一个socket监听1500端口
sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
local.sin_addr.s_addr = htonl(INADDR_ANY);
local.sin_family = AF_INET;
local.sin_port = htons(PORT);
if (bind(sListen, (struct sockaddr *)&local, sizeof(local)) == SOCKET_ERROR)
{
printf(\"bind() failed: %d\\n\", WSAGetLastError());
return 1;
}
listen(sListen, 8);
iAddrSize = sizeof(client);
sClient = accept(sListen, (struct sockaddr *)&client, &iAddrSize);
if (sClient == INVALID_SOCKET)
{
printf(\"accept() failed: %d\\n\", WSAGetLastError());
return 1;
}
printf(\"connect form: %s:%d\\n\", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
//我们自己建立一个HEAP,以免破坏掉进程默认HEAP以后shellcode无法正常运行
hHeap = HeapCreate(HEAP_GENERATE_EXCEPTIONS, 0x10000, 0xfffff);
//动态分配一块BUFFLEN大小的(32 bytes)的内存buf1
buf1 = HeapAlloc(hHeap, 0, BUFFLEN);
recv(sClient, buff, 4096, 0);
//注意:这里溢出的不是buff,而是buf1,
//buff是在栈中开辟的缓冲区,它的大小是4096,上面recv的也是4096,所以不会溢出
printf(\"recv1: %s\\n\", buff);
//将从客户端接受到的内容(即buff)拷贝到buf1中
//如果接受到的内容大于32字节将发生溢出
//这里错误的使用了COPYLEN(64 bytes),因此造成溢出
memcpy(buf1, buff, COPYLEN);
//如果覆盖到HEAP中的管理结构,那么当再次动态分配内存时将可能被利用
buf2 = HeapAlloc(hHeap, 0, BUFFLEN);
recv(sClient, buff, 4096, 0);
printf(\"recv2: %s\\n\", buf2);
HeapFree(hHeap, 0, buf1);
HeapFree(hHeap, 0, buf2);
closesocket(sListen);
WSACleanup();
return 0;
}
整个程序很简单,监听在1500端口,先分配了32字节的buf1,并把客户端发送过来的内容的前64字节拷贝到buf1里,这里是由于错误的使用了宏而发生的溢出(应该用BUFFLEN,但用了COPYLEN),这种情况在实际中也是很容易发生的。这样当再分配buf2的时候就会有写内存的操作,使得我们可以利用这个漏洞。
现在我们就可以写个攻击程序来溢出它,并且控制改写任意4字节的内存。那么到底改写什么地方比较合适呢?我想来想去有4种地方可以改写,用来控制去执行我们的shellcode:
1.堆栈中保存的函数返回地址
2.堆栈中保存的的异常处理指针
3.线程默认异常处理指针(顶层异常处理指针)
4.线程环境块(TEB)
1和2都是保存在堆栈中的地址,因此在不同的系统中可能是不一样的,如果改写这两个地址的话虽然也可能成功,但是无法保证程序的通用性,从实际攻击的成功率的角度考虑,就不能用这两种地址。
3是线程默认异常处理指针(即顶层异常处理指针),它在同一版本的操作系统中是一个固定的值。这里稍微介绍一下Windows结构化异常处理的基本原理。Windows的结构化异常处理(SEH)是一种对程序异常的处理机制,它是按照链式层状结构进行处理的。当线程中发生异常时,操作系统首
作者在 2007-01-14 00:49:00 发布以下内容