freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

使用boofuzz进行漏洞挖掘(二)
2019-04-12 10:43:16

boofuzz采用python开发的一款fuzz工具,对协议fuzz有着良好的支持。对二次开发和插件的编写都有非常好的API支持


分析ftp-simple.py代码

        根据上篇文章中我们使用了boofuzz进行了第一次的漏洞挖掘显然我们就只是运行了一下并没有做任何事至此我们还需要深入理解知其所以然。

        先一贴下代码:

#!/usr/bin/env python
# Designed for use with boofuzz v0.0.8
from boofuzz import *


def main():
    """
    This example is a very simple FTP fuzzer. It uses no process monitory
    (procmon) and assumes that the FTP server is already running.
    """
    session = Session(
        target=Target(
            connection=SocketConnection("127.0.0.1", 21, proto='tcp')))

    s_initialize("user")
    s_string("USER")
    s_delim(" ")
    s_string("anonymous")
    s_static("\r\n")

    s_initialize("pass")
    s_string("PASS")
    s_delim(" ")
    s_string("james")
    s_static("\r\n")

    s_initialize("stor")
    s_string("STOR")
    s_delim(" ")
    s_string("AAAA")
    s_static("\r\n")

    s_initialize("retr")
    s_string("RETR")
    s_delim(" ")
    s_string("AAAA")
    s_static("\r\n")

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

    session.fuzz()


if __name__ == "__main__":
    main()

        如果你之前使用Sulley的话对这些语法你肯定不会陌生作者也在github上给出了说明:Boofuzz is a fork of and the successor to the venerable Sulley fuzzing framework. Besides numerous bug fixes, boofuzz aims for extensibility. The goal: fuzz everything.

        整个代码中最关键的就是s_*系列函数。 我们一步一步深入到内部结构中去看。

        我们从s_initialize开始:

def s_initialize(name):

    """

    Initialize a new block request. All blocks / primitives generated after this call apply to the named request.

    Use s_switch() to jump between factories.

    :type  name: str

    :param name: Name of request

    """

    if name in blocks.REQUESTS:

        blocks.REQUESTS = {}

        blocks.CURRENT = None

        #raise sex.SullyRuntimeError("blocks.REQUESTS ALREADY EXISTS: %s" % name)

    blocks.REQUESTS[name] = Request(name)

    blocks.CURRENT = blocks.REQUESTS[name]

初始化一个请求的块,并且根据参数name来进行块的命名和之后的使用,并且填充blocks,blocks.REQUESTS 是一个类,有这些元素

self._name = name

     self.label = name  # node label for graph rendering.

     self.stack = []  # the request stack.

     self.block_stack = []  # list of open blocks, -1 is last open block.

     self.closed_blocks = {}  # dictionary of closed blocks.

     # dictionary of list of sizers / checksums that were unable to complete rendering:

     self.callbacks = collections.defaultdict(list)

     self.names = {}  # dictionary of directly accessible primitives.

     self._rendered = ""  # rendered block structure.

     self._mutant_index = 0  # current mutation index.

     self._element_mutant_index = None  # index of current mutant element within self.stack

     self.mutant = None  # current primitive being mutated.

当填充好这些结构之后,我们需要设置那些元素进行变异,这就需要了解s_string、s_delim、s_static等函数进行相关变异功能,这其实也是整个boofuzz的核心功能,我们来看下他们的一些实现,代码在primitives目录中

2019-04-12 10-07-35屏幕截图.png这些都是相关的一些数据操作、变异的包

2019-04-12 10-10-39屏幕截图.png这是s_string的部分内容,其实里面都是一些定义好的数据,当然我们也可以为他扩充一些算法和变异数据来满足我们不同的FUZZ需求,s_delim则是对一些符号进行变异,我们这里的实现是s_delim(" "),我们来看下对于空字符是怎么处理的2019-04-12 10-15-11屏幕截图.png如果value是“ ”, 就会加入\t 这个变异数据,其原因是boofuzz作者认为空格和\t(制表符)有相似的功能吧?

对于s_static,则boofuzz认为他是不需要进行变异的,所以在static.py中没有任何变异实现。

对数据处理好后,就要开始进行数据的发送、fuzz等处理了。

首先我们来解读下session.connect

代码在sessions.py中的Session类中的connect方法

 

def connect(self, src, dst=None, callback=None):

        """

        Create a connection between the two requests (nodes) and register an optional callback to process in between

        transmissions of the source and destination request. Leverage this functionality to handle situations such as

        challenge response systems. The session class maintains a top level node that all initial requests must be

        connected to. Example::

            sess = sessions.session()

            sess.connect(sess.root, s_get("HTTP"))

        If given only a single parameter, sess.connect() will default to attaching the supplied node to the root node.

        This is a convenient alias and is identical to the second line from the above example::

            sess.connect(s_get("HTTP"))

        If you register callback method, it must follow this prototype::

            def callback(target, fuzz_data_logger, session, node, edge, *args, **kwargs)

        Where node is the node about to be sent, edge is the last edge along the current fuzz path to "node", session

        is a pointer to the session instance which is useful for snagging data such as session.last_recv which contains

        the data returned from the last socket transmission and sock is the live socket. A callback is also useful in

        situations where, for example, the size of the next packet is specified in the first packet. As another

        example, if you need to fill in the dynamic IP address of the target register a callback that snags the IP

        from sock.getpeername()[0].

        Args:

            src (str or Request (pgrah.Node)): Source request name or request node

            dst (str or Request (pgrah.Node), optional): Destination request name or request node

            callback (def, optional): Callback function to pass received data to between node xmits. Default None.

        Returns:

            pgraph.Edge: The edge between the src and dst.

        """

        # if only a source was provided, then make it the destination and set the source to the root node.

        if dst is None:

            dst = src

            src = self.root

        # if source or destination is a name, resolve the actual node.

        if type(src) is str:

            src = self.find_node("name", src)

        if type(dst) is str:

            dst = self.find_node("name", dst)

        # if source or destination is not in the graph, add it.

        if src != self.root and not self.find_node("name", src.name):

            self.add_node(src)

        if self.find_node("name", dst.name) is None:

            self.add_node(dst)

        # create an edge between the two nodes and add it to the graph.

        edge = Connection(src.id, dst.id, callback)

        self.add_edge(edge)

return edge

这里的注释已经说的比较清楚了,设置节点、填充edge数据,edge是在pgraph.Edge中,可以在

2019-04-12 10-34-01屏幕截图.png这里进行查看

为什么要对节点进行操作,这样做的好处是"如果对两个个s_initialize进行FUZZ操作,能够满足链表的特性,也就是dst知道src是不是上面一步变异的数据。"

最后需要进行session.fuzz了,也就是发送数据

 

def fuzz(self):

        """Fuzz the entire protocol tree.

        Iterates through and fuzzes all fuzz cases, skipping according to

        self.skip and restarting based on self.restart_interval.

        If you want the web server to be available, your program must persist

        after calling this method. helpers.pause_for_signal() is

        available to this end.

        Returns:

            None

        """

        self.total_mutant_index = 0

        self.total_num_mutations = self.num_mutations()

self._main_fuzz_loop(self._iterate_protocol())

迭代并且对整个node进行模糊。_main_fuzz_loop进行模糊和状态的跟踪,有兴趣的朋友可以自己跟进下代码看看他是如何实现的。


至此,代码的分析也基本完成,我们知道了他的整个工作原理,后面一节,我们将对boofuzz进行相应的代码提取和改造,拿出有用的部分来编写一个FUZZ小工具。

敬待。

# boofuzz
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者