简介

Boofuzz是Sulley模糊测试框架的一个分支和继承者。除了修复了许多错误,boofuzz还提升了可扩展性。主要特性有以下几点:

  • 轻松快速的数据生成方式;
  • 仪表板支持AKA故障检测;
  • 失败后的目标重置;
  • 记录测试数据;
  • 在线文档;
  • 支持任意通信媒介;
  • 内置支持串行模糊测试、以太网、IP层和UDP广播;
  • 更好地记录测试数据——统一、彻底、清晰;
  • 测试结果通过CSV导出;
  • 可扩展的仪器/故障检测;
  • 方便的安装体验;

Install

ubuntu;基于python2.x

sudo apt-get install python-pip
git clone https://github.com/jtpereyda/boofuzz.git
cd boofuzz
sudo pip install .

windows:

git clone https://github.com/jtpereyda/boofuzz.git
pip install .

模块分析

Session

pre_send() 发送数据后开始遍历Pgraph结构,并沿途fuzz每个组件。这个例子以'helo' requet开头,一旦完成,将开始fuzz “maiL from” request。它通过在每个测试用例前面加上有效的“helo” request来实现。 接下来,将继续fuzz 'rcpt to’ request。同样,这是通过在每个测试用例前面加上有效的“helo”和“mail from” request来实现的。等到该过程一直到”data” request完成后,又转回到从”ehlo”开始。通过构建的Pgraph将协议分解为单个请求,并fuzz所有可能路径,使其fuzz能力十分的强大。

每个节点都连接起来,组成一幅有状态的图,我们可以在图里的每个节点进行操作,同时也可以定义一些callback回调函数,以实现诸如质询响应系统之类的功能,回调方法必须遵循以下原型:

def callback(target, fuzz_data_logger, session, node, edge, *args, **kwargs)
'fuzz_data_logger'记录测试检查和通过/失败
'session'是一个指向会话实例的指针,它对于阻塞例如session.last_recv的数据很有用
'node'是要发送的节点
'edge'是当前fuzz到node的最后一条路径

我们每次创建测试脚本的时候都会使用Session,session.connect() 用来声明连接,session.fuzz() 发送负载,以及定义会话回调和选择的协议进行交互,例如

session = Session(   
        target=Target(
            connection=SocketConnection("127.0.0.1", 21, proto='tcp')))

  session.connect(s_get("xxx"))

  session.fuzz()

Static Protocol Definition

Request是信息,Blocks是消息中的块,而Primitives是构成块/请求的元素(字节,字符串,数字,校验和等)。

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

Group允许你连接一个块到指定的group原语,和一个组关联的block必须为每一个组中的值循环穷尽该block的所有空间,例如,在表示一个有效的opcode列表或一些有相同参数的行为时,组原语是非常有用的,s_group定义一个组,并接受两个必须的参数,第一个参数指定组名称,第二个参数指定一个需要迭代的原始值列表。

例如下面官方给出的http.py中的代码

s_initialize("HTTP VERBS")
s_group("verbs", values=["GET", "HEAD", "POST", "TRACE", "PUT", "DELETE"]) 
if s_block_start("body", group="verbs"): 
    s_delim(" ") 
    s_delim("/") 
    s_string("index.html ") 
    s_delim(" ") 
    s_string("HTTP") 
    s_delim("/") 
    s_string("1") 
    s_delim(".") 
    s_string("1") 
 s_block_end()

模块常用语法

s_initialize'grammar')#初始化块请求并命名
s_static"HELLO\r\n")#始终发送此信息
s_static"PROCESS")#在HELLO\r\n之后立即发送
s_delim""# 使用s_delim()原语代替分割符
s_string"AAAA")# 这是我们的fuzz字符串
s_static"\r\n")# 告诉服务器“done

Connections

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()

首先是定义目标,加入会话

下面的netmon(网络监控代理) 、procmon(进程监控代理)、vmcontrol(VMware控制代理)为3个agent的子模块,用来监测程序

  • netmon子模块:netmon子模块主要负责捕捉网络的双向流量,并保存。 在向target发送数据之前,agent向target发送请求并记录流量,数据传送成功后,该代理子模块将记录的流量存入磁盘。
  • procmon子模块:procmon子模块主要负责检测fuzz过程中发生的故障。 在向target发送数据之后,boofuzz联系该代理以确定是否触发了故障,如果产生故障,关于故障性质的hgih level信息将被传送回session。错误信息会储存在名为"crash bin"的文件中,我们也可以在web监控服务中看到发生crash时加载详细的crash信息。
  • vmcontrol子模块:vmcontrol子模块主要用来控制虚拟机。一种常见的用法是把Target目标放在虚拟机中,使用这个子模块来控制虚拟机的启动、关闭、创建快照等,最重要的功能是能在目标出现崩溃的时候恢复主机的状态。

下面简单介绍一下请求样例:

session.connect(s_get("user"))
    session.connect(s_get("user"), s_get("pass"))
    session.connect(s_get("pass"), s_get("stor"))
    session.connect(s_get("pass"), s_get("retr"))

连接后,我们首先发送用户名请求
在发送用户名后,我们发送密码
只有在发送密码后我们才能发送stor或retr请求

Fuzzing Vulnserver

vulnserver

Vulnserver是一个多线程的基于Windows的TCP服务器,它侦听端口9999上的客户端连接(默认情况下),并允许用户运行许多不同的命令,这些命令容易受到各种类型的可利用缓冲区溢出的攻击。它在github的地址为https://github.com/stephenbradshaw/vulnserver

我们启动vulnserver

我们使用nc命令,连接服务端9999端口即可。发出HELP命令(区分大小写),查看程序有哪些功能

Fuzzing

from boofuzz import *

def main():
    port = 9999
    host = '10.211.55.17'
    protocol = 'tcp'

    session = Session(
            target=Target(
                connection = SocketConnection(host, port, proto=protocol),
            ),
    )

    s_initialize("trun")
    s_string("TRUN", fuzzable=False)
    s_delim(" ", fuzzable=False)
    s_string("FUZZ")
    s_static("\r\n")

    session.connect(s_get("trun"))
    session.fuzz()

if __name__ == "__main__":
    main()

首先,我们先设置请求,然后定义命令的名称,空格分隔符和参数,最后发送请求,开始fuzz

在fuzz的时候我们可以打开http://127.0.0.1:26000 上的Web界面中观察fuzz测试进度。这是一个boofuzz内部Web服务器,向我们显示fuzz过程完整性和导致崩溃的输入文件。

我们可以看到程序已经崩溃,但是fuzz的脚本依然在运行,而且无法精确定位漏洞

这时候我们增加一个回调函数

session.connect(s_get("trun"), callback=get_banner)
def get_banner(target, my_logger, session, *args, **kwargs):
    banner_template = "Welcome to Vulnerable Server! Enter HELP for help."
    try:
        banner = target.recv(10000)
    except:
        print "Unable to connect. Target is down. Exiting."
        exit(1)

    my_logger.log_check('Receiving banner..')
    if banner_template in banner:
        my_logger.log_pass('banner received')
    else:
        my_logger.log_fail('No banner received')
        print "No banner received, exiting.."
        exit(1)

banner在每次fuzz接收后尝试接收字符串,接收不到报异常"Unable to connect. Target is down. Exiting.",如果接收到的字符能够与正常交互的字符串匹配上,我们记录下然后返回fuzz继续测试,如果不匹配程序结束

我们可以看出,在程序崩溃后,fuzz停止,接下来我们尝试记录程序崩溃,并找到原因

我们先增加日志记录,首先我们创建一个csv文件,然后创建一个my_logger对象,调用FuzzloggerCsv()函数,Fuzz_loggers记录测试数据和结果

csv_log = open('fuzz_results.csv', 'wb')
my_logger = [FuzzLoggerCsv(file_handle=csv_log)]
fuzz_loggers=my_logger,

我们首先在端口26002上侦听本地主机的进程,监视应用程序,然后设置选项,我们把程序和process_monitor.py放在同一目录下

需要注意的是process_monitor.py仅限于在windows使用(为Unix提供process_monitor_unix.py),而且需要安装pydasm和pydbg

procmon=pedrpc.Client(host, 26002),
        procmon_options = {
            "proc_name" : "vulnserver.exe",
            "stop_commands" : ['wmic process where (name="vulnserver") delete'],
            "start_commands" : ['vulnserver.exe'],
        }

我们运行起来process_monitor.py和fuzz脚本,我们发现EIP被41414141覆盖,并发生崩溃

我们查看一下fuzz_result.csv文件,如果我们使用sulley我们需要找到存储流量的PCAP文件 ,并定位payload,而我们使用boofuzz直接查看csv文件就可以

我们可以清晰的看到payload,我们可以用于复现和利用

下面是完整的fuzz代码:

from boofuzz import *
from sys import exit

def get_banner(target, my_logger, session, *args, **kwargs):
    banner_template = "Welcome to Vulnerable Server! Enter HELP for help."
    try:
        banner = target.recv(10000)
    except:
        print "Unable to connect. Target is down. Exiting."
        exit(1)

    my_logger.log_check('Receiving banner..')
    if banner_template in banner:
        my_logger.log_pass('banner received')
    else:
        my_logger.log_fail('No banner received')
        print "No banner received, exiting.."
        exit(1)

def main():
    port = 9999
    host = '127.0.0.1'
    protocol = 'tcp'

    s_initialize("Vulnserver")
    s_group("verbs", values=["TRUN", "GMON", "KSTET"])

    if s_block_start("test", group="verbs"):
        s_delim(" ")
        s_string("AAA")
        s_string("\r\n")

    s_block_end("test")

    csv_log = open('fuzz_results.csv', 'wb') 
    my_logger = [FuzzLoggerCsv(file_handle=csv_log)]   
    session = Session(
            target=Target(
                connection = SocketConnection(host, port, proto=protocol),
                procmon=pedrpc.Client(host, 26002),
                procmon_options = {
                        "proc_name" : "vulnserver.exe",
                        "stop_commands" : ['wmic process where (name="vulnserver") delete'],
                        "start_commands" : ['vulnserver.exe'],
                }
            ),
            fuzz_loggers=my_logger, 
            crash_threshold_element= 1,# Crash how much times until stop
    )

    session.connect(s_get("Vulnserver"), callback=get_banner)
    session.fuzz()


if __name__ == "__main__":
    main()

最后

本文主要是利用boofuzz发现Vulnserver的漏洞,至于利用部分可以参考http://www.thegreycorner.com/p/vulnserver.html

参考文章

https://blog.csdn.net/u012397189/article/details/79758931

https://xz.aliyun.com/t/2575

https://zeroaptitude.com/zerodetail/fuzzing-with-boofuzz/

<http://yunpiao.pub/2017/05/08/%E5%AE%89%E5%85%A8/%E4%BD%BF%E7%94%A8sulley%20%E8%BF%9B%E8%A1%8Cfuzzing/

点击收藏 | 1 关注 | 1
登录 后跟帖