예제 #1
0
    def __init__(self, audit_file, proto, host, port, crash_detection, skip=0, start_command='TERMINATE_PID', stop_command='', 
                 procmon_port=26002, restart_interval=0, log_function=None, max_len=8192, config_options=None):
        '''
        Abstract parent class for all SIP based fuzzers. Simply updates the default
        fuzzer to include SIP specific crash detection
        '''

        # the fuzzer will put requests it sends onto this queue so they can be monitored
        # by the transaction manager
        self.request_q = Queue(0)
        AbstractFuzzer.__init__(self, audit_file, proto, host, port, 
            crash_detection, skip, start_command, stop_command, procmon_port,
            restart_interval, log_function, self.request_q, max_len, config_options)

        # queue onto which transactions to be added are put
        self.response_q = Queue(0)
        self.sip_agent = SIPAgent(self.response_q, self.request_q)
        self.ack = SIPAck()
        self.cancel = SIPCancel()
        self.options = SIPOptions()
        self.register = SIPRegister()
        self.invite = SIPInvite()
        self.curr_invite_branch = None
        
        self.curr_invite_branch = None
        self.curr_cd_branch = None

        self.local_ip = socket.gethostbyname(socket.gethostname())        

        SIPTransactionManager(self.request_q, self.response_q).start()

        if self.crash_detection > 0 and self.crash_detection < 3:
            self.options_transaction_dict = {sip_parser.r_SEND :
                                             (self.options.process,
                                               {
                                                   sip_parser.r_1XX : (None, None),                                                                             
                                                   sip_parser.r_2XX : (None, None),  
                                                   sip_parser.r_3XX : (None, None),                                                                           
                                                   sip_parser.r_4XX : (None, None),
                                                   sip_parser.r_5XX : (None, None),
                                                   sip_parser.r_6XX : (None, None),                                                                             
                                                }
                                               )
                                             }
            
            self.invite_dict = {sip_parser.r_SEND :
                             (self.invite.process,
                              {
                                  sip_parser.r_1XX :
                                  (self.cancel.process,
                                   {
                                       sip_parser.r_1XX : (None, None),
                                       sip_parser.r_2XX : (None, None),                           
                                       sip_parser.r_6XX : (self.ack.process, None),
                                       sip_parser.r_4XX : (self.ack.process, None)
                                       }
                                   )
                               }
                              )
                            }
            
            self.post_send_functions.append(self.detect_crash)

        if self.do_register:
            self.register_dict = {sip_parser.r_SEND :
                             (self.register.process,
                              {
                                  sip_parser.r_401 :
                                  (self.register.process,
                                   {
                                       sip_parser.r_2XX : (self.sip_agent.require, None),
                                       sip_parser.r_1XX : (None, None)
                                       }
                                   ),
                                  sip_parser.r_1XX : (None, None),
                                  # If a password is not required then no need for a second request
                                  sip_parser.r_2XX : (self.sip_agent.require, None),
                               }
                              )
                             }
            
            self.pre_send_functions.append(self.sip_register)
    def __init__(self,
                 audit_file,
                 proto,
                 host,
                 port,
                 crash_detection,
                 skip=0,
                 start_command='TERMINATE_PID',
                 stop_command='',
                 procmon_port=26002,
                 restart_interval=0,
                 log_function=None,
                 max_len=8192,
                 config_options=None):
        '''
        Abstract parent class for all SIP based fuzzers. Simply updates the default
        fuzzer to include SIP specific crash detection
        '''

        # the fuzzer will put requests it sends onto this queue so they can be monitored
        # by the transaction manager
        self.request_q = Queue(0)
        AbstractFuzzer.__init__(self, audit_file, proto, host, port,
                                crash_detection, skip, start_command,
                                stop_command, procmon_port, restart_interval,
                                log_function, self.request_q, max_len,
                                config_options)

        # queue onto which transactions to be added are put
        self.response_q = Queue(0)
        self.sip_agent = SIPAgent(self.response_q, self.request_q)
        self.ack = SIPAck()
        self.cancel = SIPCancel()
        self.options = SIPOptions()
        self.register = SIPRegister()
        self.invite = SIPInvite()
        self.curr_invite_branch = None

        self.curr_invite_branch = None
        self.curr_cd_branch = None

        self.local_ip = socket.gethostbyname(socket.gethostname())

        SIPTransactionManager(self.request_q, self.response_q).start()

        if self.crash_detection > 0 and self.crash_detection < 3:
            self.options_transaction_dict = {
                sip_parser.r_SEND: (self.options.process, {
                    sip_parser.r_1XX: (None, None),
                    sip_parser.r_2XX: (None, None),
                    sip_parser.r_3XX: (None, None),
                    sip_parser.r_4XX: (None, None),
                    sip_parser.r_5XX: (None, None),
                    sip_parser.r_6XX: (None, None),
                })
            }

            self.invite_dict = {
                sip_parser.r_SEND: (self.invite.process, {
                    sip_parser.r_1XX: (self.cancel.process, {
                        sip_parser.r_1XX: (None, None),
                        sip_parser.r_2XX: (None, None),
                        sip_parser.r_6XX: (self.ack.process, None),
                        sip_parser.r_4XX: (self.ack.process, None)
                    })
                })
            }

            self.post_send_functions.append(self.detect_crash)

        if self.do_register:
            self.register_dict = {
                sip_parser.r_SEND: (
                    self.register.process,
                    {
                        sip_parser.r_401: (self.register.process, {
                            sip_parser.r_2XX: (self.sip_agent.require, None),
                            sip_parser.r_1XX: (None, None)
                        }),
                        sip_parser.r_1XX: (None, None),
                        # If a password is not required then no need for a second request
                        sip_parser.r_2XX: (self.sip_agent.require, None),
                    })
            }

            self.pre_send_functions.append(self.sip_register)
예제 #3
0
class AbstractSIPFuzzer(AbstractFuzzer):
    def __init__(self, audit_file, proto, host, port, crash_detection, skip=0, start_command='TERMINATE_PID', stop_command='', 
                 procmon_port=26002, restart_interval=0, log_function=None, max_len=8192, config_options=None):
        '''
        Abstract parent class for all SIP based fuzzers. Simply updates the default
        fuzzer to include SIP specific crash detection
        '''

        # the fuzzer will put requests it sends onto this queue so they can be monitored
        # by the transaction manager
        self.request_q = Queue(0)
        AbstractFuzzer.__init__(self, audit_file, proto, host, port, 
            crash_detection, skip, start_command, stop_command, procmon_port,
            restart_interval, log_function, self.request_q, max_len, config_options)

        # queue onto which transactions to be added are put
        self.response_q = Queue(0)
        self.sip_agent = SIPAgent(self.response_q, self.request_q)
        self.ack = SIPAck()
        self.cancel = SIPCancel()
        self.options = SIPOptions()
        self.register = SIPRegister()
        self.invite = SIPInvite()
        self.curr_invite_branch = None
        
        self.curr_invite_branch = None
        self.curr_cd_branch = None

        self.local_ip = socket.gethostbyname(socket.gethostname())        

        SIPTransactionManager(self.request_q, self.response_q).start()

        if self.crash_detection > 0 and self.crash_detection < 3:
            self.options_transaction_dict = {sip_parser.r_SEND :
                                             (self.options.process,
                                               {
                                                   sip_parser.r_1XX : (None, None),                                                                             
                                                   sip_parser.r_2XX : (None, None),  
                                                   sip_parser.r_3XX : (None, None),                                                                           
                                                   sip_parser.r_4XX : (None, None),
                                                   sip_parser.r_5XX : (None, None),
                                                   sip_parser.r_6XX : (None, None),                                                                             
                                                }
                                               )
                                             }
            
            self.invite_dict = {sip_parser.r_SEND :
                             (self.invite.process,
                              {
                                  sip_parser.r_1XX :
                                  (self.cancel.process,
                                   {
                                       sip_parser.r_1XX : (None, None),
                                       sip_parser.r_2XX : (None, None),                           
                                       sip_parser.r_6XX : (self.ack.process, None),
                                       sip_parser.r_4XX : (self.ack.process, None)
                                       }
                                   )
                               }
                              )
                            }
            
            self.post_send_functions.append(self.detect_crash)

        if self.do_register:
            self.register_dict = {sip_parser.r_SEND :
                             (self.register.process,
                              {
                                  sip_parser.r_401 :
                                  (self.register.process,
                                   {
                                       sip_parser.r_2XX : (self.sip_agent.require, None),
                                       sip_parser.r_1XX : (None, None)
                                       }
                                   ),
                                  sip_parser.r_1XX : (None, None),
                                  # If a password is not required then no need for a second request
                                  sip_parser.r_2XX : (self.sip_agent.require, None),
                               }
                              )
                             }
            
            self.pre_send_functions.append(self.sip_register)
            
    def sip_register(self, sock):
        '''
        Function to register with the registrar. Sock is the socket
        provided by Sulley that the next fuzz request will be sent through
        but is not used here
        '''
       
        # only re-register every 50 requests
        if self.sent_request_count % 50 == 0:
            result, self.curr_register_branch = self.sip_agent.process_transaction(
                self.register_dict,
                {'user' : self.user,
                 'password' : self.password, 
                 'host' : self.host,
                 'port' : self.port}
                )        
        
        self.sent_request_count += 1

    def generate_unique_attributes(self, session, node, edge, sock):
        '''
        In SIP a number of fields need to be unique/random e.g branch. Without
        this some implementations freak out and don't function in a normal
        fashion, thus artificially interfering with the fuzzing process. The
        SIP rfc specifies cryptographically random values should be used but
        for our purposes the pseudo random values generated should do.
       
        @rtype: String
        @return: Returns the SIP message with the fields requiring random
        identifiers filled in correctly when possible i.e if they're not being
        fuzzed
        '''

        data = node.render()
        # branch
        if not self.curr_invite_branch:
            self.curr_invite_branch = ''.join(Random().sample(string.ascii_lowercase+string.digits, 32))
            
        data = data.replace('somebranchvalue', self.curr_invite_branch)
        data = data.replace('somefromtagvalue', self.curr_invite_branch)
        data = data.replace('somecallidvalue', self.curr_invite_branch)            

        # This stuff is new in this function and should be moved elsewhere
        # Works fine here for now though
        data = data.replace('TARGET_USER', self.target_user)
        data = data.replace('USER', self.user)
        data = data.replace('HOST', self.host)
        # For some legos we need to pass them a valid IP address
        # to begin with so they can fuzz it correctly but we still need
        # a recognisable IP to replace. Hopefully this one won't pop up
        # in a valid context. Hacky as f**k!
        data = data.replace('192.168.96.69', self.host)
        data = data.replace('192.168.99.99', self.local_ip)        
        data = data.replace('PORT', str(self.port))
        data = data.replace('LOCAL_IP', self.local_ip)
       
        return data
        
    def detect_crash(self, sock):
        '''
        This method is registered as the post-send callback with Sulley and
        attempts to detect crashes by sending an OPTIONS message to the target
        and waiting for a response. If no response is received it will notify
        the user and log the last request sent.

        @type sock: Socket
        @param sock: The socket used to send the last test case. Unused in this
        method but necessary as a parameter as it is passed to all post_send
        methods

        @todo: Change second crash detection message to something besides OPTIONS
        '''

        # if some other previous part of the session has verified the target is
        # alive then accept that
        if self.target_alive:
            return

        # To minimise the chance of a false positive two
        for x in range(2):
            res = self.send_options()
            if res != sip_agent.T_INCOMPLETE:
                self.target_alive = True
                break
            else:
                self.target_alive = False

        if not self.target_alive:
            self.log('[*] No response to OPTIONS based crash detection.')
            self.log('[*] Sleeping for 2 seconds and then attempting once more with INVITE')
            time.sleep(2)
            res = self.send_invite()
            if res != sip_agent.T_INCOMPLETE:
                self.target_alive = True            
            
        if not self.target_alive:
            self.log('\n[*] The target program has stopped responding')
            crash_log_name = self.audit_folder + '/' + \
                str(self.sess.fuzz_node.id) + '_' + \
                str(self.sess.total_mutant_index) + '.crashlog'
            crash_log = open(crash_log_name, 'w')
            crash_log.write(self.sess.fuzz_node.sent_data)
            crash_log.close()
            self.log('[*] Fuzz request logged to ' + crash_log_name)
            if self.crash_detection == 2:
                if self.using_GUI:
                    self.pause_GUI()
                else:
                    raw_input('\nPress the any key to continue\n')

    def send_options(self):
        print 'Sending options'
        res, self.curr_cd_branch = self.sip_agent.process_transaction(
            self.options_transaction_dict, {'host' : self.host,
                                            'port' : self.port,
                                            'user' : self.user,
                                            'target_user' : self.target_user}
            )

        return res

    def send_invite(self):
        print 'Sending invite'
        res, self.curr_cd_branch = self.sip_agent.process_transaction(
            self.invite_dict, {'host' : self.host,
                                'port' : self.port,
                                'user' : self.user,
                                'target_user' : self.target_user}
            )

        return res    
class AbstractSIPFuzzer(AbstractFuzzer):
    def __init__(self,
                 audit_file,
                 proto,
                 host,
                 port,
                 crash_detection,
                 skip=0,
                 start_command='TERMINATE_PID',
                 stop_command='',
                 procmon_port=26002,
                 restart_interval=0,
                 log_function=None,
                 max_len=8192,
                 config_options=None):
        '''
        Abstract parent class for all SIP based fuzzers. Simply updates the default
        fuzzer to include SIP specific crash detection
        '''

        # the fuzzer will put requests it sends onto this queue so they can be monitored
        # by the transaction manager
        self.request_q = Queue(0)
        AbstractFuzzer.__init__(self, audit_file, proto, host, port,
                                crash_detection, skip, start_command,
                                stop_command, procmon_port, restart_interval,
                                log_function, self.request_q, max_len,
                                config_options)

        # queue onto which transactions to be added are put
        self.response_q = Queue(0)
        self.sip_agent = SIPAgent(self.response_q, self.request_q)
        self.ack = SIPAck()
        self.cancel = SIPCancel()
        self.options = SIPOptions()
        self.register = SIPRegister()
        self.invite = SIPInvite()
        self.curr_invite_branch = None

        self.curr_invite_branch = None
        self.curr_cd_branch = None

        self.local_ip = socket.gethostbyname(socket.gethostname())

        SIPTransactionManager(self.request_q, self.response_q).start()

        if self.crash_detection > 0 and self.crash_detection < 3:
            self.options_transaction_dict = {
                sip_parser.r_SEND: (self.options.process, {
                    sip_parser.r_1XX: (None, None),
                    sip_parser.r_2XX: (None, None),
                    sip_parser.r_3XX: (None, None),
                    sip_parser.r_4XX: (None, None),
                    sip_parser.r_5XX: (None, None),
                    sip_parser.r_6XX: (None, None),
                })
            }

            self.invite_dict = {
                sip_parser.r_SEND: (self.invite.process, {
                    sip_parser.r_1XX: (self.cancel.process, {
                        sip_parser.r_1XX: (None, None),
                        sip_parser.r_2XX: (None, None),
                        sip_parser.r_6XX: (self.ack.process, None),
                        sip_parser.r_4XX: (self.ack.process, None)
                    })
                })
            }

            self.post_send_functions.append(self.detect_crash)

        if self.do_register:
            self.register_dict = {
                sip_parser.r_SEND: (
                    self.register.process,
                    {
                        sip_parser.r_401: (self.register.process, {
                            sip_parser.r_2XX: (self.sip_agent.require, None),
                            sip_parser.r_1XX: (None, None)
                        }),
                        sip_parser.r_1XX: (None, None),
                        # If a password is not required then no need for a second request
                        sip_parser.r_2XX: (self.sip_agent.require, None),
                    })
            }

            self.pre_send_functions.append(self.sip_register)

    def sip_register(self, sock):
        '''
        Function to register with the registrar. Sock is the socket
        provided by Sulley that the next fuzz request will be sent through
        but is not used here
        '''

        # only re-register every 50 requests
        if self.sent_request_count % 50 == 0:
            result, self.curr_register_branch = self.sip_agent.process_transaction(
                self.register_dict, {
                    'user': self.user,
                    'password': self.password,
                    'host': self.host,
                    'port': self.port
                })

        self.sent_request_count += 1

    def generate_unique_attributes(self, session, node, edge, sock):
        '''
        In SIP a number of fields need to be unique/random e.g branch. Without
        this some implementations freak out and don't function in a normal
        fashion, thus artificially interfering with the fuzzing process. The
        SIP rfc specifies cryptographically random values should be used but
        for our purposes the pseudo random values generated should do.
       
        @rtype: String
        @return: Returns the SIP message with the fields requiring random
        identifiers filled in correctly when possible i.e if they're not being
        fuzzed
        '''

        data = node.render()
        # branch
        if not self.curr_invite_branch:
            self.curr_invite_branch = ''.join(Random().sample(
                string.ascii_lowercase + string.digits, 32))

        data = data.replace('somebranchvalue', self.curr_invite_branch)
        data = data.replace('somefromtagvalue', self.curr_invite_branch)
        data = data.replace('somecallidvalue', self.curr_invite_branch)

        # This stuff is new in this function and should be moved elsewhere
        # Works fine here for now though
        data = data.replace('TARGET_USER', self.target_user)
        data = data.replace('USER', self.user)
        data = data.replace('HOST', self.host)
        # For some legos we need to pass them a valid IP address
        # to begin with so they can fuzz it correctly but we still need
        # a recognisable IP to replace. Hopefully this one won't pop up
        # in a valid context. Hacky as f**k!
        data = data.replace('192.168.96.69', self.host)
        data = data.replace('192.168.99.99', self.local_ip)
        data = data.replace('PORT', str(self.port))
        data = data.replace('LOCAL_IP', self.local_ip)

        return data

    def detect_crash(self, sock):
        '''
        This method is registered as the post-send callback with Sulley and
        attempts to detect crashes by sending an OPTIONS message to the target
        and waiting for a response. If no response is received it will notify
        the user and log the last request sent.

        @type sock: Socket
        @param sock: The socket used to send the last test case. Unused in this
        method but necessary as a parameter as it is passed to all post_send
        methods

        @todo: Change second crash detection message to something besides OPTIONS
        '''

        # if some other previous part of the session has verified the target is
        # alive then accept that
        if self.target_alive:
            return

        # To minimise the chance of a false positive two
        for x in range(2):
            res = self.send_options()
            if res != sip_agent.T_INCOMPLETE:
                self.target_alive = True
                break
            else:
                self.target_alive = False

        if not self.target_alive:
            self.log('[*] No response to OPTIONS based crash detection.')
            self.log(
                '[*] Sleeping for 2 seconds and then attempting once more with INVITE'
            )
            time.sleep(2)
            res = self.send_invite()
            if res != sip_agent.T_INCOMPLETE:
                self.target_alive = True

        if not self.target_alive:
            self.log('\n[*] The target program has stopped responding')
            crash_log_name = self.audit_folder + '/' + \
                str(self.sess.fuzz_node.id) + '_' + \
                str(self.sess.total_mutant_index) + '.crashlog'
            crash_log = open(crash_log_name, 'w')
            crash_log.write(self.sess.fuzz_node.sent_data)
            crash_log.close()
            self.log('[*] Fuzz request logged to ' + crash_log_name)
            if self.crash_detection == 2:
                if self.using_GUI:
                    self.pause_GUI()
                else:
                    raw_input('\nPress the any key to continue\n')

    def send_options(self):
        print 'Sending options'
        res, self.curr_cd_branch = self.sip_agent.process_transaction(
            self.options_transaction_dict, {
                'host': self.host,
                'port': self.port,
                'user': self.user,
                'target_user': self.target_user
            })

        return res

    def send_invite(self):
        print 'Sending invite'
        res, self.curr_cd_branch = self.sip_agent.process_transaction(
            self.invite_dict, {
                'host': self.host,
                'port': self.port,
                'user': self.user,
                'target_user': self.target_user
            })

        return res