实验名称:探索栈溢出远程利用
实验环境:
System |
IP |
Windows7 |
192.168.127.133 |
Kali |
192.168.127.159 |
实验工具:
1、 vc++6.0
2、 vim
3、 windbg
实验原理:
l 栈溢出在缓存空间填入shellcode
l 通过jmp esp找到shellcode地址
l 通过pe文件结构特点寻找kernel,进而找到GetProcAddress()就可以获取各种api地址
l 通过异或加密shellcode避免出现0x00字符造成截断
实验过程:
1) 服务器代码解析:
#include<stdio.h>
#include<string.h>
#include<winsock.h>
#pragma comment(lib,"ws2_32.lib")
void printf_data(char *data)
{
char message[20];
strcpy(message,data);
printf("receive data: %s\n",message);
return 0;
}
/*struct sockaddr
{
unsigned short sa_family;
char sa_data[14];
};
struct sockaddr_in
{
short int sin_family;
unsigned short int sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
};
struct in_addr
{
unsigned long s_addr;
};*/
int main(void)
{
struct sockaddr_in sin;
struct sockaddr_in remote_addr;
int len=sizeof(remote_addr);
int ret;
char rev_data[2000];
char *send_data="receive the messge!\n";
SOCKET s_socket;
SOCKET s_new_socket;
WORD sockVersion=MAKEWORD(2,2);
WSADATA wsaData;
if(WSAStartup(sockVersion,&wsaData)!=0)
{
return -1;
}
s_socket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(s_socket==-1)
{
printf("fail");
return 0;
}
sin.sin_family=AF_INET;
sin.sin_port=htons(55555);
sin.sin_addr.S_un.S_addr=INADDR_ANY;
if(bind(s_socket,(LPSOCKADDR)&sin,sizeof(sin))==SOCKET_ERROR)
printf("bind error !");
if(listen(s_socket,5)==SOCKET_ERROR)
{
printf("listen error!\n");
return 0;
}
while(1)
{
printf("wait for connecting...\n");
s_new_socket=accept(s_socket,(SOCKADDR*)&remote_addr,&len);
if(s_new_socket==INVALID_SOCKET)
{
printf("accept error !\n");
continue;
}
printf("success connect with : %s \r\n",inet_ntoa(remote_addr.sin_addr));
while(1)
{
ret =recv(s_new_socket,rev_data,2000,0);
if(ret>0)
{
rev_data[ret]=0x00;
printf_data(rev_data);
send(s_new_socket,send_data,strlen(send_data),0);
}
else
{
closesocket(s_new_socket);
break;
}
}
}
closesocket(s_socket);
WSACleanup();
return 0;
}
Printf_data处存在栈溢出漏洞:
void printf_data(char *data)
{
char message[20];
strcpy(message,data);
printf("receive data: %s\n",message);
return 0;
}
2) 通用shellcode原理&编写
通过pe文件结构特点寻找kernel,进而找到GetProcAddress()
·寻找kernel.dll流程:
1、fs寄存器指向TEB结构
2、Teb偏移0x030处指向peb
3、 Peb偏移0x00c处指向peb_ldr_data
4、PEB_LDR_DATA偏移0x014处指向InMemoryOrdermoduleList
5、List_entry结构体
总流程:
·寻找具体函数流程:
1、Dos头偏移0x03c找到nt头(image_nt_header)
2、进入optionalheader
3、查看DataDirectory
流程:
在导出表中得到函数:
·汇编解析:
·Vc++6.0反汇编后,利用python脚本提取机器码:
3) 测试shellcode
·shellcode存在的问题
·strcpy以0x00字符结尾
·shellcode存在0x00截断字符
4) 解决办法:
用python脚本异或加密shellcode消除0x00字符
加密后:
5) 解密shellcode的汇编&机器码
机器码:
6) 测试新的shellcode
7) 最终shellcode填入情况
buff |
Jmp esp的地址 |
用于解码的shellcode |
经加密的shellcode |
8) 发送构造好的shellcode进行攻击
成功!