Example #1
0
    def __init__ (self, block_name, request, min_reps=0, max_reps=None, step=1, variable=None, fuzzable=True, name=None):
        '''
        Repeat the rendered contents of the specified block cycling from min_reps to max_reps counting by step. By
        default renders to nothing. This block modifier is useful for fuzzing overflows in table entries. This block
        modifier MUST come after the block it is being applied to.

        @type  block_name: String
        @param block_name: Name of block to apply sizer to
        @type  request:    s_request
        @param request:    Request this block belongs to
        @type  min_reps:   Integer
        @param min_reps:   (Optional, def=0) Minimum number of block repetitions
        @type  max_reps:   Integer
        @param max_reps:   (Optional, def=None) Maximum number of block repetitions
        @type  step:       Integer
        @param step:       (Optional, def=1) Step count between min and max reps
        @type  variable:   Sulley Integer Primitive
        @param variable:   (Optional, def=None) Repititions will be derived from this variable, disables fuzzing
        @type  fuzzable:   Boolean
        @param fuzzable:   (Optional, def=True) Enable/disable fuzzing of this primitive
        @type  name:       String
        @param name:       (Optional, def=None) Specifying a name gives you direct access to a primitive
        '''

        self.block_name    = block_name
        self.request       = request
        self.variable      = variable
        self.min_reps      = min_reps
        self.max_reps      = max_reps
        self.step          = step
        self.fuzzable      = fuzzable
        self.name          = name

        self.value         = self.original_value = ""   # default to nothing!
        self.rendered      = ""                         # rendered value
        self.fuzz_complete = False                      # flag if this primitive has been completely fuzzed
        self.fuzz_library  = []                         # library of static fuzz heuristics to cycle through.
        self.mutant_index  = 0                          # current mutation number
        self.current_reps  = min_reps                   # current number of repetitions

        # ensure the target block exists.
        if self.block_name not in self.request.names:
            raise sex.error("CAN NOT ADD REPEATER FOR NON-EXISTANT BLOCK: %s" % self.block_name)

        # ensure the user specified either a variable to tie this repeater to or a min/max val.
        if self.variable == None and self.max_reps == None:
            raise sex.error("REPEATER FOR BLOCK %s DOES NOT HAVE A MIN/MAX OR VARIABLE BINDING" % self.block_name)

        # if a variable is specified, ensure it is an integer type.
        if self.variable and not isinstance(self.variable, primitives.bit_field):
            print self.variable
            raise sex.error("ATTEMPT TO BIND THE REPEATER FOR BLOCK %s TO A NON INTEGER PRIMITIVE" % self.block_name)

        # if not binding variable was specified, propogate the fuzz library with the repetition counts.
        if not self.variable:
            self.fuzz_library = range(self.min_reps, self.max_reps + 1, self.step)
        # otherwise, disable fuzzing as the repitition count is determined by the variable.
        else:
            self.fuzzable = False
Example #2
0
    def __init__ (self, block_name, request, min_reps=0, max_reps=None, step=1, variable=None, fuzzable=True, name=None):
        '''
        Repeat the rendered contents of the specified block cycling from min_reps to max_reps counting by step. By
        default renders to nothing. This block modifier is useful for fuzzing overflows in table entries. This block
        modifier MUST come after the block it is being applied to.

        @type  block_name: String
        @param block_name: Name of block to apply sizer to
        @type  request:    s_request
        @param request:    Request this block belongs to
        @type  min_reps:   Integer
        @param min_reps:   (Optional, def=0) Minimum number of block repetitions
        @type  max_reps:   Integer
        @param max_reps:   (Optional, def=None) Maximum number of block repetitions
        @type  step:       Integer
        @param step:       (Optional, def=1) Step count between min and max reps
        @type  variable:   Sulley Integer Primitive
        @param variable:   (Optional, def=None) Repititions will be derived from this variable, disables fuzzing
        @type  fuzzable:   Boolean
        @param fuzzable:   (Optional, def=True) Enable/disable fuzzing of this primitive
        @type  name:       String
        @param name:       (Optional, def=None) Specifying a name gives you direct access to a primitive
        '''

        self.block_name    = block_name
        self.request       = request
        self.variable      = variable
        self.min_reps      = min_reps
        self.max_reps      = max_reps
        self.step          = step
        self.fuzzable      = fuzzable
        self.name          = name

        self.value         = self.original_value = ""   # default to nothing!
        self.rendered      = ""                         # rendered value
        self.fuzz_complete = False                      # flag if this primitive has been completely fuzzed
        self.fuzz_library  = []                         # library of static fuzz heuristics to cycle through.
        self.mutant_index  = 0                          # current mutation number
        self.current_reps  = min_reps                   # current number of repetitions

        # ensure the target block exists.
        if self.block_name not in self.request.names:
            raise sex.error("CAN NOT ADD REPEATER FOR NON-EXISTANT BLOCK: %s" % self.block_name)

        # ensure the user specified either a variable to tie this repeater to or a min/max val.
        if self.variable == None and self.max_reps == None:
            raise sex.error("REPEATER FOR BLOCK %s DOES NOT HAVE A MIN/MAX OR VARIABLE BINDING" % self.block_name)

        # if a variable is specified, ensure it is an integer type.
        if self.variable and not isinstance(self.variable, primitives.bit_field):
            print self.variable
            raise sex.error("ATTEMPT TO BIND THE REPEATER FOR BLOCK %s TO A NON INTEGER PRIMITIVE" % self.block_name)

        # if not binding variable was specified, propogate the fuzz library with the repetition counts.
        if not self.variable:
            self.fuzz_library = range(self.min_reps, self.max_reps + 1, self.step)
        # otherwise, disable fuzzing as the repitition count is determined by the variable.
        else:
            self.fuzzable = False
Example #3
0
    def mutate(self):
        '''
        Mutate the primitive by stepping through the fuzz library, return False on completion. If variable-bounding is
        specified then fuzzing is implicitly disabled. Instead, the render() routine will properly calculate the
        correct repitition and return the appropriate data.

        @rtype:  Boolean
        @return: True on success, False otherwise.
        '''

        # render the contents of the block we are repeating.
        self.request.names[self.block_name].render()

        # if the target block for this sizer is not closed, raise an exception.
        if self.block_name not in self.request.closed_blocks:
            raise sex.error("CAN NOT APPLY REPEATER TO UNCLOSED BLOCK: %s" %
                            self.block_name)

        # if we've run out of mutations, raise the completion flag.
        if self.mutant_index == self.num_mutations():
            self.fuzz_complete = True

        # if fuzzing was disabled or complete, and mutate() is called, ensure the original value is restored.
        if not self.fuzzable or self.fuzz_complete:
            self.value = self.original_value
            return False

        # set the current value as a multiple of the rendered block based on the current fuzz library count.
        block = self.request.closed_blocks[self.block_name]
        self.value = block.rendered * self.fuzz_library[self.mutant_index]

        # increment the mutation count.
        self.mutant_index += 1

        return True
Example #4
0
    def render(self):
        '''
        Render the sizer.
        '''

        self.rendered = ""

        # if the sizer is fuzzable and we have not yet exhausted the the possible bit field values, use the fuzz value.
        if self.fuzzable and self.bit_field.mutant_index and not self.bit_field.fuzz_complete:
            self.rendered = self.bit_field.render()

        # if the target block for this sizer is already closed, render the size.
        elif self.block_name in self.request.closed_blocks:
            if self.inclusive: self_size = self.length
            else: self_size = 0

            block = self.request.closed_blocks[self.block_name]
            if self.length_in_bits:
                # we have to write the length in bytes, make shure you fill your sizer to a byte border
                if (len(block.rendered) * 8 + self_size) % 8:
                    raise sex.error("BLOCK " + self.name +
                                    " DIDNT FILL UP TO BYTE BORDER\n")
                self.bit_field.value = self.math(
                    (len(block.rendered) * 8 + self_size) / 8)
            else:
                self.bit_field.value = self.math(
                    len(block.rendered) + self_size)
            self.rendered = self.bit_field.render()

        # otherwise, add this sizer block to the requests callback list.
        else:
            if not self.request.callbacks.has_key(self.block_name):
                self.request.callbacks[self.block_name] = []

            self.request.callbacks[self.block_name].append(self)
Example #5
0
    def mutate (self):
        '''
        Mutate the primitive by stepping through the fuzz library, return False on completion. If variable-bounding is
        specified then fuzzing is implicitly disabled. Instead, the render() routine will properly calculate the
        correct repitition and return the appropriate data.

        @rtype:  Boolean
        @return: True on success, False otherwise.
        '''

        # render the contents of the block we are repeating.
        self.request.names[self.block_name].render()

        # if the target block for this sizer is not closed, raise an exception.
        if self.block_name not in self.request.closed_blocks:
            raise sex.error("CAN NOT APPLY REPEATER TO UNCLOSED BLOCK: %s" % self.block_name)

        # if we've run out of mutations, raise the completion flag.
        if self.mutant_index == self.num_mutations():
            self.fuzz_complete = True

        # if fuzzing was disabled or complete, and mutate() is called, ensure the original value is restored.
        if not self.fuzzable or self.fuzz_complete:
            self.value = self.original_value
            return False

        # set the current value as a multiple of the rendered block based on the current fuzz library count.
        block      = self.request.closed_blocks[self.block_name]
        self.value = block.rendered * self.fuzz_library[self.mutant_index]

        # increment the mutation count.
        self.mutant_index += 1

        return True
Example #6
0
    def pop (self):
        '''
        The last open block was closed, so pop it off of the block stack.
        '''

        if not self.block_stack:
            raise sex.error("BLOCK STACK OUT OF SYNC")

        self.block_stack.pop()
Example #7
0
    def pop(self):
        '''
        The last open block was closed, so pop it off of the block stack.
        '''

        if not self.block_stack:
            raise sex.error("BLOCK STACK OUT OF SYNC")

        self.block_stack.pop()
Example #8
0
    def render (self):
        '''
        Nothing fancy on render, simply return the value.
        '''

        # if the target block for this sizer is not closed, raise an exception.
        if self.block_name not in self.request.closed_blocks:
            raise sex.error("CAN NOT APPLY REPEATER TO UNCLOSED BLOCK: %s" % self.block_name)

        # if a variable-bounding was specified then set the value appropriately.
        if self.variable:
            block      = self.request.closed_blocks[self.block_name]
            self.value = block.rendered * self.variable.value

        self.rendered = self.value
        return self.rendered
Example #9
0
    def render (self):
        '''
        Nothing fancy on render, simply return the value.
        '''

        # if the target block for this sizer is not closed, raise an exception.
        if self.block_name not in self.request.closed_blocks:
            raise sex.error("CAN NOT APPLY REPEATER TO UNCLOSED BLOCK: %s" % self.block_name)

        # if a variable-bounding was specified then set the value appropriately.
        if self.variable:
            block      = self.request.closed_blocks[self.block_name]
            self.value = block.rendered * self.variable.value

        self.rendered = self.value
        return self.rendered
Example #10
0
    def checksum(self, data):
        '''
        Calculate and return the checksum (in raw bytes) over the supplied data.

        @type  data: Raw
        @param data: Rendered block data to calculate checksum over.

        @rtype:  Raw
        @return: Checksum.
        '''

        if type(self.algorithm) is str:
            if self.algorithm == "crc32":
                return struct.pack(self.endian + "L", zlib.crc32(data))

            elif self.algorithm == "adler32":
                return struct.pack(self.endian + "L", zlib.adler32(data))

            elif self.algorithm == "md5":
                digest = hashlib.md5(data).digest()

                # XXX - is this right?
                if self.endian == ">":
                    (a, b, c, d) = struct.unpack("<LLLL", digest)
                    digest = struct.pack(">LLLL", a, b, c, d)

                return digest

            elif self.algorithm == "sha1":
                digest = hashlib.sha1(data).digest()

                # XXX - is this right?
                if self.endian == ">":
                    (a, b, c, d, e) = struct.unpack("<LLLLL", digest)
                    digest = struct.pack(">LLLLL", a, b, c, d, e)

                return digest

            else:
                raise sex.error("INVALID CHECKSUM ALGORITHM SPECIFIED: %s" %
                                self.algorithm)
        else:
            return self.algorithm(data)
Example #11
0
    def checksum (self, data):
        '''
        Calculate and return the checksum (in raw bytes) over the supplied data.

        @type  data: Raw
        @param data: Rendered block data to calculate checksum over.

        @rtype:  Raw
        @return: Checksum.
        '''

        if type(self.algorithm) is str:
            if self.algorithm == "crc32":
                return struct.pack(self.endian+"L", zlib.crc32(data))

            elif self.algorithm == "adler32":
                return struct.pack(self.endian+"L", zlib.adler32(data))

            elif self.algorithm == "md5":
                digest = md5.md5(data).digest()

                # XXX - is this right?
                if self.endian == ">":
                    (a, b, c, d) = struct.unpack("<LLLL", digest)
                    digest       = struct.pack(">LLLL", a, b, c, d)

                return digest

            elif self.algorithm == "sha1":
                digest = sha.sha(data).digest()

                # XXX - is this right?
                if self.endian == ">":
                    (a, b, c, d, e) = struct.unpack("<LLLLL", digest)
                    digest          = struct.pack(">LLLLL", a, b, c, d, e)

                return digest

            else:
                raise sex.error("INVALID CHECKSUM ALGORITHM SPECIFIED: %s" % self.algorithm)
        else:
            return self.algorithm(data)
Example #12
0
    def render(self):
        # ensure there are no open blocks lingering.
        if self.block_stack:
            raise sex.error("UNCLOSED BLOCK: %s" % self.block_stack[-1].name)

        # render every item in the stack.
        for item in self.stack:
            item.render()

        # process remaining callbacks.
        for key in self.callbacks.keys():
            for item in self.callbacks[key]:
                item.render()

        def update_size(stack, name):
            # walk recursively through each block to update its size
            blocks = []

            for item in stack:
                if isinstance(item, size):
                    item.render()
                elif isinstance(item, block):
                    blocks += [item]

            for b in blocks:
                update_size(b.stack, b.name)
                b.render()

        # call update_size on each block of the request
        for item in self.stack:
            if isinstance(item, block):
                update_size(item.stack, item.name)
                item.render()

        # now collect, merge and return the rendered items.
        self.rendered = ""

        for item in self.stack:
            self.rendered += item.rendered

        return self.rendered
Example #13
0
    def render (self):
        # ensure there are no open blocks lingering.
        if self.block_stack:
            raise sex.error("UNCLOSED BLOCK: %s" % self.block_stack[-1].name)

        # render every item in the stack.
        for item in self.stack:
            item.render()

        # process remaining callbacks.
        for key in self.callbacks.keys():
            for item in self.callbacks[key]:
                item.render()

        def update_size(stack, name):
            # walk recursively through each block to update its size
            blocks = []

            for item in stack:
                if isinstance(item, size):
                    item.render()
                elif isinstance(item, block):
                    blocks += [item]

            for b in blocks:
                update_size(b.stack, b.name)
                b.render()

        # call update_size on each block of the request
        for item in self.stack:
            if isinstance(item, block):
                update_size(item.stack, item.name)
                item.render()

        # now collect, merge and return the rendered items.
        self.rendered = ""

        for item in self.stack:
            self.rendered += item.rendered

        return self.rendered
Example #14
0
    def render (self):
        # ensure there are no open blocks lingering.
        if self.block_stack:
            raise sex.error("UNCLOSED BLOCK: %s" % self.block_stack[-1].name)

        # render every item in the stack.
        for item in self.stack:
            item.render()

        # process remaining callbacks.
        for key in self.callbacks.keys():
            for item in self.callbacks[key]:
                item.render()

        # now collect, merge and return the rendered items.
        self.rendered = ""

        for item in self.stack:
            self.rendered += item.rendered

        return self.rendered
Example #15
0
    def render(self):
        # ensure there are no open blocks lingering.
        if self.block_stack:
            raise sex.error("UNCLOSED BLOCK: %s" % self.block_stack[-1].name)

        # render every item in the stack.
        for item in self.stack:
            item.render()

        # process remaining callbacks.
        for key in self.callbacks.keys():
            for item in self.callbacks[key]:
                item.render()

        # now collect, merge and return the rendered items.
        self.rendered = ""

        for item in self.stack:
            self.rendered += item.rendered

        return self.rendered
Example #16
0
    def push (self, item):
        '''
        Push an item into the block structure. If no block is open, the item goes onto the request stack. otherwise,
        the item goes onto the last open blocks stack.
        '''

        # if the item has a name, add it to the internal dictionary of names.
        if hasattr(item, "name") and item.name:
            # ensure the name doesn't already exist.
            if item.name in self.names.keys():
                raise sex.error("BLOCK NAME ALREADY EXISTS: %s" % item.name)

            self.names[item.name] = item

        # if there are no open blocks, the item gets pushed onto the request stack.
        # otherwise, the pushed item goes onto the stack of the last opened block.
        if not self.block_stack:
            self.stack.append(item)
        else:
            self.block_stack[-1].push(item)

        # add the opened block to the block stack.
        if isinstance(item, block):
            self.block_stack.append(item)
Example #17
0
    def push(self, item):
        '''
        Push an item into the block structure. If no block is open, the item goes onto the request stack. otherwise,
        the item goes onto the last open blocks stack.
        '''

        # if the item has a name, add it to the internal dictionary of names.
        if hasattr(item, "name") and item.name:
            # ensure the name doesn't already exist.
            if item.name in self.names.keys():
                raise sex.error("BLOCK NAME ALREADY EXISTS: %s" % item.name)

            self.names[item.name] = item

        # if there are no open blocks, the item gets pushed onto the request stack.
        # otherwise, the pushed item goes onto the stack of the last opened block.
        if not self.block_stack:
            self.stack.append(item)
        else:
            self.block_stack[-1].push(item)

        # add the opened block to the block stack.
        if isinstance(item, block):
            self.block_stack.append(item)
Example #18
0
    def __init__ (self, session_filename=None, audit_folder=None,skip=0, sleep_time=.2, log_level=2, proto="tcp", restart_interval=0, timeout=5.0, web_port=26001, crash_threshold=3, trans_in_q=None):
        '''
        Extends pgraph.graph and provides a container for architecting protocol dialogs.

        @type  session_filename: String
        @kwarg session_filename: (Optional, def=None) Filename to serialize persistant data to
        @type  skip:             Integer
        @kwarg skip:             (Optional, def=0) Number of test cases to skip
        @type  sleep_time:       Float
        @kwarg sleep_time:       (Optional, def=1.0) Time to sleep in between tests
        @type  log_level:        Integer
        @kwarg log_level:        (Optional, def=2) Set the log level, higher number == more log messages
        @type  proto:            String
        @kwarg proto:            (Optional, def="tcp") Communication protocol
        @type  timeout:          Float
        @kwarg timeout:          (Optional, def=5.0) Seconds to wait for a send/recv prior to timing out
        @type  restart_interval: Integer
        @kwarg restart_interval  (Optional, def=0) Restart the target after n test cases, disable by setting to 0
        @type  crash_threshold:  Integer
        @kwarg crash_threshold   (Optional, def=3) Maximum number of crashes allowed before a node is exhaust
        '''

        # run the parent classes initialization routine first.
        pgraph.graph.__init__(self)

        self.session_filename    = session_filename
        self.audit_folder        = audit_folder
        self.skip                = skip
        self.sleep_time          = sleep_time
        self.log_level           = log_level
        self.proto               = proto
        self.restart_interval    = restart_interval
        self.timeout             = timeout
        self.web_port            = web_port
        self.crash_threshold     = crash_threshold
        self.trans_in_q          = trans_in_q

        self.total_num_mutations = 0
        self.total_mutant_index  = 0
        self.crashes_detected    = 0
        self.fuzz_node           = None
        self.targets             = []
        self.netmon_results      = {}
        self.procmon_results     = {}
        self.pause_flag          = False
        self.running_flag        = True
        self.crashing_primitives = {}

        if self.proto == "tcp":
            self.proto = socket.SOCK_STREAM
        elif self.proto == "udp":
            self.proto = socket.SOCK_DGRAM
        else:
            raise sex.error("INVALID PROTOCOL SPECIFIED: %s" % self.proto)

        # import settings if they exist.
        self.import_file()
        
        # create a root node. we do this because we need to start fuzzing from a single point and the user may want
        # to specify a number of initial requests.
        self.root       = pgraph.node()
        self.root.name  = "__ROOT_NODE__"
        self.root.label = self.root.name
        self.last_recv  = None

        self.add_node(self.root)
Example #19
0
    def fuzz(self, this_node=None, path=[]):
        '''
        Call this routine to get the ball rolling. No arguments are necessary as they are both utilized internally
        during the recursive traversal of the session graph.

        @type  this_node: request (node)
        @param this_node: (Optional, def=None) Current node that is being fuzzed.
        @type  path:      List
        @param path:      (Optional, def=[]) Nodes along the path to the current one being fuzzed.
        '''

        # if no node is specified, then we start from the root node and initialize the session.
        if not this_node:
            # we can't fuzz if we don't have at least one target and one request.
            if not self.targets:
                raise sex.error("NO TARGETS SPECIFIED IN SESSION")

            if not self.edges_from(self.root.id):
                raise sex.error("NO REQUESTS SPECIFIED IN SESSION")

            this_node = self.root

            try:
                self.server_init()
            except:
                return

        # XXX - TODO - complete parallel fuzzing, will likely have to thread out each target
        target = self.targets[0]

        # step through every edge from the current node.
        for edge in self.edges_from(this_node.id):
            # the destination node is the one actually being fuzzed.
            self.fuzz_node = self.nodes[edge.dst]
            num_mutations = self.fuzz_node.num_mutations()

            # keep track of the path as we fuzz through it, don't count the root node.
            # we keep track of edges as opposed to nodes because if there is more then one path through a set of
            # given nodes we don't want any ambiguity.
            if edge.src != self.root.id:
                path.append(edge)

            current_path = " -> ".join([self.nodes[e.src].name for e in path])
            current_path += " -> %s" % self.fuzz_node.name

            self.log("current fuzz path: %s" % current_path, 2)
            self.log(
                "fuzzed %d of %d total cases" %
                (self.total_mutant_index, self.total_num_mutations), 2)
            self.updateProgressBar(self.total_mutant_index,
                                   self.total_num_mutations)
            self.update_GUI_crashes(self.crashes_detected)

            done_with_fuzz_node = False
            crash_count = 0

            # loop through all possible mutations of the fuzz node.
            while not done_with_fuzz_node:
                # the GUI sets unsets this flag when it wants the fuzzer to die
                # command line users can just ctrl-c/z or ctrl-alt-delete
                if not self.running_flag:
                    break
                # if we need to pause, do so.
                self.pause()

                # if we have exhausted the mutations of the fuzz node, break out of the while(1).
                # note: when mutate() returns False, the node has been reverted to the default (valid) state.
                if not self.fuzz_node.mutate():
                    self.log(
                        "all possible mutations for current fuzz node exhausted",
                        2)
                    done_with_fuzz_node = True
                    continue

                # make a record in the session that a mutation was made.
                self.total_mutant_index += 1

                # if we don't need to skip the current test case.
                if self.total_mutant_index > self.skip:
                    # if we've hit the restart interval, restart the target.
                    if self.restart_interval and self.total_mutant_index % self.restart_interval == 0:
                        self.log("restart interval of %d reached" %
                                 self.restart_interval)
                        self.restart_target(target)
                        # call this method in case we should wait for the client app to register with us after a restart
                        self.waitForRegister()
                        self.log(
                            "fuzzing %d of %d" %
                            (self.fuzz_node.mutant_index, num_mutations), 2)

                    # attempt to complete a fuzz transmission. keep trying until we are successful, whenever a failure
                    # occurs, restart the target.
                    while 1:
                        try:
                            # instruct the debugger/sniffer that we are about to send a new fuzz.
                            if target.procmon:
                                target.procmon.pre_send(
                                    self.total_mutant_index)
                            if target.netmon:
                                target.netmon.pre_send(self.total_mutant_index)

                            # establish a connection to the target.
                            self.host = target.host
                            self.port = target.port
                            sock = socket.socket(socket.AF_INET, self.proto)
                            sock.settimeout(self.timeout)

                            # if the user registered a pre-send function, pass it the sock and let it do the deed.
                            self.pre_send(sock)

                            # send out valid requests for each node in the current path up to the node we are fuzzing.
                            for e in path:
                                node = self.nodes[e.src]
                                self.transmit(sock, node, e, target)

                            # now send the current node we are fuzzing.
                            self.transmit(sock, self.fuzz_node, edge, target)
                            self.updateProgressBar(self.total_mutant_index,
                                                   self.total_num_mutations)

                            # if we reach this point the send was successful for break out of the while(1).
                            break

                        except sex.error, e:
                            sys.stderr.write("CAUGHT SULLEY EXCEPTION\n")
                            sys.stderr.write("\t" + e.__str__() + "\n")
                            sys.exit(1)

                        # close the socket.
                        self.close_socket(sock)

                        self.log("failed connecting to %s:%d" %
                                 (target.host, target.port))

                        self.log("restarting target and trying again")
                        self.restart_target(target)

                    # if the user registered a post-send function, pass it the sock and let it do the deed.
                    # we do this outside the try/except loop because if our fuzz causes a crash then the post_send()
                    # will likely fail and we don't want to sit in an endless loop.
                    self.post_send(sock)

                    # done with the socket.
                    # The following is necessary because in the case of a
                    # CANCEL being sent to an INVITE we need the socket to live
                    # for a little longer
                    # sock.close()
                    self.close_socket(sock)

                    # delay in between test cases.
                    self.log("sleeping for %f seconds" % self.sleep_time, 5)
                    time.sleep(self.sleep_time)

                    # poll the PED-RPC endpoints (netmon, procmon etc...) for the target.
                    self.poll_pedrpc(target)

                    # serialize the current session state to disk.
                    self.export_file()

            # recursively fuzz the remainder of the nodes in the session graph.
            if not self.running_flag:
                break
            self.fuzz(self.fuzz_node, path)
Example #20
0
    def __init__ (self, session_filename=None, skip=0, sleep_time=1.0, log_level=30, logfile=None, logfile_level=10, proto="tcp", iface="eth0", af="ipv4", bind=None, restart_interval=0, timeout=5.0, web_port=26000, crash_threshold=3, restart_sleep_time=300, start_webserver=False):
        '''
        Extends pgraph.graph and provides a container for architecting protocol dialogs.

        @type  session_filename:   String
        @kwarg session_filename:   (Optional, def=None) Filename to serialize persistant data to
        @type  skip:               Integer
        @kwarg skip:               (Optional, def=0) Number of test cases to skip
        @type  sleep_time:         Float
        @kwarg sleep_time:         (Optional, def=1.0) Time to sleep in between tests
        @type  log_level:          Integer
        @kwarg log_level:          (Optional, def=30) Set the log level (CRITICAL : 50 / ERROR : 40 / WARNING : 30 / INFO : 20 / DEBUG : 10)
        @type  logfile:            String
        @kwarg logfile:            (Optional, def=None) Name of log file
        @type  logfile_level:      Integer
        @kwarg logfile_level:      (Optional, def=10) Log level for log file, default is debug
        @type  proto:              String
        @kwarg proto:              (Optional, def="tcp") Communication protocol ("tcp", "udp", "ssl")
        @type  iface:              String
        @kwarg iface:              (Optional, def="eth0") Network interface for level2 fuzzing
        @type  af:                 String
        @kwarg af:                 (Optional, def="ipv4") Type of IP connection (accepted : ipv4 or ipv6)
        @type  bind:               Tuple (host, port)
        @kwarg bind:               (Optional, def=random) Socket bind address and port
        @type  timeout:            Float
        @kwarg timeout:            (Optional, def=5.0) Seconds to wait for a send/recv prior to timing out
        @type  restart_interval:   Integer
        @kwarg restart_interval    (Optional, def=0) Restart the target after n test cases, disable by setting to 0
        @type  crash_threshold:    Integer
        @kwarg crash_threshold     (Optional, def=3) Maximum number of crashes allowed before a node is exhaust
        @type  restart_sleep_time: Integer
        @kwarg restart_sleep_time: Optional, def=300) Time in seconds to sleep when target can't be restarted
        @type  start_webserver:    Boolean
        @kwarg start_webserver:    (Optional, def=True) Start webserver
        '''

        # run the parent classes initialization routine first.
        pgraph.graph.__init__(self)

        self.session_filename    = session_filename
        self.skip                = skip
        self.sleep_time          = sleep_time
        self.proto               = proto.lower()
        self.bind                = bind
        self.ssl                 = False
        self.restart_interval    = restart_interval
        self.timeout             = timeout
        self.web_port            = web_port
        self.crash_threshold     = crash_threshold
        self.restart_sleep_time  = restart_sleep_time

        # Network informations
        self.layer2              = False
        self.iface               = iface
        self.afname              = af
        self.connect_befor_send  = True
        self.wait_on_recv        = True


        # Initialize logger
        self.logger = logging.getLogger("Sulley_logger")
        self.logger.setLevel(logging.DEBUG)
        formatter = logging.Formatter('[%(asctime)s] [%(levelname)s] -> %(message)s')

        if logfile != None:
            filehandler = logging.FileHandler(logfile)
            filehandler.setLevel(logfile_level)
            filehandler.setFormatter(formatter)
            self.logger.addHandler(filehandler)

        consolehandler = logging.StreamHandler()
        consolehandler.setFormatter(formatter)
        consolehandler.setLevel(log_level)
        self.logger.addHandler(consolehandler)


        self.start_webserver     = start_webserver

        self.total_num_mutations = 0
        self.total_mutant_index  = 0
        self.fuzz_node           = None
        self.targets             = []
        self.netmon_results      = {}
        self.procmon_results     = {}
        self.protmon_results     = {}
        self.pause_flag          = False
        self.crashing_primitives = {}

        if self.proto == "tcp":
            self.proto = socket.SOCK_STREAM

        elif self.proto == "ssl":
            self.proto = socket.SOCK_STREAM
            self.ssl   = True

        elif self.proto == "udp":
            self.proto = socket.SOCK_DGRAM
            self.connect_befor_send = False

        elif self.proto == "layer2":
            self.layer2 = True
            self.connect_befor_send = False

        else:
            raise sex.error("INVALID PROTOCOL SPECIFIED: %s" % self.proto)

        if self.afname == "ipv4":
            self.af = socket.AF_INET
        elif self.afname == "ipv6":
            self.af = socket.AF_INET6
        else:
            raise sex.error("INVALID ADDRESS FAMILY SPECIFIED: %s" % self.afname)

        # import settings if they exist.
        self.import_file()

        # create a root node. we do this because we need to start fuzzing from a single point and the user may want
        # to specify a number of initial requests.
        self.root       = pgraph.node()
        self.root.name  = "__ROOT_NODE__"
        self.root.label = self.root.name
        self.last_recv  = None

        self.add_node(self.root)
Example #21
0
    def __init__(self,
                 session_filename=None,
                 skip=0,
                 sleep_time=1.0,
                 log_level=30,
                 logfile=None,
                 logfile_level=10,
                 proto="tcp",
                 bind=None,
                 restart_interval=0,
                 timeout=5.0,
                 web_port=26000,
                 crash_threshold=3,
                 restart_sleep_time=300):
        '''
        Extends pgraph.graph and provides a container for architecting protocol dialogs.

        @type  session_filename:   String
        @kwarg session_filename:   (Optional, def=None) Filename to serialize persistant data to
        @type  skip:               Integer
        @kwarg skip:               (Optional, def=0) Number of test cases to skip
        @type  sleep_time:         Float
        @kwarg sleep_time:         (Optional, def=1.0) Time to sleep in between tests
        @type  log_level:          Integer
        @kwarg log_level:          (Optional, def=30) Set the log level (CRITICAL : 50 / ERROR : 40 / WARNING : 30 / INFO : 20 / DEBUG : 10)
        @type  logfile:            String
        @kwarg logfile:            (Optional, def=None) Name of log file
        @type  logfile_level:      Integer
        @kwarg logfile_level:      (Optional, def=10) Log level for log file, default is debug
        @type  proto:              String
        @kwarg proto:              (Optional, def="tcp") Communication protocol ("tcp", "udp", "ssl")
        @type  bind:               Tuple (host, port)
        @kwarg bind:               (Optional, def=random) Socket bind address and port
        @type  timeout:            Float
        @kwarg timeout:            (Optional, def=5.0) Seconds to wait for a send/recv prior to timing out
        @type  restart_interval:   Integer
        @kwarg restart_interval    (Optional, def=0) Restart the target after n test cases, disable by setting to 0
        @type  crash_threshold:    Integer
        @kwarg crash_threshold     (Optional, def=3) Maximum number of crashes allowed before a node is exhaust
        @type  restart_sleep_time: Integer
        @kwarg restart_sleep_time: Optional, def=300) Time in seconds to sleep when target can't be restarted
        '''

        # run the parent classes initialization routine first.
        pgraph.graph.__init__(self)

        self.session_filename = session_filename
        self.skip = skip
        self.sleep_time = sleep_time
        self.proto = proto.lower()
        self.bind = bind
        self.ssl = False
        self.restart_interval = restart_interval
        self.timeout = timeout
        self.web_port = web_port
        self.crash_threshold = crash_threshold
        self.restart_sleep_time = restart_sleep_time

        # Initialize logger
        self.logger = logging.getLogger("Sulley_logger")
        self.logger.setLevel(logging.DEBUG)
        formatter = logging.Formatter(
            '[%(asctime)s] [%(levelname)s] -> %(message)s')

        if logfile != None:
            filehandler = logging.FileHandler(logfile)
            filehandler.setLevel(logfile_level)
            filehandler.setFormatter(formatter)
            self.logger.addHandler(filehandler)

        consolehandler = logging.StreamHandler()
        consolehandler.setFormatter(formatter)
        consolehandler.setLevel(log_level)
        self.logger.addHandler(consolehandler)

        self.total_num_mutations = 0
        self.total_mutant_index = 0
        self.fuzz_node = None
        self.targets = []
        self.netmon_results = {}
        self.procmon_results = {}
        self.protmon_results = {}
        self.pause_flag = False
        self.crashing_primitives = {}

        if self.proto == "tcp":
            self.proto = socket.SOCK_STREAM

        elif self.proto == "ssl":
            self.proto = socket.SOCK_STREAM
            self.ssl = True

        elif self.proto == "udp":
            self.proto = socket.SOCK_DGRAM

        else:
            raise sex.error("INVALID PROTOCOL SPECIFIED: %s" % self.proto)

        # import settings if they exist.
        self.import_file()

        # create a root node. we do this because we need to start fuzzing from a single point and the user may want
        # to specify a number of initial requests.
        self.root = pgraph.node()
        self.root.name = "__ROOT_NODE__"
        self.root.label = self.root.name
        self.last_recv = None

        self.add_node(self.root)
Example #22
0
    def fuzz(self, this_node=None, path=[]):
        '''
        Call this routine to get the ball rolling. No arguments are necessary as they are both utilized internally
        during the recursive traversal of the session graph.

        @type  this_node: request (node)
        @param this_node: (Optional, def=None) Current node that is being fuzzed.
        @type  path:      List
        @param path:      (Optional, def=[]) Nodes along the path to the current one being fuzzed.
        '''

        # if no node is specified, then we start from the root node and initialize the session.
        if not this_node:
            # we can't fuzz if we don't have at least one target and one request.
            if not self.targets:
                raise sex.error("NO TARGETS SPECIFIED IN SESSION")

            if not self.edges_from(self.root.id):
                raise sex.error("NO REQUESTS SPECIFIED IN SESSION")

            this_node = self.root

            try:
                self.server_init()
            except:
                return

        # XXX - TODO - complete parallel fuzzing, will likely have to thread out each target
        target = self.targets[0]

        # step through every edge from the current node.
        for edge in self.edges_from(this_node.id):
            # the destination node is the one actually being fuzzed.
            self.fuzz_node = self.nodes[edge.dst]
            num_mutations = self.fuzz_node.num_mutations()

            # keep track of the path as we fuzz through it, don't count the root node.
            # we keep track of edges as opposed to nodes because if there is more then one path through a set of
            # given nodes we don't want any ambiguity.
            path.append(edge)

            current_path = " -> ".join(
                [self.nodes[e.src].name for e in path[1:]])
            current_path += " -> %s" % self.fuzz_node.name

            self.logger.error("current fuzz path: %s" % current_path)
            self.logger.error(
                "fuzzed %d of %d total cases" %
                (self.total_mutant_index, self.total_num_mutations))

            done_with_fuzz_node = False
            crash_count = 0

            # loop through all possible mutations of the fuzz node.
            while not done_with_fuzz_node:
                # if we need to pause, do so.
                self.pause()

                # if we have exhausted the mutations of the fuzz node, break out of the while(1).
                # note: when mutate() returns False, the node has been reverted to the default (valid) state.
                if not self.fuzz_node.mutate():
                    self.logger.error(
                        "all possible mutations for current fuzz node exhausted"
                    )
                    done_with_fuzz_node = True
                    continue

                # make a record in the session that a mutation was made.
                self.total_mutant_index += 1

                # if we've hit the restart interval, restart the target.
                if self.restart_interval and self.total_mutant_index % self.restart_interval == 0:
                    self.logger.error("restart interval of %d reached" %
                                      self.restart_interval)
                    self.restart_target(target)

                # exception error handling routine, print log message and restart target.
                def error_handler(e, msg, target, sock=None):
                    if sock:
                        sock.close()

                    msg += "\nException caught: %s" % repr(e)
                    msg += "\nRestarting target and trying again"

                    self.logger.critical(msg)
                    self.restart_target(target)

                # if we don't need to skip the current test case.
                if self.total_mutant_index > self.skip:
                    self.logger.error(
                        "fuzzing %d of %d" %
                        (self.fuzz_node.mutant_index, num_mutations))

                    # attempt to complete a fuzz transmission. keep trying until we are successful, whenever a failure
                    # occurs, restart the target.
                    while 1:
                        # instruct the debugger/sniffer that we are about to send a new fuzz.
                        if target.procmon:
                            try:
                                target.procmon.pre_send(
                                    self.total_mutant_index)
                            except Exception, e:
                                error_handler(e,
                                              "failed on procmon.pre_send()",
                                              target)
                                continue

                        if target.netmon:
                            try:
                                target.netmon.pre_send(self.total_mutant_index)
                            except Exception, e:
                                error_handler(e, "failed on netmon.pre_send()",
                                              target)
                                continue

                        try:
                            # establish a connection to the target.
                            sock = socket.socket(socket.AF_INET, self.proto)
                        except Exception, e:
                            error_handler(e, "failed creating socket", target)
                            continue

                        if self.bind:
                            try:
                                sock.bind(self.bind)
                            except Exception, e:
                                error_handler(e, "failed binding on socket",
                                              target, sock)
                                continue

                        try:
                            sock.settimeout(self.timeout)
                            # Connect is needed only for TCP stream
                            if self.proto == socket.SOCK_STREAM:
                                sock.connect((target.host, target.port))
                        except Exception, e:
                            error_handler(e, "failed connecting on socket",
                                          target, sock)
                            continue
Example #23
0
    def fuzz (self, this_node=None, path=[]):
        '''
        Call this routine to get the ball rolling. No arguments are necessary as they are both utilized internally
        during the recursive traversal of the session graph.

        @type  this_node: request (node)
        @param this_node: (Optional, def=None) Current node that is being fuzzed.
        @type  path:      List
        @param path:      (Optional, def=[]) Nodes along the path to the current one being fuzzed.
        '''

        # if no node is specified, then we start from the root node and initialize the session.
        if not this_node:
            # we can't fuzz if we don't have at least one target and one request.
            if not self.targets:
                raise sex.error("NO TARGETS SPECIFIED IN SESSION")

            if not self.edges_from(self.root.id):
                raise sex.error("NO REQUESTS SPECIFIED IN SESSION")

            this_node = self.root

            try:    self.server_init()
            except: return

        # XXX - TODO - complete parallel fuzzing, will likely have to thread out each target
        target = self.targets[0]

        # step through every edge from the current node.
        for edge in self.edges_from(this_node.id):
            # the destination node is the one actually being fuzzed.
            self.fuzz_node = self.nodes[edge.dst]
            num_mutations  = self.fuzz_node.num_mutations()

            # keep track of the path as we fuzz through it, don't count the root node.
            # we keep track of edges as opposed to nodes because if there is more then one path through a set of
            # given nodes we don't want any ambiguity.
            path.append(edge)

            current_path  = " -> ".join([self.nodes[e.src].name for e in path[1:]])
            current_path += " -> %s" % self.fuzz_node.name

            self.log("current fuzz path: %s" % current_path, 2)
            self.log("fuzzed %d of %d total cases" % (self.total_mutant_index, self.total_num_mutations), 2)

            done_with_fuzz_node = False
            crash_count         = 0

            # loop through all possible mutations of the fuzz node.
            while not done_with_fuzz_node:
                # if we need to pause, do so.
                self.pause()

                # if we have exhausted the mutations of the fuzz node, break out of the while(1).
                # note: when mutate() returns False, the node has been reverted to the default (valid) state.
                if not self.fuzz_node.mutate():
                    self.log("all possible mutations for current fuzz node exhausted", 2)
                    done_with_fuzz_node = True
                    continue

                # make a record in the session that a mutation was made.
                self.total_mutant_index += 1

                # if we've hit the restart interval, restart the target.
                if self.restart_interval and self.total_mutant_index % self.restart_interval == 0:
                    self.log("restart interval of %d reached" % self.restart_interval)
                    self.restart_target(target)

                # exception error handling routine, print log message and restart target.
                def error_handler (e, msg, target, sock=None):
                    if sock:
                        sock.close()

                    msg += "\nException caught: %s" % repr(e)
                    msg += "\nRestarting target and trying again"

                    self.log(msg)
                    self.restart_target(target)

                # if we don't need to skip the current test case.
                if self.total_mutant_index > self.skip:
                    self.log("fuzzing %d of %d" % (self.fuzz_node.mutant_index, num_mutations), 2)

                    # attempt to complete a fuzz transmission. keep trying until we are successful, whenever a failure
                    # occurs, restart the target.
                    while 1:
                        # instruct the debugger/sniffer that we are about to send a new fuzz.
                        if target.procmon:
                            try:
                                target.procmon.pre_send(self.total_mutant_index)
                            except Exception, e:
                                error_handler(e, "failed on procmon.pre_send()", target)
                                continue

                        if target.netmon:
                            try:
                                target.netmon.pre_send(self.total_mutant_index)
                            except Exception, e:
                                error_handler(e, "failed on netmon.pre_send()", target)
                                continue

                        try:
                            # establish a connection to the target.
                            sock = socket.socket(socket.AF_INET, self.proto)
                        except Exception, e:
                            error_handler(e, "failed creating socket", target)
                            continue

                        if self.bind:
                            try:
                                sock.bind(self.bind)
                            except Exception, e:
                                error_handler(e, "failed binding on socket", target, sock)
                                continue

                        try:
                            sock.settimeout(self.timeout)
                            sock.connect((target.host, target.port))
                        except Exception, e:
                            error_handler(e, "failed connecting on socket", target, sock)
                            continue
Example #24
0
    def __init__(self,
                 session_filename=None,
                 audit_folder=None,
                 skip=0,
                 sleep_time=.2,
                 log_level=2,
                 proto="tcp",
                 restart_interval=0,
                 timeout=5.0,
                 web_port=26001,
                 crash_threshold=3,
                 trans_in_q=None):
        '''
        Extends pgraph.graph and provides a container for architecting protocol dialogs.

        @type  session_filename: String
        @kwarg session_filename: (Optional, def=None) Filename to serialize persistant data to
        @type  skip:             Integer
        @kwarg skip:             (Optional, def=0) Number of test cases to skip
        @type  sleep_time:       Float
        @kwarg sleep_time:       (Optional, def=1.0) Time to sleep in between tests
        @type  log_level:        Integer
        @kwarg log_level:        (Optional, def=2) Set the log level, higher number == more log messages
        @type  proto:            String
        @kwarg proto:            (Optional, def="tcp") Communication protocol
        @type  timeout:          Float
        @kwarg timeout:          (Optional, def=5.0) Seconds to wait for a send/recv prior to timing out
        @type  restart_interval: Integer
        @kwarg restart_interval  (Optional, def=0) Restart the target after n test cases, disable by setting to 0
        @type  crash_threshold:  Integer
        @kwarg crash_threshold   (Optional, def=3) Maximum number of crashes allowed before a node is exhaust
        '''

        # run the parent classes initialization routine first.
        pgraph.graph.__init__(self)

        self.session_filename = session_filename
        self.audit_folder = audit_folder
        self.skip = skip
        self.sleep_time = sleep_time
        self.log_level = log_level
        self.proto = proto
        self.restart_interval = restart_interval
        self.timeout = timeout
        self.web_port = web_port
        self.crash_threshold = crash_threshold
        self.trans_in_q = trans_in_q

        self.total_num_mutations = 0
        self.total_mutant_index = 0
        self.crashes_detected = 0
        self.fuzz_node = None
        self.targets = []
        self.netmon_results = {}
        self.procmon_results = {}
        self.pause_flag = False
        self.running_flag = True
        self.crashing_primitives = {}

        if self.proto == "tcp":
            self.proto = socket.SOCK_STREAM
        elif self.proto == "udp":
            self.proto = socket.SOCK_DGRAM
        else:
            raise sex.error("INVALID PROTOCOL SPECIFIED: %s" % self.proto)

        # import settings if they exist.
        self.import_file()

        # create a root node. we do this because we need to start fuzzing from a single point and the user may want
        # to specify a number of initial requests.
        self.root = pgraph.node()
        self.root.name = "__ROOT_NODE__"
        self.root.label = self.root.name
        self.last_recv = None

        self.add_node(self.root)
Example #25
0
    def fuzz (self, this_node=None, path=[]):
        '''
        Call this routine to get the ball rolling. No arguments are necessary as they are both utilized internally
        during the recursive traversal of the session graph.

        @type  this_node: request (node)
        @param this_node: (Optional, def=None) Current node that is being fuzzed.
        @type  path:      List
        @param path:      (Optional, def=[]) Nodes along the path to the current one being fuzzed.
        '''

        # if no node is specified, then we start from the root node and initialize the session.
        if not this_node:
            # we can't fuzz if we don't have at least one target and one request.
            if not self.targets:
                raise sex.error("NO TARGETS SPECIFIED IN SESSION")

            if not self.edges_from(self.root.id):
                raise sex.error("NO REQUESTS SPECIFIED IN SESSION")

            this_node = self.root

            try:    self.server_init()
            except: return

        # XXX - TODO - complete parallel fuzzing, will likely have to thread out each target
        target = self.targets[0]

        # step through every edge from the current node.
        for edge in self.edges_from(this_node.id):
            # the destination node is the one actually being fuzzed.
            self.fuzz_node = self.nodes[edge.dst]
            num_mutations  = self.fuzz_node.num_mutations()

            # keep track of the path as we fuzz through it, don't count the root node.
            # we keep track of edges as opposed to nodes because if there is more then one path through a set of
            # given nodes we don't want any ambiguity.
            if edge.src != self.root.id:
                path.append(edge)

            current_path  = " -> ".join([self.nodes[e.src].name for e in path])
            current_path += " -> %s" % self.fuzz_node.name

            self.log("current fuzz path: %s" % current_path, 2)            
            self.log("fuzzed %d of %d total cases" % (self.total_mutant_index, self.total_num_mutations), 2)
            self.updateProgressBar(self.total_mutant_index, self.total_num_mutations)
            self.update_GUI_crashes(self.crashes_detected)

            done_with_fuzz_node = False
            crash_count         = 0

            # loop through all possible mutations of the fuzz node.
            while not done_with_fuzz_node:
                # the GUI sets unsets this flag when it wants the fuzzer to die
                # command line users can just ctrl-c/z or ctrl-alt-delete
                if not self.running_flag:
                    break                
                # if we need to pause, do so.
                self.pause()

                # if we have exhausted the mutations of the fuzz node, break out of the while(1).
                # note: when mutate() returns False, the node has been reverted to the default (valid) state.
                if not self.fuzz_node.mutate():
                    self.log("all possible mutations for current fuzz node exhausted", 2)
                    done_with_fuzz_node = True
                    continue

                # make a record in the session that a mutation was made.
                self.total_mutant_index += 1

                # if we don't need to skip the current test case.
                if self.total_mutant_index > self.skip:
                    # if we've hit the restart interval, restart the target.
                    if self.restart_interval and self.total_mutant_index % self.restart_interval == 0:
                        self.log("restart interval of %d reached" % self.restart_interval)
                        self.restart_target(target)
                        # call this method in case we should wait for the client app to register with us after a restart
                        self.waitForRegister()
                        self.log("fuzzing %d of %d" % (self.fuzz_node.mutant_index, num_mutations), 2)

                    # attempt to complete a fuzz transmission. keep trying until we are successful, whenever a failure
                    # occurs, restart the target.
                    while 1:
                        try:
                            # instruct the debugger/sniffer that we are about to send a new fuzz.
                            if target.procmon: target.procmon.pre_send(self.total_mutant_index)
                            if target.netmon:  target.netmon.pre_send(self.total_mutant_index)

                            # establish a connection to the target.
                            self.host = target.host
                            self.port = target.port
                            sock = socket.socket(socket.AF_INET, self.proto)
                            sock.settimeout(self.timeout)                           

                            # if the user registered a pre-send function, pass it the sock and let it do the deed.
                            self.pre_send(sock)

                            # send out valid requests for each node in the current path up to the node we are fuzzing.
                            for e in path:
                                node = self.nodes[e.src]
                                self.transmit(sock, node, e, target)

                            # now send the current node we are fuzzing.
                            self.transmit(sock, self.fuzz_node, edge, target)
                            self.updateProgressBar(self.total_mutant_index, self.total_num_mutations)

                            # if we reach this point the send was successful for break out of the while(1).
                            break

                        except sex.error, e:
                            sys.stderr.write("CAUGHT SULLEY EXCEPTION\n")
                            sys.stderr.write("\t" + e.__str__() + "\n")
                            sys.exit(1)

                        # close the socket.                        
                        self.close_socket(sock)

                        self.log("failed connecting to %s:%d" % (target.host, target.port))
                        
                        self.log("restarting target and trying again")
                        self.restart_target(target)

                    # if the user registered a post-send function, pass it the sock and let it do the deed.
                    # we do this outside the try/except loop because if our fuzz causes a crash then the post_send()
                    # will likely fail and we don't want to sit in an endless loop.
                    self.post_send(sock)

                    # done with the socket.
                    # The following is necessary because in the case of a
                    # CANCEL being sent to an INVITE we need the socket to live
                    # for a little longer
                    # sock.close()
                    self.close_socket(sock)

                    # delay in between test cases.
                    self.log("sleeping for %f seconds" % self.sleep_time, 5)
                    time.sleep(self.sleep_time)

                    # poll the PED-RPC endpoints (netmon, procmon etc...) for the target.
                    self.poll_pedrpc(target)

                    # serialize the current session state to disk.
                    self.export_file()

            # recursively fuzz the remainder of the nodes in the session graph.
            if not self.running_flag:
                break            
            self.fuzz(self.fuzz_node, path)
Example #26
0
    def fuzz(self, this_node=None, path=[]):
        '''
        Call this routine to get the ball rolling. No arguments are necessary as they are both utilized internally
        during the recursive traversal of the session graph.

        @type  this_node: request (node)
        @param this_node: (Optional, def=None) Current node that is being fuzzed.
        @type  path:      List
        @param path:      (Optional, def=[]) Nodes along the path to the current one being fuzzed.
        '''
        if self.fuzz_mode == "auto":
            # if no node is specified, then we start from the root node and initialize the session.
            if not this_node:
                # we can't fuzz if we don't have at least one target and one request.
                if not self.targets:
                    raise sex.error("NO TARGETS SPECIFIED IN SESSION")

                if not self.edges_from(self.root.id):
                    raise sex.error("NO REQUESTS SPECIFIED IN SESSION")

                this_node = self.root

                try:
                    self.server_init()
                except:
                    return

            # XXX - TODO - complete parallel fuzzing, will likely have to thread out each target
            target = self.targets[0]

            # step through every edge from the current node.
            for edge in self.edges_from(this_node.id):
                # the destination node is the one actually being fuzzed.
                self.fuzz_node = self.nodes[edge.dst]
                num_mutations = self.fuzz_node.num_mutations()

                # keep track of the path as we fuzz through it, don't count the root node.
                # we keep track of edges as opposed to nodes because if there is more then one path through a set of
                # given nodes we don't want any ambiguity.
                path.append(edge)

                current_path = " -> ".join(
                    [self.nodes[e.src].name for e in path[1:]])
                current_path += " -> %s" % self.fuzz_node.name

                self.logger.error("current fuzz path: %s" % current_path)
                self.logger.error(
                    "fuzzed %d of %d total cases" %
                    (self.total_mutant_index, self.total_num_mutations))

                done_with_fuzz_node = False
                crash_count = 0

                # loop through all possible mutations of the fuzz node.
                while not done_with_fuzz_node:
                    # if we need to pause, do so.
                    #self.pause()

                    # if we have exhausted the mutations of the fuzz node, break out of the while(1).
                    # note: when mutate() returns False, the node has been reverted to the default (valid) state.
                    if not self.fuzz_node.mutate():
                        self.logger.error(
                            "all possible mutations for current fuzz node exhausted"
                        )
                        done_with_fuzz_node = True
                        continue

                    # make a record in the session that a mutation was made.
                    self.total_mutant_index += 1

                    # if we've hit the restart interval, restart the target.
                    if self.restart_interval and self.total_mutant_index % self.restart_interval == 0:
                        self.logger.error("restart interval of %d reached" %
                                          self.restart_interval)
                        self.restart_target(target)

                    # exception error handling routine, print log message and restart target.
                    def error_handler(e, msg, target, sock=None):
                        if sock:
                            sock.close()

                        msg += "\nException caught: %s" % repr(e)
                        msg += "\nRestarting target and trying again"

                        self.logger.critical(msg)
                        self.restart_target(target)

                    # if we don't need to skip the current test case.
                    if self.total_mutant_index > self.skip:
                        self.logger.error(
                            "fuzzing %d of %d" %
                            (self.fuzz_node.mutant_index, num_mutations))

                        # attempt to complete a fuzz transmission. keep trying until we are successful, whenever a failure
                        # occurs, restart the target.
                        while 1:
                            # instruct the debugger/sniffer that we are about to send a new fuzz.
                            # send out valid requests for each node in the current path up to the node we are fuzzing.
                            try:
                                for e in path[:-1]:
                                    node = self.nodes[e.dst]
                                    self.transmit(node, e, target)
                            except Exception, e:
                                error_handler(
                                    e,
                                    "failed transmitting a node up the path",
                                    target, sock)
                                continue

                            # now send the current node we are fuzzing.
                            self.transmit(self.fuzz_node, edge, target)

                            # if we reach this point the send was successful for break out of the while(1).
                            break
                        '''
                        # if the user registered a post-send function, pass it the sock and let it do the deed.
                        # we do this outside the try/except loop because if our fuzz causes a crash then the post_send()
                        # will likely fail and we don't want to sit in an endless loop.
                        try:
                            self.post_send(sock)
                        except Exception, e:
                            error_handler(e, "post_send() failed", target, sock)
                        '''
                        # done with the socket.
                        # delay in between test cases.
                        self.logger.warning("sleeping for %f seconds" %
                                            self.sleep_time)
                        time.sleep(self.sleep_time)

                        # poll the PED-RPC endpoints (netmon, procmon etc...) for the target.
                        self.poll_pedrpc(target)

                        # serialize the current session state to disk.
                        self.export_file()

                # recursively fuzz the remainder of the nodes in the session graph.
                self.fuzz(self.fuzz_node, path)

            # finished with the last node on the path, pop it off the path stack.
            if path:
                path.pop()

            # loop to keep the main thread running and be able to receive signals
            if self.signal_module:
                # wait for a signal only if fuzzing is finished (this function is recursive)
                # if fuzzing is not finished, web interface thread will catch it
                if self.total_mutant_index == self.total_num_mutations:
                    import signal