Fuzzing及Sulley简介

一、模糊测试(Fuzzing)

1、泛化定义:

Fuzzing是一种软件漏洞检测技术,通过提供非预期的、随机的或者错误的数据作为输入来检测软件漏洞,在软件出现崩溃时监视异常结果来找出其存在的潜在漏洞。

2、类比理解

可以将模糊测试类比为如何闯进一幢房子。为了破门进入某人的家,假设采用纯白盒的方法,那么在实施破门之前应该能够得到对所有关于这个家的充分信息。这些信息可能要包括房屋设计图、各种锁的制造商列表、房屋建筑材料的详情,等等。尽管这种方法有独一无二的优点,但是也并非万无一失而没有短处。在这种方法下,执行破门的过程中,你要做的不是在实际执行时去检查房屋的设计而是要对房屋的设计执行静态分析。让我们打个比方,例如,事先研究表明起居室的侧面窗户代表了一个弱点,可以砸破这面窗户然后破门而入,如果是这样的话,那你肯定不希望到时候有一个拿着鸟枪的愤怒房主站在里面正在等着你。另一方面,如果采用一种纯的黑盒测试方法来完成破门的话,那么应该在黑夜的掩盖下逐步靠近这个房子,安静地尝试所有的门和窗户是否有漏洞,向房子内窥视以决定哪里可能是最好的突破口。最后,如果选择采用模糊测试来完成突破的话,便可以不必研究设计图更不用人工测试各种锁。要做的就是让找枪和破门而入的过程自动化–这就是强制性的漏洞发掘!

3、模糊器分类

  • 基于变异的模糊器:对已有数据应用变异技术以创建测试用例
  • 基于生成的模糊器:通过目标协议或文件格式的方法从头开始产生测试用例

4、模糊测试阶段

模糊测试方法的选择依赖不同的因素,可能有很大的变化。没有一种绝对正确的模糊测试方法。模糊测试方法的选择完全取决于目标应用程序、研究者的技能,以及需要测试的数据所采用的格式。然而,无论要对什么进行测试,也不论确定选择了哪种方法,模糊测试总要经历几个基本的阶段。

识别目标

在没有考虑清楚目标应用程序的情况下,不可能对模糊测试工具或技术作出选择。如果是在安全审核的过程中对内部开发的应用程序进行模糊测试,目标应用程序的选择应该小心谨慎。相反,如果是对第三方应用程序进行安全漏洞的发掘研究,这种选择就有一定的灵活性。选择了目标应用程序之后,还必须选择应用程序中具体的目标文件或库。如果确要选择目标文件或库,应该选择那些被多个应用程序共享的库,因为这些库的用户群体较大,出现安全漏洞的风险也相应较高。

识别输入

几乎所有可被人利用的漏洞都是因为应用程序接受了用户的输入并且在处理输入数据时没有首先清除非法数据或执行确认例程。枚举输入向量对模糊测试的成功至关重要。未能定位可能的输入源或预期的输入值对模糊测试将产生严重的限制。在查找输入向量同时应该运用水平思考。尽管有一些输入向量是很明显的,但是其它一些则难以捉摸。最后要说的是,任何从客户端发往目标应用程序的输入都应该被认为是输入向量。这些输入包括消息头、文件名、环境变量、注册键值等等。所有这些都应该被认为是输入向量,因此都应该是可能的模糊测试变量。

生成模糊测试数据

一旦识别出输入向量,模糊测试就必须被生成。如何使用预先确定的值、如何变异已有的数据或动态生成数据,这些决策将取决于目标应用程序及其数据格式。不管选择了哪种方法,这个过程中都应该引入自动化。

执行模糊测试数据

这一步与前一步并行进行,在这一步,模糊测试成为一个动词。执行过程可能包括发送数据包给目标应用程序、打开一个文件或发起一个目标进程。同样,这个过程中的自动化也是至关重要的。没有自动化,我们便无法执行真正的模糊测试。

监视异常

在模糊测试过程中,一个至关紧要但却被经常忽视的步骤是对故障或异常的监视过程。举一个例子,如果我们没有办法准确指出是哪一个数据包引起崩溃的话,那么向目标Web服务器发送10000个模糊测试数据包并最终导致服务器崩溃便是一次无用的努力。监视可以采用多种形式并且应该不依赖目标应用程序和所选择的模糊测试类型。

确定可利用性

一旦故障被识别,因审核的目标不同,还可能需要确定所发现的bug是否可被进一步利用。这典型地是一个人工过程,需要具备安全领域的专业知识。因此执行这一步的人可能不是最初执行模糊测试的人。

模糊测试阶段
识别目标
识别输入
生成模糊测试数据
执行模糊测试数据
监视异常
确定可利用性

不管采用什么类型的模糊测试,所有上述阶段都应该被考虑到,只有确定可利用性这一步有可能例外。各个阶段的顺序和侧重点可依据研究者的目标而改变。尽管模糊测试非常强大,但绝不意味着它对任何被测软件都将发现百分之百的错误。

5、模糊测试局限性

几种无法被模糊器发现的漏洞:

  • 访问控制缺陷
  • 设计逻辑不良
  • 后门
  • 内存破坏
  • 多阶段安全漏洞

6、模糊测试方法

  • 预先生成测试用例:测试用例的开发始于对一个专门规约的研究,其目的是为了理解所有被支持的数据结构和每种数据结构可接受的值范围。
  • 随机方法:随机方法只是简单地大量产生伪随机数据给目标软件
  • 协议变异人工测试:研究者本人就是模糊器。在加载了目标应用程序后,研究者仅仅通过输入不恰当的数据来试图让服务器崩溃或使其产生非预期的行为。
  • 变异或强制性测试:模糊器从一个有效的协议或数据格式样本开始,持续不断地打乱数据包或文件中的每一个字节、字、双字或字符串。
  • 自动协议生成测试:在这种方法中,需要进行前期的研究,首先要理解和解释协议规约或文件定义。然而,与前一种方法不同,这种方法并不创建硬编码的测试用例,而是创建一个描述协议规约如何工作的文法。为了达到这个目的,需要识别数据包或文件中的静态部分和动态部分,后者代表了可被模糊的变量。之后,模糊器动态解析这些模板,生成模糊测试数据,然后向被测目标发送模糊后产生的包或文件。这种方法的不足之处是需要耗费一定的时间来产生文法或数据格式的定义。

二、Sulley

Sulley模糊测试是一种基于Python的fuzzing框架,主要应用于网络协议方面的测试,由Pedram AMINI 和 Aaron Portnoy设计。

1、功能

  • 观察着网络通信并系统的维护着相关的记录。
  • 监视着目标应用的状态,并且能够使用多种方法将其恢复到一个好的状态。
  • 将所发现的错误进行检测、跟踪和分类。
  • 并行的进行模糊测试,这就极大的提高了测试速度。
  • 能够自动的确定是哪个唯一的测试用例序列触发了错误。
  • 不需要人工干预就可以自动化的完成上述工作以及更多的工作。

2、目录结构

  • archived_fuzzies:这是一个自由格式的目录,由模糊测试目标应用的名字进行组织,以存储已存档的模糊器以及从模糊测试会话中所生成的数据。
  • audits:针对活动的模糊测试会话所记录的PCAP、崩溃的二进制文件、代码覆盖以及分析图等应当被保存到这个目录中。一旦模糊测试执行完毕,应当将所记录的数据转移到archived_fuzzies目录中。
  • docs:该目录包含相关文档以及所生成的Epydoc API引用。
  • requests:该目录是Sulley请求库。每个目标应用都应当获得其自己的文件,该文件可以被用来存储多个请求。
  • http.py:表示不同的Web服务器模糊测试请求。
  • sulley:该目录是模糊器的框架。除非你想要扩展该框架,否则的话不要触及这些文件。
  • legos:包含用户所定义的复杂原始类型。
  • pgraph:该目录包含Python图形抽象库。在构建会话时使用。
  • utils:包含不同的帮助例程。
  • init_py:这里定义了在创建请求时所使用的不同的s别名。
  • blocks.py:这里定义了块以及块的帮助例程。
  • primitives.py:该文件定义了不同的模糊器原始类型,包括静态类型、随机类型、字符串类型以及整数类型等。
  • sessions.py:这个文件包含构建以及执行一个会话所需要的功能。
  • sex.py:包含Sulley的定制异常处理类。
  • unit_tests:该目录包含Sulley的单元测试套件。
  • utils:包含多种不同的单机版工具。
  • Crashbin_explorer.py:包含用于研究存储在序列化的崩溃bin文件中的结果的命令行工具。
  • pcap_cleaner.py:包含用于清除一个PCAP目录中与错误不相关的所有条目的命令行工具。
  • network_monitor.py:包含PedRPC驱动的网络监视代理
  • process_monitor.py:包含PedRPC驱动的、基于调试器的目标监视代理。
  • unit_test.py:Sulley的单元测试套件。
  • vmcontrol.py:包含PedRPC驱动的VMWare控制代理。

3、框架结构

  1. 数据表示:运行你的目标应用,并且当拦截包时处理某些接口。将协议分解为单独的请求,并使用Sulley将它们表示为块
  2. 会话:将所开发的请求进行连接以形成一个会话,并将其关联到不同的可用的Sulley监视代理(socket,调试器等等),然后开始执行模糊测试
  3. 事后工作:对生成的数据以及所监视的结果进行评审。重放单独的测试用例,找出其存在的潜在漏洞

3.1数据表示

   Sulley利用了基于块的方法来生成单独的请求,这些请求稍后将组合在一起以构成一个会话。一开始,使用一个新的名字来初始化你的请求:
   s_initialize("new request")
   现在你就可以向请求中添加原始类型、块以及嵌套的块了。每个原始类型都可以被单独的显示和变异。显示一个原始类型将导致以原始数据格式来返回其内容。对原始类型进行变异将导致转换其内部内容。当可模糊化的值被用完时,每个可变异的原始类型将接收一个默认的恢复值。
静态和随机的原始类型
s_static():
最简单的原始类型s_static()开始讨论,它将向请求中添加一个静态的、任意长度的未变异的值。为了便于使用,Sulley为它提供了许多不同的别名,例如s_dunno(),s_raw()以及s_unknown()都是s_static()的别名。

原始类型和块等原始都包含一个可选的名字关键字参数。指定一个名字就允许你在请求中通过request.names["name"]来直接的访问被命名的条目,而不用遍历块的结构以到达所需要的元素。

s_binary():
与前面的类型有些关联但并不等同的是s_binary()原始类型,它可以接收以多种格式来表示的二进制数据。

s_random():
由于大多数的Sulley的原始类型都是由模糊测试的启发式规则来驱动的,因此它只包含有限数量的变异。而其中的一个例外情形是s_random()原始类型,它可以被用来生成不同长度的随机数据。
这个原始类型包含两个强制使用的参数'min_length'和'max_length',它们分别指明了在每次循环迭代过程中所生成的随机数据的最大长度和最小长度。该原始类型同时也接受下面所列出的可选的关键字参数:
num_mutations(整数值,默认值是25):在恢复到默认值之前要进行的变异的数量。
fuzzable(布尔值,默认值是True):将对此原始类型的模糊测试设置为使能或者不使能。
name(字符串值,默认值是None):在所有Sulley对象中,指定一个名字就可以在整个请求中直接访问该原始类型。
num_mutations关键字参数指定了在该原始类型被认为用完之前,应当被重新显示多少次。为了用随机数据填充一个静态大小的字段,可以将'min_length'和'max_length'的值设置为相同值。
整数类型
二进制协议的ASCII协议同样都包含有许多不同大小的整数值,例如HTTP中的内容长度字段。同大多数模糊测试框架一样,Sulley中的一部分负责表示以下这些类型:
  • 一个字节:s_byte(),s_char()
  • 两个字节:s_word(),s_short()
  • 四个字节:s_dword(),s_long(),s_int()
  • 八个字节:s_qword(),s_double()

       每个整数类型至少接受一个单一的参数,即默认的整数值。另外,下面的可选的关键字参数也可以被指定:
    
  • endian(字符型,默认值为’<’):位字段的存放顺序。将低位字节先存放在低地址处的顺序指定为<,将高位字节先存放在低地址处的顺序指定为>。

  • format(字符串型,默认值是”binary”):将输出格式指定为”binary”或者”ascii”,用以控制整数原始类型的显示格式。例如,值100以ASCII格式显示为”100”,而以二进制格式则显示为”\x64”。
  • signed(布尔型,默认值是False):将大小设置为有符号的或者无符号的,只有当输出格式设定为”ascii”时才可应用该类型。
  • full_range(布尔型,默认值是False):如果其值为真,那么该原始类型将变异所有可能的值(稍后将对该类型进行更加详细的介绍)
  • fuzzable(布尔型,默认值是True):将对该原始类型的模糊测试设置为使能或者不使能。
  • name(字符串型,默认值是None):在所有的Sulley对象中,指定一个名字就可以在整个请求中直接访问该原始类型。
  • full_range修饰符在上述这些类型中是特别有趣的一个。考虑你想要对一个DWORD值进行模糊测试,总共有4294967295个可能的值。以每秒钟执行10个测试用例的速度来计算,它将会耗费13年的时间完成对这个单一的原始类型的模糊测试。为了减少巨大的输入空间,Sulley在默认情况下只尝试那些”智能”值。这包括对0周围的10个边界测试用例加上或减去适当的值,最大的整数值(MAX_VAL),MAX_VAL/2,MAX_VAL/3,MAX_VAL/4,MAX_VAL/8,MAX_VAL/16,MAX_VAL/32。缩减后的输入空间只包含141个测试用例,而执行这些测试用例只需要几秒钟的时间。
字符串和分隔符

随处都可以发现字符串。当进行模糊测试时,你必定会遇到许多的字符串构件,例如邮件地址、主机名、用户名、密码等等都是字符串的例子。Sulley提供了原始类型s_string()来表示这些字段。该原始类型含有一个单一的强制性的参数,这个参数为该类型指明了默认的有效值。也可以指定下面所列出的额外的关键字参数:

  • size(整数值,默认值是-1):该字符串的静态大小。为了指定动态大小,将该值设置为-1。
  • padding(字符型,默认值是’\x00’):如果一个显式大小被指定,并且所生成的字符串要小于该大小,那么就使用该值将字段变为等于大小值。
  • encoding(字符串型,默认值是”ascii”):编码以为字符串所用。有效选项包括Python str.encode()例程可以接受的任何内容。对于微软的Unicode字符串而言,将其指定为”utf_16_le”。
  • fuzzable(布尔值,默认值是True):将对该原始类型的模糊测试设置为使能或者不使能。
  • name(字符串型,默认值是None):在所有的Sulley对象中,指定一个名字就可以在整个请求中直接访问该原始类型。

通过使用分隔符,字符串经常被解析为子字段。例如,空格字符在HTTP请求GET/index.html HTTP/1.0中就被用做一个分隔符。在这个请求当中,前面的斜线(/)和点(.)字符同样也是分隔符。当在Sulley中定义一个协议时,要确保使用s_delim()原始类型来表示分隔符。同其它的原始类型一样,第一个参数是强制使用的,并且被用来指定默认值。同样,与其它原始类型一样,s_delim()也接受可选的’fuzzable’和’name’关键字参数。针对分隔符的变异包括重复、替换和删除。

块block

在掌握了原始类型的相关知识后,下面让我们来看一下如何将它们进行组织,并且在块的内部进行嵌套。通过使用s_block_start()来定义并打开一个新块,使用s_block_end()来关闭该块。每个块必须要给定一个名字,并将其作为s_block_start()的第一个参数。该例程同时也接受下面列出的可选的关键字参数:

  • group(字符串型,默认值是None):该块所关联的群组的名字(稍后将对此进行更加详细的介绍)。
  • encoder(函数指针,默认值是None):指向一个函数的指针,该函数在返回所显示的数据之前显示该数据。
  • dep(字符串型,默认值是None):可选的原始类型,该块依赖于该类型的特定值。
  • dep_value(混合类型,默认值是None):为了能够显示块,dep字段必须要包含的值。
  • dep_value(混合类型的列表,默认值是[]):为了能够显示块,dep字段可能将包含的值。
  • dep_compare(字符串型,默认值是”==”):应用于依赖的比较方法。有效的选项包括==,!=,>,>=,<以及<=。
    分组、编码和依赖是在其它的大多数框架中所没有的功能强大的特性,因此值得对它们进行深入的研究。
群组group

分组允许你将一个块关联到一个群组原始类型,以指定该块应当为群组内部的每个值来循环遍历所有可能的变异。例如,群组原始类型对于表示带有相似参数结构的有效操作码或动词的一个列表是非常有用的。原始类型s_group()定义了一个群组,并且接受两个强制使用的参数。第一个参数指定了群组的名字,第二个参数指定了将要遍历的、可能的原始值的列表。作为一个简单的例子,考这个完整的Sulley请求,该请求被设计为对一个Web服务器进行模糊测试。

from sulley import *
s_initialize("HTTP BASIC")
s_group("verbs", values=["GET", "HEAD", "POST", "TRACE"])
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")
    s_static("\r\n\r\n")
s_block_end("body")

在该脚本的开始,首先导入了Sulley的所有构件。接下来,初始化了一个新的请求,并且将其命名为HTTP BASIC。在后面可以通过引用这个名字来直接访问该请求。然后定义了一个群组,其名字为verbs,所包含的可能的字符串值为GET,HEAD,POST以及TRACE。接着启动了一个新块,其名字为body,并且通过可选的group关键字参数将其关联到前面所定义的群组原始类型。注意s_block_start()将一直返回True,这就允许你可以使用一个简单的if语句来识别出其所包含的原始类型。同时注意到s_block_end()的name参数也是可选的。这些框架的设计决定纯粹是出于美观的目的而做出的。接着,在body块的内部定义了一系列基本的分隔符和字符串原始类型,然后将该块关闭。当将这个被定义的请求加载到一个Sulley会话中时,模糊器将为该块生成并传递所有可能的值,每次为群组中定义的每个动词生成并传递值。

编码器

编码器是一个简单而又功能强大的块修饰符。可以指定一个函数并将其关联到一个块,以在通过网络返回和传输之前,来修改所显示的该块的内容。Sulley编码器包含一个单一的参数,即将要编码的数据,并且返回被编码的数据。现在可以将这个定义的编码器关联到包含可模糊化原始类型的一个块,并且允许模糊器的开发者继续工作就好像不存在这个小的障碍一样。

依赖

依赖允许你将一个条件应用于一个完整块的显示中。其实现过程如下,首先使用可选的dep关键字参数将一个块与它将要依赖的原始类型相连接。当Sulley准备显示依赖块时,它将会检查所连接的原始类型的值并相应的采取行为。可以使用dep_value关键字参数来指定一个依赖值。另外,可以使用dep_values关键字参数来指定依赖值的一个列表。最后,可以通过使用dep_compare关键字参数来修改实际的条件比较。例如,考虑这样一个情形,取决于一个整数的值而期望得到不同的数据。可以采用多种方法将块依赖串接起来,以构成功能更加强大(但也更为复杂)的组合。

块帮助函数

为了有效的利用Sulley,你必须要熟悉的关于数据生成的一个重要方面就是块帮助函数。这些函数包括大小计算函数(sizers)、校验和函数(checksums)和重复函数(repeaters)等。

  • 大小计算函数s_sizer():使用块的名字来度量第一个参数的大小
  • 校验和s_checksum():使用块的名字来计算第一个参数的校验和
  • 重复函数s_repeat():对一个块重复可变数量的次数,包含三个强制使用的参数:将要被重复的块的名字,重复的最小次数以及重复的最大次数
Legos

Sulley利用legos来表示用户定义的构件,如邮件地址、主机名以及在微软的RPC ,XDR,ASN.1以及其它标准中所使用的协议原始类型.

3.2会话

一旦你已经定义了许多的请求,那么就可以将它们在一个会话中连接起来。同其它的模糊测试框架相比,Sulley所具有的一个主要的优越性在于,它具备对协议进行深层次模糊测试的能力。这一功能是通过在一个图中将请求连接在一起来实现的。当开始进行模糊测试时,Sulley将从根节点开始遍历该图的结构,并且同时对每一个构件进行模糊测试。
当实例化一个会话时,可以指定如下所示的可选的关键字参数:

  • sessio_filename(字符串型,默认值是None):序列化持久数据的文件名。指定一个文件名将允许你停止并重新启动模糊器。
  • skip(整数值,默认值是0):将要被略过的测试用例数。
  • sleep_time(浮点数,默认值是1.0):在传输测试用例之间的休眠时间。
  • log_level(整数值,默认值是2):设置日志级别,一个更大的数字表示将包含更多的日志消息。
  • proto(字符串型,默认值是”tcp”):通信协议。
  • timeout(浮点数,默认值是5.0):在超时之前等待send()或recv()返回的秒数。
    Sulley所具备的另外一个高级特性是,它可以在协议图结构中所定义的每条边上记录回调函数。这就允许我们在节点传输之间记录一个将调用的函数,以实现诸如挑战响应系统这样的功能。回调方法必须要采用的原型加以定义。
    在上述原型定义中,node表示将要被发送的节点,edge是针对node的模糊测试路径的最后一条边,last_recv包含从最后一次socket传输所返回的数据,sock是当前活动的socket。回调函数在下面的情形中也是有用的,例如下一个包的大小被指定在第一个包中。作为另外一个例子,如果你需要填充目标应用的动态IP地址,那么就记录一个从sock.getpeername()[0]拦截IP的回调函数。也通过session.connect()方法的可选的关键字参数callback来记录边的回调函数。
目标和代理

下一步就是定义目标,将它们同代理相连接,并且将目标添加到会话中。在下面的例子中,我们实例化了一个运行在VMWare虚拟机中的新目标,并且将其与三个代理相连接。

target = sessions.target("10.0.0.1", 5168)

target.netmon = pedrpc.client("10.0.0.1",26001)
target.procmon = pedrpc.client("10.0.0.1", 26002)
target.vmcontrol = pedrpc.client("127.0.0.1",26003)

target.procmon_options = \
{ 
    "proc_name" : "SpntSvc.exe",
    "stop_commands" : ['net stop "trend serverprotect"'],
    "start_commands" : ['net start "trend serverprotect"'], 
}
sess.add_target(target)
sess.fuzz()

实例化的目标被绑定在IP地址为10.0.0.1的主机上的TCP端口5168。在目标系统中运行着一个网络监视代理,该代理默认在端口26001进行监听。网络监视器将所有的socket通信记录到单独的PCAP文件中,这些文件通过测试用例号来进行标识。进程监视代理也运行在目标系统中,默认在端口26002处进行监听。该代理接受额外的参数以指明将要关联到的进程名,停止目标进程执行的命令以及启动目标进程执行的命令。最后,VMWare控制代理是运行在本地系统中的,默认在端口26003处进行监听。将目标添加到会话中,然后就可以开始进行模糊测试了。Sulley可以对多种目标进行模糊测试,对每个目标使用连接代理的唯一集合进行测试。这样,就可以通过将总的测试空间划分到不同的目标来节省测试时间。
下面让我们来详细的讨论一下每个单独代理的功能。
代理:网络监视器(network_monitor.py)
网络监视器代理负责监视网络通信,并且将它们作为日志记录到磁盘上的PCAP文件中。该代理被硬编码并被绑定到TCP端口26001,并且通过PedRPC定制的二进制协议接受来自于Sulley会话的连接。在将一个测试用例传递到目标之前,Sulley将关联到该代理,并且要求它开始记录网络通信。一旦测试用例被成功的传递,Sulley将再次关联到此代理,并且要求它将所记录的通信导出到磁盘上的一个PCAP文件中。用测试用例号来命名PCAP文件,以便于检索。该代理不需要被部署到与目标软件相同的系统中。然而,它必须可见的发送和接收网络通信。该代理接受命令行参数。

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] {C2E1878A-A59A-4023-A4E5-77EE1ECCEFAE} 192.168.235.1
[1] {11E03B98-5931-490C-8617-EC82C36121B0}
[2] {E23A46F5-E2F9-46B7-B407-6868DF329C54} 192.168.75.1
[3] {554DA01B-6938-4FC5-A3C1-26CA49E8DF3D} 192.168.0.100
[4] {E0887D7D-3247-4896-ABAF-C0CDE3AC9C86}

代理:进程监视器(process_monitor.py)
进程监视器代理负责检测在模糊测试的目标进程中可能发生的错误。该代理被硬编码并被绑定到TCP端口26002,并且通过PedRPC定制的二进制协议接受来自于Sulley会话的连接。在成功的将每个单独的测试用例传递到目标之后,Sulley将关联到该代理以确定是否有一个错误被触发。如果有错误被触发,那么有关错误本质的高层信息将被传递回Sulley会话,以在Web服务器内部显示(稍后将对此进行更加详细的讨论)。所触发的错误也被记录到一个序列化的”崩溃二进制文件”中,以用于后续的分析。我们在后面将对该功能进行更加详细的研究。该代理接受命令行参数。

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

代理:VMWare控制(vmcontrol.py)
VMWare控制代理被硬编码并被绑定到TCP端口26003,并且通过PedRPC定制的二进制协议接受来自于Sulley会话的连接。该代理提供了一个API以同一个虚拟机映像进行通信,包括具备启动、停止、挂起或者重设映像的能力,同时也可以获取、删除和恢复快照。如果一个错误已经被发现或者目标不能被到达,Sulley可以连接到该代理并且将虚拟机恢复到一个已知的好的状态。测试序列处理工具将主要依赖该代理来实现其功能,即识别触发任何给定的复杂错误的准确的测试用例序列。该代理接受命令行参数。
Web监视接口
Sulley会话类含有一个内嵌的最小化Web服务器,该服务器被硬编码并被绑定到端口26000。一旦会话类的fuzz()方法被调用,那么该Web服务器线程将解锁,并且模糊器的执行进度包括中间结果都可以被显示出来。

可以通过点击适当的按钮来暂停和恢复模糊器的执行。每个所检测到错误的概要信息将被作为一个列表而显示,该列表的第一列是导致错误发生的测试用例的编号。点击测试用例编号将会加载错误发生时的详细崩溃信息。当然,该信息在崩溃二进制文件中也是可用的,并且可以通过编程来获取。一旦会话被执行完毕,就进入了事后验证阶段并开始分析所生成的结果。

3.3事后工作

一旦一个Sulley模糊测试会话过程执行完毕,下面就开始对结果进行评审并且进入到事后验证阶段。会话的内嵌Web服务器将会向你提供有关潜在的未发现问题的早期提示信息,但实际上这时你将会对结果进行分隔。在这个处理过程中,有一些可用的工具可以为你提供帮助。第一个是crash-bin_explorer.py工具,该工具接受命令行参数。
USAGE: crashbin_explorer.py <xxx.crashbin>
[-t|--test #] dump the crash synopsis for a specific test case number
[-g|--graph name] generate a graph of all crash paths, save to 'name'.udg

例如,我们可以使用该工具来查看检测到错误的每个位置,并且进一步列出在该地址触发一个错误的单个测试用例编号。

D:\Program Files (x86)\Sulley\sulley-master1>python crashbin_explorer.py D:\easyftpserver.crash
[2] easyftp.exe:0042d063 rep movsd from thread 6008 caused access violation
        4559, 5682,

在上面所列出的错误点中,没有一个错误可能会作为一个明显的可利用问题而显得突出。我们可以通过使用-t命令行开关来指定一个测试用例编号,从而深入到一个单个错误的特定方面中。看一下编号为4559测试用例:

D:\Program Files (x86)\Sulley\sulley-master1>python crashbin_explorer.py D:\easyftpserver.crash -t 4559
easyftp.exe:0042d063 rep movsd from thread 6008 caused access violation
when attempting to read from 0x021af000

CONTEXT DUMP
  EIP: 0042d063 rep movsd
  EAX: 040b70af (  67858607) -> N/A
  EBX: 01f0fefa (  32571130) -> N/A
  ECX: 007c202c (   8134700) -> N/A
  EDX: 00000002 (         2) -> N/A
  EDI: 045ebe74 (  73318004) ->  (heap)
  ESI: 021aeffd (  35319805) -> N/A
  EBP: 0297dc38 (  43506744) -> X4E,@^q0lDCWGD,@^*[@0qt,G:~@hl0*lLjIhlkXh`h`h+|GLjI (stack)
  ESP: 0297dc30 (  43506736) -> X4E,@^q0lDCWGD,@^*[@0qt,G:~@hl0*lLjIhlkXh`h`h+ (stack)
  +00: 0297dc94 (  43506836) -> ,@^ (stack)
  +04: 0297dcb8 (  43506872) -> qt,G:~@hl0*lLjIhlkXh`h`h+|GLjIGdX\t\?[w| (stack)
  +08: 0297dc58 (  43506776) -> lDCWGD,@^*[@0qt,G:~@hl0*lLjIhlkXh`h`h+|GLjIG (stack)
  +0c: 00453415 (   4535317) -> N/A
  +10: 045e402c (  73285676) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%DTM %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% (heap)
  +14: 021a71b5 (  35287477) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%DTM %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% (heap)

disasm around:
        0x0042d044 cmp edi,esi
        0x0042d046 jna 0x42d050
        0x0042d048 cmp edi,eax
        0x0042d04a jc 0x42d1c8
        0x0042d050 test edi,0x3
        0x0042d056 jnz 0x42d06c
        0x0042d058 shr ecx,0x2
        0x0042d05b and edx,0x3
        0x0042d05e cmp ecx,0x8
        0x0042d061 jc 0x42d08c
        0x0042d063 rep movsd
        0x0042d065 jmp [edx*4+0x42d178]
        0x0042d06c mov eax,edi
        0x0042d06e mov edx,0x3
        0x0042d073 sub ecx,0x4
        0x0042d076 jc 0x42d084
        0x0042d078 and eax,0x3
        0x0042d07b add ecx,eax
        0x0042d07d jmp [eax*4+0x42d090]
        0x0042d084 jmp [ecx*4+0x42d188]
        0x0042d08b nop

stack unwind:
        easyftp.exe:00453415
        easyftp.exe:0044bb6c
        easyftp.exe:0044bafd
        easyftp.exe:00405b17

SEH unwind:
        0297dcbc -> easyftp.exe:00475743
        0297de74 -> easyftp.exe:00472c08
        0297de9c -> easyftp.exe:00473068
        0297fefc -> easyftp.exe:00472be0
        0297ff70 -> easyftp.exe:004730a1
        0297ffcc -> easyftp.exe:00431ba0
        0297ffe4 -> ntdll.dll:77e56750
        ffffffff -> ntdll.dll:77e62ebc

以上崩溃信息根据PyDbg崩溃信息格式分析处理,发现可能存在的潜在漏洞。
我们可能想要执行的最后一个步骤就是删除所有的不包含错误相关信息的PCAP文件。可以编写pcap_cleaner.py构件来很好的完成这个操作。该构件将打开特定的崩溃二进制文件,在触发错误的测试用例编号列表中进行读取,并且从特定的目录中删除所有其它的PCAP文件。

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值