使用sulley framework

sulley的安装过程就不介绍了,官网的介绍很详细。下面先介绍一些sulley的基本用法,再对WarFTPD1.6(有已知的缓冲区溢出漏洞)进行模糊测试,主要目的是了解sulley的使用方法。

Sulley primitives

在我们开始开始对目标动手前,必须先定义好所有的building blocks(构建块),这些块负责产生协议相关的测试数据。Sulley提供了所需的各种的数据格式,为我们创建简单高效的protocol descriptions提供了便利。这些单独的数据组件叫做primitives(原语)。我们先简短讲解一些fuzz WarFTPD时候会用到的primitives。一旦你理解了如何使用其中一个primitives,那剩下的就很容易了。

Strings

字符串(Strings)是使用最多的primitives。到处都有字符串;用户名,ip地址,目录等等。s_string()指令表示添加进测试数据的primitives是一个可fuzz的字符串。s_string()只有一个参数,就是有效的字符串,用于协议交互中的正常输入。
比如,你fuzzing一个email地址:
s_string(“justin@immunityinc.com”),Sulley会把justin@immunityinc.com当作一个有效值,然后进行各种变形,最后扔给目标程序。让我们看看email地址变成了什么样。

1
2
3
justin@immunityinc.comAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
justin@%n%n%n%n%n%n.com
%d%d%d@immunityinc.comAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Delimiters

Delimiters(定界符),用于将大的字符串分割成小的容易管理的片段。还是用先前的email地址做例子,用 s_delim()指令能够将它分割成更多的fuzz字符串。

1
2
3
4
5
s_string("justin")
s_delim("@")
s_string("immunityinc")
s_delim(".",fuzzable=False)
s_string("com")

通过s_delim(),我们将email地址分成了几个子串,并且告诉Sulley,我们在fuzzing的时候不使用点(.),但是会使用@ 。

Static and Random Primitives

s_static()s_random(),顾名思义,第一个使传入的数据不改变,第二个使数据随机的改变。

1
2
s_static("Hello,world!")
s_static("\x41\x41\x41")

s_random()可以随机产生变长的数据。

1
s_random("Justin",min_length=6, max_length=256, num_mutations=10)

min_length和max_length告诉Sully变形后的数据的长度范围,num_mutations为可选参数,表示变形的次数,默认为25次。
在我们的例子,使用“Justin”作为源数据,经过10次变形,产生6-256个长度的字符。

Binary Data

Binary Data(二进制数据)是数据表示中的瑞士军刀。Sulley几乎能处理所有二进制数据。当我们在处理一些未知协议的数据包的时候,也许只是想看看服务器是如何回应我们生成的这些没有意义的数据的,这时候s_binary()就非常有用了。

1
s_binary("0x00 \\x41\\x42\\x43 0d 0a 0d 0a")

Sully能识别出所有这类的数据,然后像将它们当作字符串使用。

Integers

Integers(整数)的应用无处不在,从能看得见的明文数据,到看不见的二进制协议,以及数据长度,各种结构,等等。
下面列出了Sulley支持的主要几种整数类型。
1 byte – s_byte(), s_char()
2 bytes – s_word(), s_short()
4 bytes – s_dword(), s_long(), s_int()
8 bytes – s_qword(), s_double()

Blocks and Groups

Blocks(块)Groups(组)是Sulley提供的强大的组织工具。Blocks将独立的primitives组装成一个的有序的块。Groups 中包含了一些特定的primitives,一个Group和一个Block结合后,每次fuzzer调用Block的时候,都会将Group中的数据循环的取出,组成不同的Block。
下面就是一个使用块和组 fuzzing HTTP的例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# import all of Sulley's functionality.
from sulley import *
# this request is for fuzzing: {GET,HEAD,POST,TRACE} /index.html HTTP/1.1
# define a new block named "HTTP BASIC".
s_initialize("HTTP BASIC")
# define a group primitive listing the various HTTP verbs we wish to fuzz.
s_group("verbs", values=["GET", "HEAD", "POST", "TRACE"])
# define a new block named "body" and associate with the above group.
if s_block_start("body", group="verbs"):
# break the remainder of the HTTP request into individual primitives.
s_delim(" ")
s_delim("/")
s_string("index.html")
s_delim(" ")
s_string("HTTP")
s_delim("/")
s_string("1")
s_delim(".")
s_string("1")
# end the request with the mandatory static sequence.
s_static("\r\n\r\n")
# close the open block, the name argument is optional here.
s_block_end("body")

程序一开始我们就定义了一个叫verbs的组,其中包含了所有HTTP请求类型。之后定义了一个叫body的块,并且和verbs组绑定。这意味着,以后Sulley每次调用body内的变形数据的时候,都会循环的获取(GET, HEAD, POST, TRACE)4种请求方式,这样一来,一次body内的变形就相当于产生4个不同的body。
到目前为止,我们已经讲解完了Sulley的基础知识。当然Sulley不仅仅如此,还有数据解码,校验和计算,长度自动处理等等。想深入学习的同学可以看Pedram写的Fuzzing: Brute Force Vulnerability Discovery (Addison-Wesley, 2007),一本综合了Sulley和fuzzing相关技术的好书。现在该开始对WarFTPD下手了。我们要先创建自己的primitive集合,然后将它们传给负责构建测试的框架内。

Fuzzing WarFTPD

FTP认证命令的格式如下:
USER <USERNAME>
PASS <PASSWORD>
一旦客户端传入了有效的用户名和密码后,服务器就会赋予客户端,传输文件,改变目录,查询文件等各种权限。当然USER和PASS命令只是FTP服务器提供的功能中的一个子集,在认证成功后还有很多别的功能,如下面列出的。这些新的命令都要加入到我们程序的协议框架(protocol skeleton)中。FTP协议详细的命令,请看rfc959。
CWD <DIRECTORY> - change working directory to DIRECTORY
DELE <FILENAME> - delete a remote file FILENAME
MDTM <FILENAME> - return last modified time for file FILENAME
MKD <DIRECTORY> - create directory DIRECTORY

创建ftp协议框架

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
#ftp.py
from sulley import *

s_initialize("user")
s_static("USER")
s_delim(" ")
s_string("bruce")
s_static("\r\n")
s_initialize("pass")
s_static("PASS")
s_delim(" ")
s_string("bruce")
s_static("\r\n")
s_initialize("cwd")
s_static("CWD")
s_delim(" ")
s_string("c: ")
s_static("\r\n")
s_initialize("dele")
s_static("DELE")
s_delim(" ")
s_string("c:\\test.txt")
s_static("\r\n")
s_initialize("mdtm")
s_static("MDTM")
s_delim(" ")
s_string("C:\\boot.ini")
s_static("\r\n")
s_initialize("mkd")
s_static("MKD")
s_delim(" ")
s_string("C:\\TESTDIR")
s_static("\r\n")

protocol skeleton完成之后,让我们开始创建Sulley会话,把所有的请求信息连起来,同时启动网络嗅探和客户端调试。

Sulley会话

Sulley会话包含了请求数据整合,网络数据包的捕捉,进程调试,崩溃报告,和虚拟机控制。先让我们定义一个会话文件,然后详细的分析每个部分。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# ftp_session.py
from sulley import *
from requests import ftp # this is our ftp.py file
def receive_ftp_banner(sock):
sock.recv(1024)
sess = sessions.session(session_filename="audits/warftpd.session")
target = sessions.target("192.168.231.130", 21)
target.netmon = pedrpc.client("127.0.0.1", 26001)
target.procmon = pedrpc.client("127.0.0.1", 26002)
target.procmon_options = { "proc_name" : "war-ftpd.exe" }
# Here we tie in the receive_ftp_banner function which receives
# a socket.socket() object from Sulley as its only parameter
sess.pre_send = receive_ftp_banner
sess.add_target(target)
sess.connect(s_get("user"))
sess.connect(s_get("user"), s_get("pass"))
sess.connect(s_get("pass"), s_get("cwd"))
sess.connect(s_get("pass"), s_get("dele"))
sess.connect(s_get("pass"), s_get("mdtm"))
sess.connect(s_get("pass"), s_get("mkd"))
sess.fuzz()

receive_ftp_banner()是必须的,因为每个FTP服务器在客户端连接上的时候,都会发送banner(标识)。我们将它和sess.pre_send绑定起来,这样Sulley发送fuzzing数据前的时候就会先接收FTP banner。和receive_ftp_banner一样,pre_send也只接收一个由Sulley传递的sock对象。
第一步,我们创建一个会话文件,用于记录当前fuzzer的状态,同时控制fuzzing的启动和停止。
第二步,定义攻击的目标,包括IP地址和端口号。这里设置成 192.168.231.130端口21(这是我们运行WarFTPD虚拟机的IP)。
第三步,设置网络嗅探的端口为26001,IP地址和FTP服务器的地址一样,这个端口用于接受Sulley发出的命令。
第四步,设置调试器监听的端口26002,这个端口用于接收Sulley发出的调试命令。
第五步,procmon_options选项告诉调试器我们关注的进程是war-ftpd.exe。
第六步,在会话中加入定义好的目标对象。
第七步,将FTP请求指令有序的组织好。先是认证,然后将操作指令和需要的密码成对传入。
最后,启动Sulley开始fuzzing。

网络和进程监控

Sulley的优点之一就是能非常好的跟踪fuzz期间的数据交互,以及目标系统的崩溃信息。这样我们就能在第一时间内分析出引起目标崩溃的数据包,然后快速的开发出exploit。
在Sulley的主目录下可以找到process_monitor.py和network_monitor.py两个脚本,他们分别负责网络监控和进程监控。
如下启动进程监控。

1
$ python process_monitor.py -c audits\warftpd.crash -p war-ftpd.exe

1
2
3
4
5
6
7
8
9
10
11
$ python network_monitor.py
Output:
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] \Device\NPF_GenericDialupAdapter
[1] {83071A13-14A7-468C-B27E-24D47CB8E9A4} 192.168.231.130

在这里我们需要使用[1]网络接口。如下启动网络监控。

1
$ python network_monitor.py -d 1 -f "src or dst port 21" -P pcaps

提示:在启动之前必须先建立pcaps目录。

Fuzzing和Web界面

现在我们启动Sulley,并使用内置的Web界面观察整个fuzz过程。

1
$ python ftp_session.py

用浏览器打开http://127.0.0.1:26000,将看到fuzzing的过程。
类似的模糊测试器还有SPIKE、Peach等,也是非常有名,以后有机会希望能学习一下。
reference
http://biancheng.dnbcw.net/win2003/341413.html