在渗透测试中使用fuzz技术(附windows安装指南)
前言:本文翻译自sans.org,有删节。在学习sulley的过程中发现中文参考资料很少,所以抛砖引玉翻译一下,希望对学习协议fuzz测试的同学有帮助。本文适合作为学习sulley协议测试过程中参考资料,如果需要学习sulley的使用可以参考其文档。
背景
不久前,我们接到了一个渗透测试的项目。分配任务的时候,团队里有的同学负责无线渗透,有的负责web应用渗透,我分到的任务是对外网开放的网络端口进行测试。我的主要测试范围包括:从外部可以访问的系统接口,不包括客户测的漏洞利用,不包括web应用,不包括外部的资源管理类的攻击(比如域名管理类的攻击),不包括账号密码猜解。
前期的扫描中并没有发现很多有意思的漏洞,这也在意料之中。不过Nmap的扫描也发现了一些有意义的目标。如下图所示。目标部署了一个CheckPoint服务器作为远程V**接入。一共开放了两个端口。
264端口引起了我的兴趣,经过一番查证得知这是CheckPoint V** 客户端用来通信的一个通道,使用的是一种私有协议,叫做SecuRemote。这个协议是没有加密的。网上能找到一些这个协议存在信息泄露的漏洞,不过我们客户这边都是新版本,不存在那些已知的问题。现在我有的资源就是一些协议资料和大把剩余的任务时间,我决定做一下协议fuzzing。
协议分析
做协议fuzz的首要条件就是要熟悉自己测试的协议。SecuRemote作为私有协议,没有详细的文档,而且我也没发现有人作为逆向分析。所以我得通过抓包自己分析协议。在Google上搜索了一下,找到几个别人抓的网络包文件。都是他们抓包让别人帮分析问题的。通过分析这些网络包,我分析出了SecuRemote协议的一些细节。
3次握手之后,CheckPoint客户端会发送两个4字节的小包。对于特定的CheckPoint服务器和客户端来说这两个字节看起来是固定的。在客户端发送第二个包之后,服务端返回了一个4字节的包。跟客户端发的第一个包很像。
第四个包发送字符串"securemote"到服务器。最后是一个NULL的结束字节。开始的4个字节看起来是大端存储的后面字符串的长度。因为0x0b刚好是"securemote0"的长度。第四个包发完之后,服务器返回了一个同样的带字符串长度的包,包含了一些服务器的相关信息。根据之前版本的CheckPoint exp,可以获取更多的协议数据。虽然没法简单的识别出每一个比特位代表的意义。但是根据上面的分析,也可以找到一些有意思的模糊测试目标。
四字节的长度标志。任何时候,在协议中发现了标志长度的字节,一定要对它进行模糊测试。几个固定的x00的字节。减少了我们进行枚举组合的次数。在字符串结尾使用NULL结束符。因为协议提供了长度标志,所以结束符不是必须的。所以测试一下结束符不存在的情况也是不错的选择。
下一步就是准备测试样例,进行模糊测试了。
工具选择
我比较喜欢的智能模糊器是Sulley,一个用python完成的模糊测试框架。不过开发者Pedram已经不再维护Sulley。使用Peach也是一个不错的选择,peach使用XML进行协议描述,相对来说我更喜欢python的语法。在sulley中,我们使用基本的数据类型:字符串区,数字区,分隔符和静态值来描述协议。每一种类型的数据都可以自动突变来进行模糊测试。Sulley也提供了一些高级的功能来描述复杂协议,比如计算数据段长度和进行摘要校检。更多详细的sulley使用方法可以参考Sulley的项目文档。下面的脚本是对客户端发送的4个字节进行测试。其中使用了s_byte()这个定义一个字节的函数。将下面的脚本保存到sulley的目录就可以开始测试了。
#!/usr/bin/env python
# Fuzzing the initial 4-byte packet from client to CheckPoint V** server.
import time
import sys
from sulley import *
# Time to wait between mutations
SLEEP_TIME=0.5
# Time to wait before claiming a host is unresponsive
TIMEOUT=3
# number of crashes to observe before skipping the remainder of a group
CRASH_THRESHOLD=3
# Initialize the Sulley mutation descriptor
s_initialize("SecuRemote-Initial-Packet")
s_byte("x51",full_range=True)
s_static("x00x00x00")
print "Total mutations: " + str(s_num_mutations()) + "n"
print "Minimum time for execution: " + str(round(((s_num_mutations() * (SLEEP_TIME))/3600),2)) + " hours."
print "Press CTRL/C to cancel in ",
for i in range(5):
print str(5 - i) + " ",
sys.stdout.flush()
time.sleep(1)
# For debugging purposes, uncomment these lines to see Sulley's mutations
# in hex dump format
#print "Hex dump mutation output:"
#while s_mutate():
# print s_hex_dump(s_render())
sess = sessions.session(session_filename="SecuRemote-Initial-Packet.sess", sleep_time=SLEEP_TIME, timeout=TIMEOUT, crash_threshold=CRASH_THRESHOLD)
# Tie this session to the SecuRemote-Simple-String fuzzing cases
sess.connect(s_get("SecuRemote-Initial-Packet"))
# Change this IP address to the target system
target = sessions.target("127.0.0.1", 264)
# Add the target to the session (can be repeated for multiple targets)
sess.add_target(target)
# Kick off the fuzzer, monitoring with WebUI on localhost:26000
sess.fuzz()
很不幸的是经过上面的测试,并没有发现有意义的结果。为了更深入的对目标系统进行评估,需要我们的Sulley实现一些协议状态,对协议进行更深入的测试。
对有状态的协议进行测试
在SecuRemote的案例中,从客户端发给V**服务器的头两个包对我们来说并不够吸引人。客户端交换的第四个包才更有趣,包含了一个字符串和长度值,以及NULL结束符。为了测试V**服务器对这个位置的畸形包的返回,我们需要在sulley中先模拟前面的两步。然后再发送这一步的畸形包。Sulley中已经提供了相关的接口。在发送每一个畸形包之前,Sulley会检查一个叫做pre_send的回调函数。通过定义我们自己的回调函数,读写socket,完成初始化的头两个4字节包的发送,然后读取服务器的返回。之后再发送我们的测试畸形包。进行测试的脚本如下:
#!/usr/bin/env python
from sulley import *
SLEEP_TIME=0.5TIMEOUT=3CRASH_THRESHOLD=3# The function Sulley will run prior to sending each mutation. We leverage
# it to setup the target system with the initial packets and response in the
# protocol exchange prior to our target packet.
def preconn(sock):
sock.send("x51x00x00x00") time.sleep(0.5)
sock.send("x00x00x00x21")
# Set a socket timeout on the recv so we aren't waiting indefinitely if
# the server crashed from a previous test case.
sock.settimeout(5) response = sock.recv(4)
print "Setup response: ", for i in response:
print "%02x" % ord(i),
print
s_initialize("SecuRemote-Simple-String")
# Create a size field, which is based on the content of the named block
# Sulley uses ">" to indicate big-endian values, "<" is little-endian
s_size("client-name-string", length=4, endian=">")
# This is the block of data used for filling in the s_sizeif s_block_start("client-name-string"):
# "securemote" is the default string
s_string("securemote")
# constant null terminator
s_byte("x00")
s_block_end()
sess = sessions.session(session_filename="Securemote-Simple-string.sess", sleep_time=SLEEP_TIME, timeout=TIMEOUT, crash_threshold=CRASH_THRESHOLD)
# Call preconn() before each mutation is sent to setup the target
sess.pre_send = preconn
sess.connect(s_get("SecuRemote-Simple-String"))
target = sessions.target("127.0.0.1", 264)
sess.add_target(target)
sess.fuzz()
通过这种方式,可以继续对其他的请求包进行测试。
成果
我们一共进行了好几个晚上的fuzz测试,通过记录的数据发现,确实发现了一些小问题,一些畸形的数据包会导致V**服务重启,达到Dos的效果。客户对我们的这个报告也很认可。
附录:sulley的windows安装指南
测试环境win7+python2.7
安装MinGW Complilier。
在安装引导界面选择C++ Compiler和ObjC Compiler。这里有个问题就是新版的MinGW已经不支持-mno-cygwin选项了。可以选择老版本的MinGW或者安装最新版,然后patch python代码。这里选择后者。
安装python2.7(x64)
安装Git 工具
更新$PATH环境变量,添加C:Python27 和 C:MinGWbin(根据自己的安装路径填写)
安装pydbg
git clone https://Fitblip@github.com/Fitblip/pydbg.git
C:sulley_build>git clone https://Fitblip@github.com/Fitblip/pydbg.git
Cloning into 'pydbg'...
remote: Counting objects: 17, done.
remote: Compressing objects: 100% (12/12), done.
remote: Total 17 (delta 4), reused 17 (delta 4)
Unpacking objects: 100% (17/17), done.
C:sulley_buildpydbg>python setup.py install
running install
running build
running build_py
creating build
creating buildlib
creating buildlibpydbg
...snip...
running install_egg_info
Removing C:python27Libsite-packagespydbg-0.0.0-py2.7.egg-info
Writing C:python27Libsite-packagespydbg-0.0.0-py2.7.egg-info
安装libdasm
下载libdasm
编译扩展并安装。这里需要用到gcc,而gcc4.7.x已经去掉了对-mn0-cygwin的支持。需要先把distutilscygwinccompiler.py文件中的”-mno-cygwin”删掉。总过4,5个地方的样子。参考http://stackoverflow.com/q/6034390/333353
C:sulley_buildlibdisasmpydasm>python setup.py build_ext -c mingw32
running build_ext
building 'pydasm' extension
...snip...
C:sulley_buildlibdisasmpydasm>python setup.py install
running install
running build
running build_ext
running install_lib
copying buildlib.win32-2.7pydasm.pyd -> C:python27Libsite-packages
running install_egg_info
Writing C:python27Libsite-packagespydasm-1.5-py2.7.egg-info
下载sulley
C:sulley_build>git clone https://github.com/OpenRCE/sulley.git
Cloning into 'sulley'...
remote: Counting objects: 148, done.
remote: Compressing objects: 100% (91/91), done.
remote: Total 148 (delta 53), reused 146 (delta 51)
Receiving objects: 100% (148/148), 267.03 KiB, done.
Resolving deltas: 100% (53/53), done.
确认process_monitor.py正常工作。(没有提示缺少模块)
C:sulley_buildsulley>python process_monitor.py
ERR> USAGE: process_monitor.py
<-c|--crash_bin FILENAME> filename to serialize crash bin class to
[-p|--proc_name NAME] process name to search for and attach to
[-i|--ignore_pid PID] ignore this PID when searching for the target process
[-l|--log_level LEVEL] log level (default 1), increase for more verbosity
[--port PORT] TCP port to bind this agent to
下载解压PCapy
下载解压WinPcap Dev Kit,记下文件路径。这里用C:sulley_buildWpdPack
编译PCap并安装
C:sulley_buildpcapy-0.10.5>python setup.py build_ext -c mingw32 -I "C:sulley_buildWpdPackInclude" -L "C:sulley_buildWpdPackLib"
running build_ext
building 'pcapy' extension
creating build
creating buildtemp.win32-2.7
creating buildtemp.win32-2.7Release
creating buildtemp.win32-2.7Releasewin32
...snip...
C:sulley_buildpcapy-0.10.5>python setup.py install
running install
running build
running build_ext
running install_lib
copying buildlib.win32-2.7pcapy.pyd -> C:python27Libsite-packages
running install_data
creating C:python27share
creating C:python27sharedoc
creating C:python27sharedocpcapy
copying README -> C:python27sharedocpcapy
copying LICENSE -> C:python27sharedocpcapy
copying pcapy.html -> C:python27sharedocpcapy
running install_egg_info
Writing C:python27Libsite-packagespcapy-0.10.5-py2.7.egg-info
安装WinPcap这个相信大家机器上都有。
安装Impacket
安装的时候注意要进入到setup.py的目录敲命令
C:sulley_buildImpacket-0.9.6.0>python setup.py install
running install
running build
running build_py
creating build
creating buildlib
creating buildlibimpacket
copying impacketImpactDecoder.py -> buildlibimpacket
copying impacketImpactPacket.py -> buildlibimpacket
copying impacketnmb.py -> buildlibimpacket
copying impacketntlm.py -> buildlibimpacket
copying impacketsmb.py -> buildlibimpacket
copying impacketstructure.py -> buildlibimpacket
copying impacketuuid.py -> buildlibimpacket
copying impacket__init__.py -> buildlibimpacket
creating buildlibimpacketdcerpc
...snip...
确认network_monitor.py正常工作
C:sulley_buildsulley>python network_monitor.py
ERR> USAGE: network_monitor.py
<-d|--device DEVICE #> device to sniff on (see list below)
[-f|--filter PCAP FILTER] BPF filter string
[-P|--log_path PATH] log directory to store pcaps to
[-l|--log_level LEVEL] log level (default 1), increase for more verbosity
[--port PORT] TCP port to bind this agent to
Network Device List:
[0] DeviceNPF_GenericDialupAdapter
[1] {CF0B388B-8DF5-4BC4-8ECF-404F2A1B489C} 10.0.2.64
安装到这里基本就完成了。对于windows平台,还存在几个已知的bug。比如连接需要使用ssl的时候,需要修改sulley/sessions.py的486行。把
if self.ssl:
try:
ssl = socket.ssl(sock)
sock = httplib.FakeSocket(sock, ssl)
except Exception, e:
...
替换成
if self.ssl:
try:
import ssl
sock = ssl.wrap_socket(sock)
except Exception, e:
- 【技术专栏】OpenVirteX体系结构之组件(一)
- 洞察 | 深圳数据分析师的职业前景如何?爬完拉勾数据给你分析 (附代码和过程)
- 【温故】金融数据挖掘之朴素贝叶斯
- 为你的网站加上SSL,可以使用HTTPS进行访问
- LeeCX - 开源后台管理系统简单介绍
- fastdfs 图片服务器 使用java端作为客户端上传图片
- Shiro系列(3) - What is shiro?
- 干货|用python抓取摩拜单车API数据并做可视化分析(源码)
- 干货|机器学习:Python实现聚类算法之K-Means
- 美团点餐—listview内部按钮点击事件
- C4C和CRM里获取当前登录用户分配的Organization Unit信息
- 使用Excel调用ABAP系统的函数
- 如何计算并测量ABAP及Java代码的环复杂度Cyclomatic complexity
- 漫谈版本控制系统
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- GDP越高就越幸福吗?用Python分析《世界幸福指数报告》后我们发现…
- 图解:什么是哈希?
- 【Python基础】一文看懂 Pandas 中的透视表
- 彻底干掉恶心的 SQL 注入漏洞, 一网打尽!
- 某小型公司持续集成工具 Jenkins 实践
- 使用RBAC Impersonation简化Kubernetes资源访问控制
- 求求你别再用 MySQL offset 和 limit 分页了?
- 短视频带货源码,获取购物车中所有商品列表并加载显示
- 【Flutter 实战】菜单(Menu)功能
- 【Flutter 实战】路由堆栈详解
- 【Flutter 实战】全局监听路由堆栈变化
- 数据挖掘从入门到放弃:线性回归和逻辑回归
- 【Flutter 实战】文件系统目录
- 【原创】Spring Boot终极篇《上》
- 【原创】Spring Boot终极篇《下》