9.2 Sulley primitives

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

9.2.1 Strings

字符串(Strings)是使用最多的 primitives。到处都有字符串;用户名,ip 地址,目录等等。

s_string()指令表示添加进测试数据的 primitives 是一个可 fuzz 的字符串。s_string()只有一个参数,就是有效的字符串,用于协议交互中的正常输入。比如,你 fuzzing 一个 email 地址:

s_string("[email protected])

Sulley 会把 [email protected] 当作一个有效值,然后进行各种变形,最后扔给目 标程序。让我们看看 email 地址变成了什么样。

[email protected] AA
justin@%n%n%n%n%n%n.com
%d%d%[email protected] AAAA

9.2.2 Delimiters

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

s_string("justin") 
s_delim("@") 
s_string("immunityinc") 
s_delim(".",fuzzable=False) 
s_string("com")

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

9.2.3 Static and Random Primitives

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

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

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

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

min_length 和 max_length 告诉 Sully 变形后的数据的长度范围, num_mutations 为可选 参数,表示变形的次数,默认为 25 次。

在我们的例子,使用"Justin"作为源数据,经过 10 次变形,产生 6-256 个长度的字符。

9.2.4 Binary Data

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

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

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

9.2.5 Integers

Integers(整数)的应用无处不在,从能看的见的明文数据,到看不见的二进制协议,以及 数据长度,各种结构,等等。

表 9-1 列出了 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()

Listing 9-1: Sulley 支持的整数类型

所有的整数表达式都有几个重要的的选项。endian 项表示整数将以什么样的形式变现出 来,是小端- (<) 还是 大端- (>)格式 l 默认似乎小端。format 项有两个可选值,ascii 和 binary; 代表整数将被如何使用。举个例子,如果你有一个用 ASCII 格式 表示是 1,用 binary 表示 就是\x31。signed 项说明整数是有符号的还是无符号的,这个选项只有在 format 指定为 ascii 后有效,默认似乎 False。最后一个有趣的选项是 full_range,启用这个选项以后,Sulley 就 会在一个很广的范围内枚举可能的整数值。举个例子,如果我们传入的整数是一个无符号的 整数,把 full_range 设置成 True,这时候 Sulley 就会很智能的测试边界值(接近或者超过最 大值,或者接近最小值),无符号的最大值是 65535,Sulley 就会试着使用 65534, 65535, 65536 去进行测试。full_range 默认为 False,因为可枚举的时间可是很长的。看看下面的例子。

s_word(0x1234, endian=">", fuzzable=False) 
s_dword(0xDEADBEEF, format="ascii", signed=True)

第一个例子,我们设置了一个 2 字节大小的值 0x1234,并且将表示方式设置成大端,同时作为一个静态值。第二个例子,我们设置了一个 4 字节(双字)大小的值 0xDEADBEEF, 并且将它作为有符号的整数,以 ASCII 形式表现。

9.2.6 Blocks and Groups

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

下面就是一个使用块和组 fuzzing HTTP 的例子。

# 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)5 种请求方式,这样一来,一 次 body 内的变形就相当于产生 5 个不同的 body。

到目前为止,我们已经讲解完了 Sulley 的基础知识。当然 Sulley 不仅仅如此,还有数 据解码,校验和计算,长度自动处理等等。想深入学习的同学可以看 Pedram 写的 Fuzzing: Brute Force Vulnerability Discovery (Addison-Wesley, 2007),一本综合了 Sulley 和 fuzzing 相 关技术的好书。现在该开始对 WarFTPD 下手了。我们要先创建自己的 primitive 集合,然后

将它们传给负责构建测试的框架内。