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