Пример #1
0
    def __init__(self, opts):
        """
        :opts:
                {
                    pcap:                     (boolean) Whether to save packets in pcap format,
                    udp:                      (boolean) Whether to use UDP proxy (default: TCP),
                    program:                  (list) Program and arguments,
                    jobs:                     (int) Number of jobs to run concurrently,
                    timeout:                  (int) Seconds to give process before killing it,
                    output_directory:         (string) Directory to place found bugs,
                    input_arguments:          (string) Arguments for the input module,
                    input_module:             (string) Module to use for input generation,
                    fuzzing_module:           (string) Module to use for fuzzing the network data,
                    prerocessing_module:      (string) Module to use for preprocessing data,
                    instrumentation_module:   (string) Module to use for instrumentation
                }
        """
        for opt, value in opts.items():
            setattr(self, opt, value)

        Fuzzer.__init__(self, opts)
        self._tlock = threading.Lock()

        self._set_operating_mode()
        self._validate_arguments()

        if self.udp:
            self.proxy = UdpProxy(self.log)
        else:
            self.proxy = TcpProxy(self.log)

        self.proxy.address = self.address
        self.proxy.listen_port = self.listen_port
        self.proxy.forward_port = self.forward_port
        self.proxy.data_handler = self.data_handler
Пример #2
0
class NetworkFuzzer(Fuzzer):
    """
    Network fuzzer with capability to work either as transparent proxy or
    also as process manager by starting services and their input
    automatically. All data that needs to be handled should go through
    the proxy which will then pass it along channels to the recipient.

    The packets that pass through the proxy will be saved individually
    as into a packet backlog per session.

    When input is self generated, the proxy should use the default port.
    Input modules using sockets send all traffic to the default
    proxy port.

    MODE_TRANSPARENT:
        Transparent mode only initializes proxy and data handling
        modules (preprocessing and fuzzing). By default the proxy
        will listen on port 6000 and forward everything to the
        specified port.

    MODE_GENERATE:
        Generator mode will use the input module to generate input
        that is then forwarded to the recipient.

    MODE_SERVICE:
        Service mode is MODE_TRANSPARENT + one managed service to
        which the data is forwarded through the proxy. The managed
        service is handled by the ProcessManagement object and can
        only handle one service at a time in this mode.

    MODE_CONTROLLED:
        Controlled mode is the combination of MODE_SERVICE and
        MODE_GENERATE with the added benefit that by using randomized
        ports (%p), it is possible run dozens or hundreds of instances
        concurrently with the ProcessManagement object.
    """

    MODE_TRANSPARENT = False
    MODE_GENERATE = False
    MODE_SERVICE = False
    MODE_CONTROLLED = False

    def __init__(self, opts):
        """
        :opts:
                {
                    pcap:                     (boolean) Whether to save packets in pcap format,
                    udp:                      (boolean) Whether to use UDP proxy (default: TCP),
                    program:                  (list) Program and arguments,
                    jobs:                     (int) Number of jobs to run concurrently,
                    timeout:                  (int) Seconds to give process before killing it,
                    output_directory:         (string) Directory to place found bugs,
                    input_arguments:          (string) Arguments for the input module,
                    input_module:             (string) Module to use for input generation,
                    fuzzing_module:           (string) Module to use for fuzzing the network data,
                    prerocessing_module:      (string) Module to use for preprocessing data,
                    instrumentation_module:   (string) Module to use for instrumentation
                }
        """
        for opt, value in opts.items():
            setattr(self, opt, value)

        Fuzzer.__init__(self, opts)
        self._tlock = threading.Lock()

        self._set_operating_mode()
        self._validate_arguments()

        if self.udp:
            self.proxy = UdpProxy(self.log)
        else:
            self.proxy = TcpProxy(self.log)

        self.proxy.address = self.address
        self.proxy.listen_port = self.listen_port
        self.proxy.forward_port = self.forward_port
        self.proxy.data_handler = self.data_handler

    def _validate_arguments(self):
        """
        Validate command line arguments.
        """
        if not self.forward_port and not self.MODE_CONTROLLED:
            self.log.error("No forward port specified.")
            self._exit()
        elif not self.MODE_CONTROLLED:
            self.forward_port = int(self.forward_port)

        if not self.listen_port:
            self.listen_port = 6000
        else:
            self.listen_port = int(self.listen_port)

        if not self.address:
            self.address = "localhost"

        if self.MODE_CONTROLLED or self.MODE_SERVICE:
            if not self.jobs:
                self.jobs = 1

    def _exit(self):
        self.proxy.running = False

        if not self.MODE_TRANSPARENT or not self.MODE_GENERATE:
            self.process_management.running = False

    def _set_operating_mode(self):
        """
        Sets operating mode according to given arguments.
        """
        if (self.input_module or self.input_arguments) and self.program:
            self.log.info("Running on controlled mode")
            self.MODE_CONTROLLED = True

        elif self.input_module or self.input_arguments:
            self.log.info("Running on input generation mode")
            self.MODE_GENERATE = True

        elif self.program:
            self.log.info("Running on service mode")
            self.MODE_SERVICE = True

        else:
            self.log.info("Running on transparent mode")
            self.MODE_TRANSPARENT = True

    def _new_process_handler(self, process):
        """
        Creates new Process instance that ProcessManagement
        can monitor.

        In controlled mode NetworkFuzzer will use the
        post_start_handler to start each input generator
        for each process
        """
        self.log.debug("new_process_handler called")
        if not self.timeout:
            self.timeout = 0

        process.time_left = int(self.timeout)
        if process.time_left == 0:
            process.timeout = False

        # For saving network session to file
        # file_type = "raw"
        # if self.pcap:
        #     file_type = "pcap"

        # process.session = "/tmp/mal-%d.%s" % (process.index, file_type)
        # Instead of session files, use packet_queue list
        # to log all packets, and if necessary, write them to file.
        process.packet_queue = []

        # Redirect stderr to per process log file
        process.stderr.close()
        process.stderr_file = "/tmp/mal-%d.stderr" % process.index
        process.stderr = open(process.stderr_file, "w")

        # NetworkFuzzer will assume "%p" to
        # be listening port for the service and use
        # bind("", 0) to bind to any free port.
        if "%p" in self.program[0]:
            process.port = self._random_port()

            with self._tlock:
                self.log.debug("Starting new service on port %d" % process.port)
                self.proxy.service_ports.insert(0, process.port)

            process.program = self.program[0].replace("%p", str(process.port))
            process.program = process.program.split()

        else:
            process.port = self.forward_port
            process.program = self.program[0].split()

        # On controlled mode, we are responsible
        # for the input generation
        if self.MODE_CONTROLLED:
            process.post_start_handler = self._post_start_handler

        return process
    
    def _random_port(self):
        return random.randint(10000, 65000)

    def _post_start_handler(self, process):
        """
        Wait until the service has stopped initializing
        itself, and then start the input generator.
        """
        self.log.debug("post_start_handler() called")
        
        if "%p" in self.input_arguments:
            self.input_arguments = self.input_arguments.replace("%p", str(self.proxy.listen_port))

        self.log.debug("Starting input generator")
        self.log.debug(self.input_arguments)

        time.sleep(1)
        
        self.inputer.generate(self.input_arguments)

    def _end_process_handler(self, process):
        self.log.debug("Ending process pid: %d" % process.pid)
        try:
            os.remove("/tmp/mal-%d.stderr" % process.index)
            self.log.debug("Removed /tmp/mal-%d.stderr" % process.index)
        except:
            self.log.error("Could not remove file")

    def _test_connection(self, port):
        """
        Tries connecting to the service to determine
        whether it is ready or not. 
        Returns true if the connection was established.
        """
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as test_socket:
            try:
                test_socket.connect((self.proxy.address, port))
                return True
            except:
                pass
        
    def _save_packet(self, data, index):
        if self.pcap:
            self._packet_to_pcap(data, "/tmp/mal-%d.pcap" % index)

        else:
            self._packet_to_raw(data, "/tmp/mal-%s.raw" % index)

    def _packet_to_pcap(self, packet, pcap_file):
        pass

    def _packet_to_raw(self, packet, raw_file):
        with open(raw_file, "ab") as f:
            f.write(b"===========================")
            f.write(packet)

    def _data_handler(self, index, data, processed, malformed):
        # self._save_packet(data)
        # self._save_packet(malformed_data, index)      # Temporary comment
        return malformed

    def input_generator(self):
        """
        This function is only called when input is
        specifically generated in self.MODE_GENERATE.

        It calls the input module to generate input
        and if input is returned, forwards it to the
        remote host. Otherwise, it is expected that
        the input module creates the necessary input
        on its own.
        """
        data = self.inputer.generate(self.input_arguments)

        if data:
            self._send_data(self.data_handler(bytes(data), 0))

    def _send_data(self, data):
        """
        This function simply forwards data to remote host.
        It is merely for expected behaviour with the defalt
        input module.
        """
        address = (self.address, self.listen_port)

        if self.udp:
            sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        else:
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.connect(address)

        if self.udp:
            while data:
                sock.sendto(self.data_handler(data[:65535], 0), address)
                data = data[65535:]
        else:
            sock.send(self.data_handler(data, 0))

        sock.close()

    def start(self):
        self.proxy.start()

        if self.MODE_GENERATE:
            self.scheduled_functions.append(self.input_generator)

        self.main_loop()