def main(cls): """ Main fuzzing routine. :return: """ args = cls.parse_args() conf = cls.parse_config() logger = cls.logger(levels[args.log_level], "tPortmapd.fuzz", "./session.log") victim = args.target_addr port = args.target_port version = args.target_version vmrun = conf.get("VMWARE", "vmrun") vmx = conf.get("VMWARE", "vm_path") snapshot_name = conf.get("VMWARE", "snapshot") web_port = conf.getint("KITTY", "web_port") to_log = "Started VxWorks {}.x fuzzing session\n".format(version) to_log += "Target:\n\tip address: {}\n\tport: {}\n".format(victim, port) to_log += "VM: {}\nsnapshot: {}\n".format(vmx, snapshot_name) logger.info(to_log) # Define target target = TcpTarget("tPortmapd", logger=logger, host=victim, port=port, timeout=2) # Define the controller controller = VmWareController(name="VMWare Controller", logger=logger, vmrun_path=vmrun, vmx_path=vmx, snap_name=snapshot_name, target_addr=victim, target_port=port) target.set_controller(controller) # Define the monitor monitor = VxWorksProcessMonitor(name="VxWorks Process Monitor", logger=logger, target_addr=victim, target_version=version) target.add_monitor(monitor) # Define the model model = GraphModel() model.connect(portmap_proc_null) # Define the fuzzing session fuzzer = ServerFuzzer(name="PortmapFuzzer", logger=logger) fuzzer.set_interface(WebInterface(port=web_port)) fuzzer.set_model(model) fuzzer.set_target(target) fuzzer.set_delay_between_tests(0) # Start! try: fuzzer.start() except KeyboardInterrupt: logger.info("Session interrupted by user...") fuzzer.stop() return 1 except Exception as exc: logger.error(exc) fuzzer.stop() return -1
def mod(ics_ip): print ">>>>> ICS FUZZING MODULE <<<<<\n" # 定义目标Fuzz对象的IP地址 TARGET_IP = ics_ip # 定义目标Fuzz对象的通讯端口 TARGET_PORT = 502 # 定义随机数种子 RANDSEED = int(RandShort()) # 根据ISF中Modbus-tcp协议的数据结构构造测试数据包,下面例子中将使用RandShort对请求的地址及bit位长度进行测试 write_coils_request_packet = ModbusHeaderRequest( func_code=0x05) / WriteSingleCoilRequest(ReferenceNumber=RandShort(), Value=RandShort()) # 使用ScapyField直接将Scapy的数据包结构应用于Kitty框架中 write_coils_request_packet_template = Template( name='Write Coils Request', fields=[ ScapyField( write_coils_request_packet, name='wrire_coils_request_packet', # 定义这个Field的名字,用于在报告中显示 fuzzable=True, # 定义这个Field是否需要Fuzz seed=RANDSEED, # 定义用于变异的随机数 fuzz_count=2000 # 这个数据结构的fuzz次数 ), ]) # 使用GraphModel进行Fuzz model = GraphModel() # 在使用GraphModel中注册第一个节点,由于Modbus的Read Coils请求是单次的请求/回答形式,因此这里只要注册简单的一个节点即可 model.connect(write_coils_request_packet_template) # 定义一个目标Target, 设置IP、端口及连接超时时间 modbus_target = TcpTarget(name='modbus target', host=TARGET_IP, port=TARGET_PORT, timeout=2) # 定义是需要等待Target返回响应,如果设置为True Target不返回数据包则会被识别成异常进行记录。 modbus_target.set_expect_response(True) # 定义使用ServerFuzzer的方式进行Fuzz测试 fuzzer = ServerFuzzer() # 定义fuzzer使用的交互界面为web界面 fuzzer.set_interface(WebInterface(port=26001)) # 在fuzzer中定义使用GraphModel fuzzer.set_model(model) # 在fuzzer中定义target为modbus_target fuzzer.set_target(modbus_target) # 定义每个测试用例发送之间的延迟 fuzzer.set_delay_between_tests(0.1) # 开始执行Fuzz fuzzer.start()
def fuzzing(host, port, template): # Define target target = TcpTarget('HTTP', host, int(port), timeout=1) target.set_expect_response(True) # target.add_monitor(monitor) # Define model model = GraphModel() model.connect(template) # Define fuzzer fuzzer = ServerFuzzer() fuzzer.set_interface(WebInterface(port=4445)) fuzzer.set_delay_between_tests(0.2) # Run fuzzer session_name = '%s.sqlite' % time.ctime().replace(' ', '_') sessions_dbs = os.path.join('/tmp', 'sessions', session_name) fuzzer.set_session_file(sessions_dbs) fuzzer.set_store_all_reports('reports') fuzzer.set_target(target) fuzzer.set_model(model) fuzzer.start() fuzzer.stop()
def fuzzing(host, port, template): # Define target monitor = GdbServerMonitor( name='GdbServerMonitor', gdb_path='gdb-multiarch', host=host, port=2222, signals=[signal.SIGSEGV, signal.SIGILL, signal.SIGKILL, signal.SIGTERM] ) target = TcpTarget('upnp', host, int(port), timeout=1) target.set_expect_response(True) target.add_monitor(monitor) # Define model model = GraphModel() model.connect(template) # Define fuzzer fuzzer = ServerFuzzer() fuzzer.set_interface(WebInterface(port=4445)) fuzzer.set_delay_between_tests(0.2) # Run fuzzer fuzzer.set_session_file('sessions/%s.sqlite' % time.ctime().replace(' ', '_')) fuzzer.set_store_all_reports('reports') fuzzer.set_target(target) fuzzer.set_model(model) fuzzer.start() fuzzer.stop()
class TestServerFuzzer(unittest.TestCase): def setUp(self): self.logger = get_test_logger() self.logger.debug('TESTING METHOD: %s', self._testMethodName) self.t_str = Template(name='simple_str_template', fields=[String(name='str1', value='kitty')]) self.t_int = Template(name='simple_int_template', fields=[UInt32(name='int1', value=0x1234)]) self.fuzzer = None self.prepare() def tearDown(self): if self.fuzzer: self.fuzzer.stop() if self.session_file_name: if os.path.exists(self.session_file_name): os.remove(self.session_file_name) def new_model(self): model = GraphModel() model.logger = self.logger model.connect( Template(name='simple_str_template', fields=[String(name='str1', value='kitty')])) return model def prepare(self): self.start_index = 10 self.end_index = 20 self.delay_duration = 0 self.session_file_name = None self.interface = EmptyInterface() self.model = self.new_model() self.target = ServerTargetMock({}, logger=self.logger) self.fuzzer = ServerFuzzer(name="TestServerFuzzer", logger=self.logger) self.fuzzer.set_interface(self.interface) self.fuzzer.set_model(self.model) self.fuzzer.set_target(self.target) self.fuzzer.set_range(self.start_index, self.end_index) self.fuzzer.set_delay_between_tests(self.delay_duration) def testRaisesExceptionWhenStartedWithoutModel(self): self.fuzzer.set_model(None) self.assertRaises(AssertionError, self.fuzzer.start) self.fuzzer = None def testRaisesExceptionWhenStartedWithoutTarget(self): self.fuzzer.set_target(None) self.assertRaises(AssertionError, self.fuzzer.start) self.fuzzer = None def testRaisesExceptionWhenStartedWithoutInterface(self): self.fuzzer.set_interface(None) self.assertRaises(AssertionError, self.fuzzer.start) self.fuzzer = None def testCommandLineArgumentDelay(self): self.delay_duration = 0.1 cmd_line = '--delay=%s' % self.delay_duration self.fuzzer = ServerFuzzer(name="TestServerFuzzer", logger=self.logger, option_line=cmd_line) self.fuzzer.set_interface(self.interface) self.fuzzer.set_model(self.model) self.fuzzer.set_target(self.target) self.fuzzer.set_range(self.start_index, self.end_index) self.assertEqual(self.delay_duration, self.fuzzer.config.delay_secs) start_time = time.time() self.fuzzer.start() end_time = time.time() expected_runtime = self.delay_duration * (self.end_index - self.start_index + 1) actual_runtime = end_time - start_time self.assertAlmostEqual(int(actual_runtime), int(expected_runtime)) def testCommandLineArgumentSession(self): self.session_file_name = 'mysession.sqlite' cmd_line = '--session=%s' % self.session_file_name self.fuzzer = ServerFuzzer(name="TestServerFuzzer", logger=self.logger, option_line=cmd_line) self.fuzzer.set_interface(self.interface) self.fuzzer.set_model(self.model) self.fuzzer.set_target(self.target) self.fuzzer.set_delay_between_tests(self.delay_duration) self.fuzzer.set_range(self.start_index, self.end_index) self.assertEqual(self.session_file_name, self.fuzzer.config.session_file_name) self.fuzzer.start() def testCommandLineArgumentTestList(self): cmd_line = '--test-list=%s' % (','.join(str(i) for i in [1, 3, 5, 7])) self.fuzzer = ServerFuzzer(name='TestServerFuzzer', logger=self.logger, option_line=cmd_line) self.fuzzer.set_interface(self.interface) self.fuzzer.set_model(self.model) self.fuzzer.set_target(self.target) self.fuzzer.start() # check what tests were started by the fuzzer pre_test_list = self.target.instrument.list_get('pre_test') self.assertListEqual(pre_test_list, [-1, 1, 3, 5, 7]) def testCommandLineArgumentNoEnvTest(self): cmd_line = '--test-list=%s' % (','.join(str(i) for i in [1, 3, 5, 7])) cmd_line += ' --no-env-test' self.fuzzer = ServerFuzzer(name='TestServerFuzzer', logger=self.logger, option_line=cmd_line) self.fuzzer.set_interface(self.interface) self.fuzzer.set_model(self.model) self.fuzzer.set_target(self.target) self.fuzzer.start() # check what tests were started by the fuzzer pre_test_list = self.target.instrument.list_get('pre_test') self.assertListEqual(pre_test_list, [1, 3, 5, 7]) def testMaxFailures(self): target_config = { '1': { 'send': { "raise exception": True } }, '2': { 'send': { "raise exception": True } }, '3': { 'send': { "raise exception": True } }, } self.target = ServerTargetMock(target_config, logger=self.logger) cmd_line = '--test-list=0-10' self.fuzzer = ServerFuzzer(name='TestServerFuzzer', logger=self.logger, option_line=cmd_line) self.fuzzer.set_interface(self.interface) self.fuzzer.set_model(self.model) self.fuzzer.set_target(self.target) self.fuzzer.set_max_failures(3) self.fuzzer.start() # check what tests were started by the fuzzer pre_test_list = self.target.instrument.list_get('pre_test') self.assertListEqual(pre_test_list, [-1, 0, 1, 2, 3]) def testSessionResume(self): session_file_name = 'testSessionResume.session' try: os.remove(session_file_name) except: pass self.logger.info( 'Make the fuzzer stop after 2 tests by reaching max failures') target_config = { '1': { 'send': { "raise exception": True } }, } self.target = ServerTargetMock(target_config, logger=self.logger) cmd_line = '--test-list=0-10 --session=%s' % (session_file_name) self.fuzzer = ServerFuzzer(name='TestServerFuzzer', logger=self.logger, option_line=cmd_line) self.fuzzer.set_interface(self.interface) self.fuzzer.set_model(self.new_model()) self.fuzzer.set_target(self.target) self.fuzzer.set_max_failures(1) self.fuzzer.start() pre_test_list = self.target.instrument.list_get('pre_test') self.assertListEqual(pre_test_list, [-1, 0, 1]) self.fuzzer.stop() self.logger.info( 'Now use the same session file to run from where we have stopped') cmd_line = '--session=%s --no-env-test' % (session_file_name) self.target = ServerTargetMock({}, logger=self.logger) self.fuzzer = ServerFuzzer(name='TestServerFuzzer', logger=self.logger, option_line=cmd_line) self.fuzzer.set_interface(self.interface) self.fuzzer.set_model(self.new_model()) self.fuzzer.set_target(self.target) self.fuzzer.start() pre_test_list = self.target.instrument.list_get('pre_test') self.assertListEqual(pre_test_list, list(range(2, 11))) os.remove(session_file_name) def testRetest(self): session_file_name = 'testSessionResume.session' try: os.remove(session_file_name) except: pass self.logger.info( 'Make the fuzzer stop after 2 tests by reaching max failures') target_config = { '1': { 'send': { "raise exception": True } }, '3': { 'send': { "raise exception": True } }, '5': { 'send': { "raise exception": True } }, } self.target = ServerTargetMock(target_config, logger=self.logger) cmd_line = '--test-list=0-10 --session=%s' % (session_file_name) self.fuzzer = ServerFuzzer(name='TestServerFuzzer', logger=self.logger, option_line=cmd_line) self.fuzzer.set_interface(self.interface) self.fuzzer.set_model(self.new_model()) self.fuzzer.set_target(self.target) self.fuzzer.start() pre_test_list = self.target.instrument.list_get('pre_test') self.assertListEqual(pre_test_list, list(range(-1, 11))) self.fuzzer.stop() self.logger.info('Now use the same session file to rerun failed tests') cmd_line = '--retest=%s --no-env-test' % (session_file_name) self.target = ServerTargetMock({}, logger=self.logger) self.fuzzer = ServerFuzzer(name='TestServerFuzzer', logger=self.logger, option_line=cmd_line) self.fuzzer.set_interface(self.interface) self.fuzzer.set_model(self.new_model()) self.fuzzer.set_target(self.target) self.fuzzer.start() pre_test_list = self.target.instrument.list_get('pre_test') self.assertListEqual(pre_test_list, [1, 3, 5]) os.remove(session_file_name) def testVanilla(self): self.fuzzer.start() info = self.fuzzer._get_session_info() # reports = self.fuzzer._get_reports_manager() # self.assertEqual(len(reports), 0) self.assertEqual(info.failure_count, 0) self.assertEqual(info.current_index, None) # self.assertEqual(info.original_start_index, 10) self.assertEqual(info.start_index, self.start_index) self.assertEqual(info.end_index, self.end_index) def testStartingFromStartIndex(self): start_index = self.model.num_mutations() - 2 self.fuzzer.set_range(start_index) self.fuzzer.start() info = self.fuzzer._get_session_info() self.assertEqual(info.current_index, None) self.assertEqual(info.end_index, self.model.last_index()) def testEndingAtEndIndex(self): start_index = 0 end_index = 3 self.fuzzer.set_range(start_index, end_index) self.fuzzer.start() info = self.fuzzer._get_session_info() self.assertEqual(info.start_index, 0) self.assertEqual(info.end_index, 3) self.assertEqual(info.current_index, None) def testFullMutationRange(self): self.fuzzer.set_range() self.fuzzer.start() info = self.fuzzer._get_session_info() self.assertEqual(info.start_index, 0) self.assertEqual(info.end_index, self.model.last_index()) self.assertEqual(info.current_index, None) def _MOVE_TO_TARGET_TESTS_test_send_failure(self): config = {'12': {'send': {"raise exception": True}}} send_error_target = ServerTargetMock(config, logger=self.logger) self.fuzzer.set_target(send_error_target) self.fuzzer.start() info = self.fuzzer._get_session_info() reports = self.fuzzer._get_reports_manager() self.assertEqual(len(reports), 1) self.assertTrue(12 in reports) self.assertEqual(info.failure_count, 1) def testTestFailedWhenReportIsFailed(self): config = { '13': { 'report': { 'status': 'failed', 'reason': 'failure reason' } } } target = ServerTargetMock(config, logger=self.logger) self.fuzzer.set_target(target) self.fuzzer.start() info = self.fuzzer._get_session_info() reports = self.fuzzer.dataman.get_report_test_ids() self.assertEqual(reports, [int(x) for x in config.keys()]) self.assertEqual(info.failure_count, len(config)) def testAllFailedTestsHaveReports(self): config = { '10': { 'report': { 'status': 'failed', 'reason': 'failure reason' } }, '11': { 'report': { 'status': 'failed', 'reason': 'failure reason' } }, '12': { 'report': { 'status': 'failed', 'reason': 'failure reason' } }, '13': { 'report': { 'status': 'failed', 'reason': 'failure reason' } } } target = ServerTargetMock(config, logger=self.logger) self.fuzzer.set_target(target) self.fuzzer.start() info = self.fuzzer._get_session_info() reports = self.fuzzer.dataman.get_report_test_ids() self.assertEqual(reports, sorted([int(x) for x in config.keys()])) self.assertEqual(info.failure_count, len(config)) def testStoringAllReportsWhenStoreAllReportsIsSetToTrue(self): config = {} target = ServerTargetMock(config, logger=self.logger) self.fuzzer.set_store_all_reports(True) self.fuzzer.set_target(target) self.fuzzer.start() info = self.fuzzer._get_session_info() reports = self.fuzzer.dataman.get_report_test_ids() expected_mutation_count = self.end_index - self.start_index + 1 expected_failure_count = 0 self.assertEqual(len(reports), expected_mutation_count) self.assertEqual(info.failure_count, expected_failure_count) def testOnlyTestsInSetRangeAreExecuted(self): start_index = self.model.num_mutations() - 5 self.model.connect(self.t_str, self.t_int) expected_end_index = self.model.last_index() # expected_num_mutations = expected_end_index - start_index self.fuzzer.set_range(start_index) self.fuzzer.start() info = self.fuzzer._get_session_info() self.assertEqual(info.failure_count, 0) self.assertEqual(info.current_index, None) self.assertEqual(info.start_index, start_index) self.assertEqual(info.end_index, expected_end_index) def testCallbackIsCalledBetweenTwoNodes(self): template1 = Template(name='template1', fields=String('str1')) template2 = Template(name='template2', fields=String('str2')) self.cb_call_count = 0 def t1_t2_cb(fuzzer, edge, response): self.assertEqual(fuzzer, self.fuzzer) self.assertEqual(edge.src, template1) self.assertEqual(edge.dst, template2) self.cb_call_count += 1 model = GraphModel() model.logger = self.logger model.connect(template1) model.connect(template1, template2, t1_t2_cb) self.model = model self.fuzzer.set_model(model) self.fuzzer.set_range() self.fuzzer.start() self.assertEqual(template2.num_mutations(), self.cb_call_count) def testCorrectCallbackIsCalledForEachEdge(self): template1 = Template(name='template1', fields=String('str1')) template2 = Template(name='template2', fields=String('str2')) template3 = Template(name='template3', fields=String('str3')) self.cb2_call_count = 0 self.cb3_call_count = 0 def t1_t2_cb(fuzzer, edge, response): self.assertEqual(fuzzer, self.fuzzer) self.assertEqual(edge.src, template1) self.assertEqual(edge.dst, template2) self.cb2_call_count += 1 def t1_t3_cb(fuzzer, edge, response): self.assertEqual(fuzzer, self.fuzzer) self.assertEqual(edge.src, template1) self.assertEqual(edge.dst, template3) self.cb3_call_count += 1 model = GraphModel() model.logger = self.logger model.connect(template1) model.connect(template1, template2, t1_t2_cb) model.connect(template1, template3, t1_t3_cb) self.model = model self.fuzzer.set_model(model) self.fuzzer.set_range() self.fuzzer.start() self.assertEqual(template2.num_mutations(), self.cb2_call_count) self.assertEqual(template3.num_mutations(), self.cb3_call_count) def testCorrectCallbackIsCalledForAllEdgesInPath(self): template1 = Template(name='template1', fields=String('str1')) template2 = Template(name='template2', fields=String('str2')) template3 = Template(name='template3', fields=String('str3')) self.cb2_call_count = 0 self.cb3_call_count = 0 def t1_t2_cb(fuzzer, edge, response): self.assertEqual(fuzzer, self.fuzzer) self.assertEqual(edge.src, template1) self.assertEqual(edge.dst, template2) self.cb2_call_count += 1 def t2_t3_cb(fuzzer, edge, response): self.assertEqual(fuzzer, self.fuzzer) self.assertEqual(edge.src, template2) self.assertEqual(edge.dst, template3) self.cb3_call_count += 1 model = GraphModel() model.logger = self.logger model.connect(template1) model.connect(template1, template2, t1_t2_cb) model.connect(template2, template3, t2_t3_cb) self.model = model self.fuzzer.set_model(model) self.fuzzer.set_range() self.fuzzer.start() self.assertEqual(template2.num_mutations() + template3.num_mutations(), self.cb2_call_count) self.assertEqual(template3.num_mutations(), self.cb3_call_count)
fuzzer.logger.info('session is: %s' % resp[1:3].encode('hex')) fuzzer.target.session_data['session_id'] = resp[1:3] # Define session target target = TcpTarget(name='session_test_target', host=target_ip, port=target_port, timeout=2) # Make target expect response target.set_expect_response(True) # Define controller controller = SessionServerController(name='ServerController', host=target_ip, port=target_port) target.set_controller(controller) # Define model model = GraphModel() model.connect(get_session) model.connect(get_session, send_data, new_session_callback) # Define fuzzer fuzzer = ServerFuzzer() fuzzer.set_interface(WebInterface(port=web_port)) fuzzer.set_model(model) fuzzer.set_target(target) fuzzer.set_delay_between_tests(0.2) fuzzer.start()
class TestServerFuzzer(unittest.TestCase): def setUp(self): self.logger = get_test_logger() self.logger.debug('TESTING METHOD: %s', self._testMethodName) self.t_str = Template(name='simple_str_template', fields=[String(name='str1', value='kitty')]) self.t_int = Template(name='simple_int_template', fields=[UInt32(name='int1', value=0x1234)]) self.fuzzer = None self.prepare() def tearDown(self): if self.fuzzer: self.fuzzer.stop() if self.session_file_name: if os.path.exists(self.session_file_name): os.remove(self.session_file_name) def prepare(self): self.start_index = 10 self.end_index = 20 self.delay_duration = 0 self.session_file_name = None self.interface = EmptyInterface() self.model = GraphModel() self.model.logger = self.logger self.model.connect(self.t_str) self.target = ServerTargetMock({}, logger=self.logger) self.fuzzer = ServerFuzzer(name="TestServerFuzzer", logger=self.logger) self.fuzzer.set_interface(self.interface) self.fuzzer.set_model(self.model) self.fuzzer.set_target(self.target) self.fuzzer.set_range(self.start_index, self.end_index) self.fuzzer.set_delay_between_tests(self.delay_duration) def testRaisesExceptionWhenStartedWithoutModel(self): self.fuzzer.set_model(None) self.assertRaises(AssertionError, self.fuzzer.start) self.fuzzer = None def testRaisesExceptionWhenStartedWithoutTarget(self): self.fuzzer.set_target(None) self.assertRaises(AssertionError, self.fuzzer.start) self.fuzzer = None def testRaisesExceptionWhenStartedWithoutInterface(self): self.fuzzer.set_interface(None) self.assertRaises(AssertionError, self.fuzzer.start) self.fuzzer = None def testCommandLineArgumentsStart(self): self.start_index = 10 cmd_line = '--start=%d' % self.start_index self.fuzzer = ServerFuzzer(name="TestServerFuzzer", logger=self.logger, option_line=cmd_line) self.fuzzer.set_interface(self.interface) self.fuzzer.set_model(self.model) self.fuzzer.set_target(self.target) self.fuzzer.set_delay_between_tests(self.delay_duration) self.fuzzer.start() info = self.fuzzer._get_session_info() self.assertEqual(info.current_index, self.model.last_index()) self.assertEqual(info.start_index, self.start_index) def testCommandLineArgumentsEnd(self): self.end_index = 10 cmd_line = '--end=%d' % self.end_index self.fuzzer = ServerFuzzer(name="TestServerFuzzer", logger=self.logger, option_line=cmd_line) self.fuzzer.set_interface(self.interface) self.fuzzer.set_model(self.model) self.fuzzer.set_target(self.target) self.fuzzer.set_delay_between_tests(self.delay_duration) self.fuzzer.start() info = self.fuzzer._get_session_info() self.assertEqual(info.start_index, 0) self.assertEqual(info.end_index, self.end_index) self.assertEqual(info.current_index, self.end_index) def testCommandLineArgumentDelay(self): self.delay_duration = 0.1 cmd_line = '--delay=%s' % self.delay_duration self.fuzzer = ServerFuzzer(name="TestServerFuzzer", logger=self.logger, option_line=cmd_line) self.fuzzer.set_interface(self.interface) self.fuzzer.set_model(self.model) self.fuzzer.set_target(self.target) self.fuzzer.set_range(self.start_index, self.end_index) self.assertEqual(self.delay_duration, self.fuzzer.config.delay_secs) start_time = time.time() self.fuzzer.start() end_time = time.time() expected_runtime = self.delay_duration * (self.end_index - self.start_index + 1) actual_runtime = end_time - start_time self.assertAlmostEqual(int(actual_runtime), int(expected_runtime)) def testCommandLineArgumentSession(self): self.session_file_name = 'mysession.sqlite' cmd_line = '--session=%s' % self.session_file_name self.fuzzer = ServerFuzzer(name="TestServerFuzzer", logger=self.logger, option_line=cmd_line) self.fuzzer.set_interface(self.interface) self.fuzzer.set_model(self.model) self.fuzzer.set_target(self.target) self.fuzzer.set_delay_between_tests(self.delay_duration) self.fuzzer.set_range(self.start_index, self.end_index) self.assertEqual(self.session_file_name, self.fuzzer.config.session_file_name) self.fuzzer.start() def testVanilla(self): self.fuzzer.start() info = self.fuzzer._get_session_info() # reports = self.fuzzer._get_reports_manager() # self.assertEqual(len(reports), 0) self.assertEqual(info.failure_count, 0) self.assertEqual(info.current_index, self.end_index) # self.assertEqual(info.original_start_index, 10) self.assertEqual(info.start_index, self.start_index) self.assertEqual(info.end_index, self.end_index) mutations_tested = info.current_index - info.start_index self.assertEqual(mutations_tested, self.end_index - self.start_index) def testStartingFromStartIndex(self): start_index = self.model.num_mutations() - 2 self.fuzzer.set_range(start_index) self.fuzzer.start() info = self.fuzzer._get_session_info() self.assertEqual(info.current_index, self.model.last_index()) self.assertEqual(info.end_index, self.model.last_index()) def testEndingAtEndIndex(self): start_index = 0 end_index = 3 self.fuzzer.set_range(start_index, end_index) self.fuzzer.start() info = self.fuzzer._get_session_info() self.assertEqual(info.start_index, 0) self.assertEqual(info.end_index, 3) self.assertEqual(info.current_index, 3) def testFullMutationRange(self): self.fuzzer.set_range() self.fuzzer.start() info = self.fuzzer._get_session_info() self.assertEqual(info.start_index, 0) self.assertEqual(info.end_index, self.model.last_index()) self.assertEqual(info.current_index, self.model.last_index()) def _MOVE_TO_TARGET_TESTS_test_send_failure(self): config = { '12': { 'send': {"raise exception": True} } } send_error_target = ServerTargetMock(config, logger=self.logger) self.fuzzer.set_target(send_error_target) self.fuzzer.start() info = self.fuzzer._get_session_info() reports = self.fuzzer._get_reports_manager() self.assertEqual(len(reports), 1) self.assertTrue(12 in reports) self.assertEqual(info.failure_count, 1) def testTestFailedWhenReportIsFailed(self): config = { '13': { 'report': { 'status': 'failed', 'reason': 'failure reason' } } } target = ServerTargetMock(config, logger=self.logger) self.fuzzer.set_target(target) self.fuzzer.start() info = self.fuzzer._get_session_info() reports = self.fuzzer.dataman.get_report_test_ids() self.assertEqual(reports, [int(x) for x in config.keys()]) self.assertEqual(info.failure_count, len(config)) def testAllFailedTestsHaveReports(self): config = { '10': {'report': {'status': 'failed', 'reason': 'failure reason'}}, '11': {'report': {'status': 'failed', 'reason': 'failure reason'}}, '12': {'report': {'status': 'failed', 'reason': 'failure reason'}}, '13': {'report': {'status': 'failed', 'reason': 'failure reason'}} } target = ServerTargetMock(config, logger=self.logger) self.fuzzer.set_target(target) self.fuzzer.start() info = self.fuzzer._get_session_info() reports = self.fuzzer.dataman.get_report_test_ids() self.assertEqual(reports, sorted([int(x) for x in config.keys()])) self.assertEqual(info.failure_count, len(config)) def testStoringAllReportsWhenStoreAllReportsIsSetToTrue(self): config = {} target = ServerTargetMock(config, logger=self.logger) self.fuzzer.set_store_all_reports(True) self.fuzzer.set_target(target) self.fuzzer.start() info = self.fuzzer._get_session_info() reports = self.fuzzer.dataman.get_report_test_ids() expected_mutation_count = self.end_index - self.start_index + 1 expected_failure_count = 0 self.assertEqual(len(reports), expected_mutation_count) self.assertEqual(info.failure_count, expected_failure_count) def testOnlyTestsInSetRangeAreExecuted(self): start_index = self.model.num_mutations() - 5 self.model.connect(self.t_str, self.t_int) expected_end_index = self.model.last_index() expected_num_mutations = expected_end_index - start_index self.fuzzer.set_range(start_index) self.fuzzer.start() info = self.fuzzer._get_session_info() self.assertEqual(info.failure_count, 0) self.assertEqual(info.current_index, expected_end_index) mutations_tested = info.current_index - info.start_index self.assertEqual(mutations_tested, expected_num_mutations) self.assertEqual(info.start_index, start_index) self.assertEqual(info.end_index, expected_end_index) def testCallbackIsCalledBetweenTwoNodes(self): template1 = Template(name='template1', fields=String('str1')) template2 = Template(name='template2', fields=String('str2')) self.cb_call_count = 0 def t1_t2_cb(fuzzer, edge, response): self.assertEqual(fuzzer, self.fuzzer) self.assertEqual(edge.src, template1) self.assertEqual(edge.dst, template2) self.cb_call_count += 1 model = GraphModel() model.logger = self.logger model.connect(template1) model.connect(template1, template2, t1_t2_cb) self.model = model self.fuzzer.set_model(model) self.fuzzer.set_range() self.fuzzer.start() self.assertEqual(template2.num_mutations(), self.cb_call_count) def testCorrectCallbackIsCalledForEachEdge(self): template1 = Template(name='template1', fields=String('str1')) template2 = Template(name='template2', fields=String('str2')) template3 = Template(name='template3', fields=String('str3')) self.cb2_call_count = 0 self.cb3_call_count = 0 def t1_t2_cb(fuzzer, edge, response): self.assertEqual(fuzzer, self.fuzzer) self.assertEqual(edge.src, template1) self.assertEqual(edge.dst, template2) self.cb2_call_count += 1 def t1_t3_cb(fuzzer, edge, response): self.assertEqual(fuzzer, self.fuzzer) self.assertEqual(edge.src, template1) self.assertEqual(edge.dst, template3) self.cb3_call_count += 1 model = GraphModel() model.logger = self.logger model.connect(template1) model.connect(template1, template2, t1_t2_cb) model.connect(template1, template3, t1_t3_cb) self.model = model self.fuzzer.set_model(model) self.fuzzer.set_range() self.fuzzer.start() self.assertEqual(template2.num_mutations(), self.cb2_call_count) self.assertEqual(template3.num_mutations(), self.cb3_call_count) def testCorrectCallbackIsCalledForAllEdgesInPath(self): template1 = Template(name='template1', fields=String('str1')) template2 = Template(name='template2', fields=String('str2')) template3 = Template(name='template3', fields=String('str3')) self.cb2_call_count = 0 self.cb3_call_count = 0 def t1_t2_cb(fuzzer, edge, response): self.assertEqual(fuzzer, self.fuzzer) self.assertEqual(edge.src, template1) self.assertEqual(edge.dst, template2) self.cb2_call_count += 1 def t2_t3_cb(fuzzer, edge, response): self.assertEqual(fuzzer, self.fuzzer) self.assertEqual(edge.src, template2) self.assertEqual(edge.dst, template3) self.cb3_call_count += 1 model = GraphModel() model.logger = self.logger model.connect(template1) model.connect(template1, template2, t1_t2_cb) model.connect(template2, template3, t2_t3_cb) self.model = model self.fuzzer.set_model(model) self.fuzzer.set_range() self.fuzzer.start() self.assertEqual(template2.num_mutations() + template3.num_mutations(), self.cb2_call_count) self.assertEqual(template3.num_mutations(), self.cb3_call_count)
edge.src is the get_session template edge.dst is the send_data template :param resp: the response from the target """ fuzzer.logger.info("session is: %s" % resp[1:3].encode("hex")) fuzzer.target.session_data["session_id"] = resp[1:3] # Define session target target = TcpTarget(name="session_test_target", host=target_ip, port=target_port, timeout=2) # Make target expect response target.set_expect_response(True) # Define controller controller = SessionServerController(name="ServerController", host=target_ip, port=target_port) target.set_controller(controller) # Define model model = GraphModel() model.connect(get_session) model.connect(get_session, send_data, new_session_callback) # Define fuzzer fuzzer = ServerFuzzer() fuzzer.set_interface(WebInterface(port=web_port)) fuzzer.set_model(model) fuzzer.set_target(target) fuzzer.set_delay_between_tests(0.2) fuzzer.start()
def s7(ics_ip): print ">>>>> ICS FUZZING MODULE <<<<<\n" # snap7 server 配置信息 TARGET_IP = ics_ip TARGET_PORT = 102 RANDSEED = int(RandShort()) SRC_TSAP = "0100".encode('hex') DST_TSAP = "0103".encode('hex') # 定义COTP CR建立连接数据包 COTP_CR_PACKET = TPKT() / COTPCR() COTP_CR_PACKET.Parameters = [COTPOption() for i in range(3)] COTP_CR_PACKET.PDUType = "CR" COTP_CR_PACKET.Parameters[0].ParameterCode = "tpdu-size" COTP_CR_PACKET.Parameters[0].Parameter = "\x0a" COTP_CR_PACKET.Parameters[1].ParameterCode = "src-tsap" COTP_CR_PACKET.Parameters[2].ParameterCode = "dst-tsap" COTP_CR_PACKET.Parameters[1].Parameter = SRC_TSAP COTP_CR_PACKET.Parameters[2].Parameter = DST_TSAP # 因为是建立连接使用,因此fuzzable参数需要设置为False避免数据包被变异破坏 COTP_CR_TEMPLATE = Template(name='cotp cr template', fields=[ ScapyField(COTP_CR_PACKET, name='cotp cr', fuzzable=False), ]) # 定义通讯参数配置数据结构 SETUP_COMM_PARAMETER_PACKET = TPKT() / COTPDT(EOT=1) / S7Header( ROSCTR="Job", Parameters=S7SetConParameter()) SETUP_COMM_PARAMETER_TEMPLATE = Template( name='setup comm template', fields=[ ScapyField(SETUP_COMM_PARAMETER_PACKET, name='setup comm', fuzzable=False), ]) # 定义需要Fuzzing的数据包结构, 下面例子中将使用RandShort对请求的SZLId及SZLIndex值进行变异测试 READ_SZL_PACKET = TPKT() / COTPDT(EOT=1) / S7Header( ROSCTR="UserData", Parameters=S7ReadSZLParameterReq(), Data=S7ReadSZLDataReq(SZLId=RandShort(), SZLIndex=RandShort())) # 定义READ_SZL_TEMPLATE为可以进行变异的结构,fuzzing的次数为1000次 READ_SZL_TEMPLATE = Template(name='read szl template', fields=[ ScapyField(READ_SZL_PACKET, name='read szl', fuzzable=True, fuzz_count=1000), ]) # 使用GraphModel进行Fuzz model = GraphModel() # 在使用GraphModel中注册第一个节点, 首先发送COTP_CR请求。 model.connect(COTP_CR_TEMPLATE) # 在使用GraphModel中注册第二个节点, 在发送完COTP_CR后发送SETUP_COMM_PARAMETER请求 model.connect(COTP_CR_TEMPLATE, SETUP_COMM_PARAMETER_TEMPLATE) # 在使用GraphModel中注册第三个节点, 在发送完SETUP_COMM_PARAMETER后发送READ_SZL请求 model.connect(SETUP_COMM_PARAMETER_TEMPLATE, READ_SZL_TEMPLATE) # define target s7comm_target = TcpTarget(name='s7comm target', host=TARGET_IP, port=TARGET_PORT, timeout=2) # 定义是需要等待Target返回响应,如果设置为True Target不返回数据包则会被识别成异常进行记录 s7comm_target.set_expect_response(True) # 定义使用基础的ServerFuzzer进行Fuzz测试 fuzzer = ServerFuzzer() # 定义fuzzer使用的交互界面为web界面 fuzzer.set_interface(WebInterface(port=26001)) # 在fuzzer中定义使用GraphModel fuzzer.set_model(model) # 在fuzzer中定义target为s7comm_target fuzzer.set_target(s7comm_target) # 定义每个测试用例发送之间的延迟 fuzzer.set_delay_between_tests(0.1) # 开始执行Fuzz fuzzer.start()
class TestServerFuzzer(unittest.TestCase): def setUp(self): self.logger = get_test_logger() self.logger.debug('TESTING METHOD: %s', self._testMethodName) self.t_str = Template(name='simple_str_template', fields=[String(name='str1', value='kitty')]) self.t_int = Template(name='simple_int_template', fields=[UInt32(name='int1', value=0x1234)]) self.fuzzer = None self.prepare() def tearDown(self): if self.fuzzer: self.fuzzer.stop() if self.session_file_name: if os.path.exists(self.session_file_name): os.remove(self.session_file_name) def new_model(self): model = GraphModel() model.logger = self.logger model.connect( Template(name='simple_str_template', fields=[String(name='str1', value='kitty')]) ) return model def prepare(self): self.start_index = 10 self.end_index = 20 self.delay_duration = 0 self.session_file_name = None self.interface = EmptyInterface() self.model = self.new_model() self.target = ServerTargetMock({}, logger=self.logger) self.fuzzer = ServerFuzzer(name="TestServerFuzzer", logger=self.logger) self.fuzzer.set_interface(self.interface) self.fuzzer.set_model(self.model) self.fuzzer.set_target(self.target) self.fuzzer.set_range(self.start_index, self.end_index) self.fuzzer.set_delay_between_tests(self.delay_duration) def testRaisesExceptionWhenStartedWithoutModel(self): self.fuzzer.set_model(None) self.assertRaises(AssertionError, self.fuzzer.start) self.fuzzer = None def testRaisesExceptionWhenStartedWithoutTarget(self): self.fuzzer.set_target(None) self.assertRaises(AssertionError, self.fuzzer.start) self.fuzzer = None def testRaisesExceptionWhenStartedWithoutInterface(self): self.fuzzer.set_interface(None) self.assertRaises(AssertionError, self.fuzzer.start) self.fuzzer = None def testCommandLineArgumentDelay(self): self.delay_duration = 0.1 cmd_line = '--delay=%s' % self.delay_duration self.fuzzer = ServerFuzzer(name="TestServerFuzzer", logger=self.logger, option_line=cmd_line) self.fuzzer.set_interface(self.interface) self.fuzzer.set_model(self.model) self.fuzzer.set_target(self.target) self.fuzzer.set_range(self.start_index, self.end_index) self.assertEqual(self.delay_duration, self.fuzzer.config.delay_secs) start_time = time.time() self.fuzzer.start() end_time = time.time() expected_runtime = self.delay_duration * (self.end_index - self.start_index + 1) actual_runtime = end_time - start_time self.assertAlmostEqual(int(actual_runtime), int(expected_runtime)) def testCommandLineArgumentSession(self): self.session_file_name = 'mysession.sqlite' cmd_line = '--session=%s' % self.session_file_name self.fuzzer = ServerFuzzer(name="TestServerFuzzer", logger=self.logger, option_line=cmd_line) self.fuzzer.set_interface(self.interface) self.fuzzer.set_model(self.model) self.fuzzer.set_target(self.target) self.fuzzer.set_delay_between_tests(self.delay_duration) self.fuzzer.set_range(self.start_index, self.end_index) self.assertEqual(self.session_file_name, self.fuzzer.config.session_file_name) self.fuzzer.start() def testCommandLineArgumentTestList(self): cmd_line = '--test-list=%s' % (','.join(str(i) for i in [1, 3, 5, 7])) self.fuzzer = ServerFuzzer(name='TestServerFuzzer', logger=self.logger, option_line=cmd_line) self.fuzzer.set_interface(self.interface) self.fuzzer.set_model(self.model) self.fuzzer.set_target(self.target) self.fuzzer.start() # check what tests were started by the fuzzer pre_test_list = self.target.instrument.list_get('pre_test') self.assertListEqual(pre_test_list, [-1, 1, 3, 5, 7]) def testCommandLineArgumentNoEnvTest(self): cmd_line = '--test-list=%s' % (','.join(str(i) for i in [1, 3, 5, 7])) cmd_line += ' --no-env-test' self.fuzzer = ServerFuzzer(name='TestServerFuzzer', logger=self.logger, option_line=cmd_line) self.fuzzer.set_interface(self.interface) self.fuzzer.set_model(self.model) self.fuzzer.set_target(self.target) self.fuzzer.start() # check what tests were started by the fuzzer pre_test_list = self.target.instrument.list_get('pre_test') self.assertListEqual(pre_test_list, [1, 3, 5, 7]) def testMaxFailures(self): target_config = { '1': {'send': {"raise exception": True}}, '2': {'send': {"raise exception": True}}, '3': {'send': {"raise exception": True}}, } self.target = ServerTargetMock(target_config, logger=self.logger) cmd_line = '--test-list=0-10' self.fuzzer = ServerFuzzer(name='TestServerFuzzer', logger=self.logger, option_line=cmd_line) self.fuzzer.set_interface(self.interface) self.fuzzer.set_model(self.model) self.fuzzer.set_target(self.target) self.fuzzer.set_max_failures(3) self.fuzzer.start() # check what tests were started by the fuzzer pre_test_list = self.target.instrument.list_get('pre_test') self.assertListEqual(pre_test_list, [-1, 0, 1, 2, 3]) def testSessionResume(self): session_file_name = 'testSessionResume.session' try: os.remove(session_file_name) except: pass self.logger.info('Make the fuzzer stop after 2 tests by reaching max failures') target_config = { '1': {'send': {"raise exception": True}}, } self.target = ServerTargetMock(target_config, logger=self.logger) cmd_line = '--test-list=0-10 --session=%s' % (session_file_name) self.fuzzer = ServerFuzzer(name='TestServerFuzzer', logger=self.logger, option_line=cmd_line) self.fuzzer.set_interface(self.interface) self.fuzzer.set_model(self.new_model()) self.fuzzer.set_target(self.target) self.fuzzer.set_max_failures(1) self.fuzzer.start() pre_test_list = self.target.instrument.list_get('pre_test') self.assertListEqual(pre_test_list, [-1, 0, 1]) self.fuzzer.stop() self.logger.info('Now use the same session file to run from where we have stopped') cmd_line = '--session=%s --no-env-test' % (session_file_name) self.target = ServerTargetMock({}, logger=self.logger) self.fuzzer = ServerFuzzer(name='TestServerFuzzer', logger=self.logger, option_line=cmd_line) self.fuzzer.set_interface(self.interface) self.fuzzer.set_model(self.new_model()) self.fuzzer.set_target(self.target) self.fuzzer.start() pre_test_list = self.target.instrument.list_get('pre_test') self.assertListEqual(pre_test_list, range(2, 11)) os.remove(session_file_name) def testRetest(self): session_file_name = 'testSessionResume.session' try: os.remove(session_file_name) except: pass self.logger.info('Make the fuzzer stop after 2 tests by reaching max failures') target_config = { '1': {'send': {"raise exception": True}}, '3': {'send': {"raise exception": True}}, '5': {'send': {"raise exception": True}}, } self.target = ServerTargetMock(target_config, logger=self.logger) cmd_line = '--test-list=0-10 --session=%s' % (session_file_name) self.fuzzer = ServerFuzzer(name='TestServerFuzzer', logger=self.logger, option_line=cmd_line) self.fuzzer.set_interface(self.interface) self.fuzzer.set_model(self.new_model()) self.fuzzer.set_target(self.target) self.fuzzer.start() pre_test_list = self.target.instrument.list_get('pre_test') self.assertListEqual(pre_test_list, range(-1, 11)) self.fuzzer.stop() self.logger.info('Now use the same session file to rerun failed tests') cmd_line = '--retest=%s --no-env-test' % (session_file_name) self.target = ServerTargetMock({}, logger=self.logger) self.fuzzer = ServerFuzzer(name='TestServerFuzzer', logger=self.logger, option_line=cmd_line) self.fuzzer.set_interface(self.interface) self.fuzzer.set_model(self.new_model()) self.fuzzer.set_target(self.target) self.fuzzer.start() pre_test_list = self.target.instrument.list_get('pre_test') self.assertListEqual(pre_test_list, [1, 3, 5]) os.remove(session_file_name) def testVanilla(self): self.fuzzer.start() info = self.fuzzer._get_session_info() # reports = self.fuzzer._get_reports_manager() # self.assertEqual(len(reports), 0) self.assertEqual(info.failure_count, 0) self.assertEqual(info.current_index, None) # self.assertEqual(info.original_start_index, 10) self.assertEqual(info.start_index, self.start_index) self.assertEqual(info.end_index, self.end_index) def testStartingFromStartIndex(self): start_index = self.model.num_mutations() - 2 self.fuzzer.set_range(start_index) self.fuzzer.start() info = self.fuzzer._get_session_info() self.assertEqual(info.current_index, None) self.assertEqual(info.end_index, self.model.last_index()) def testEndingAtEndIndex(self): start_index = 0 end_index = 3 self.fuzzer.set_range(start_index, end_index) self.fuzzer.start() info = self.fuzzer._get_session_info() self.assertEqual(info.start_index, 0) self.assertEqual(info.end_index, 3) self.assertEqual(info.current_index, None) def testFullMutationRange(self): self.fuzzer.set_range() self.fuzzer.start() info = self.fuzzer._get_session_info() self.assertEqual(info.start_index, 0) self.assertEqual(info.end_index, self.model.last_index()) self.assertEqual(info.current_index, None) def _MOVE_TO_TARGET_TESTS_test_send_failure(self): config = { '12': { 'send': {"raise exception": True} } } send_error_target = ServerTargetMock(config, logger=self.logger) self.fuzzer.set_target(send_error_target) self.fuzzer.start() info = self.fuzzer._get_session_info() reports = self.fuzzer._get_reports_manager() self.assertEqual(len(reports), 1) self.assertTrue(12 in reports) self.assertEqual(info.failure_count, 1) def testTestFailedWhenReportIsFailed(self): config = { '13': { 'report': { 'status': 'failed', 'reason': 'failure reason' } } } target = ServerTargetMock(config, logger=self.logger) self.fuzzer.set_target(target) self.fuzzer.start() info = self.fuzzer._get_session_info() reports = self.fuzzer.dataman.get_report_test_ids() self.assertEqual(reports, [int(x) for x in config.keys()]) self.assertEqual(info.failure_count, len(config)) def testAllFailedTestsHaveReports(self): config = { '10': {'report': {'status': 'failed', 'reason': 'failure reason'}}, '11': {'report': {'status': 'failed', 'reason': 'failure reason'}}, '12': {'report': {'status': 'failed', 'reason': 'failure reason'}}, '13': {'report': {'status': 'failed', 'reason': 'failure reason'}} } target = ServerTargetMock(config, logger=self.logger) self.fuzzer.set_target(target) self.fuzzer.start() info = self.fuzzer._get_session_info() reports = self.fuzzer.dataman.get_report_test_ids() self.assertEqual(reports, sorted([int(x) for x in config.keys()])) self.assertEqual(info.failure_count, len(config)) def testStoringAllReportsWhenStoreAllReportsIsSetToTrue(self): config = {} target = ServerTargetMock(config, logger=self.logger) self.fuzzer.set_store_all_reports(True) self.fuzzer.set_target(target) self.fuzzer.start() info = self.fuzzer._get_session_info() reports = self.fuzzer.dataman.get_report_test_ids() expected_mutation_count = self.end_index - self.start_index + 1 expected_failure_count = 0 self.assertEqual(len(reports), expected_mutation_count) self.assertEqual(info.failure_count, expected_failure_count) def testOnlyTestsInSetRangeAreExecuted(self): start_index = self.model.num_mutations() - 5 self.model.connect(self.t_str, self.t_int) expected_end_index = self.model.last_index() expected_num_mutations = expected_end_index - start_index self.fuzzer.set_range(start_index) self.fuzzer.start() info = self.fuzzer._get_session_info() self.assertEqual(info.failure_count, 0) self.assertEqual(info.current_index, None) self.assertEqual(info.start_index, start_index) self.assertEqual(info.end_index, expected_end_index) def testCallbackIsCalledBetweenTwoNodes(self): template1 = Template(name='template1', fields=String('str1')) template2 = Template(name='template2', fields=String('str2')) self.cb_call_count = 0 def t1_t2_cb(fuzzer, edge, response): self.assertEqual(fuzzer, self.fuzzer) self.assertEqual(edge.src, template1) self.assertEqual(edge.dst, template2) self.cb_call_count += 1 model = GraphModel() model.logger = self.logger model.connect(template1) model.connect(template1, template2, t1_t2_cb) self.model = model self.fuzzer.set_model(model) self.fuzzer.set_range() self.fuzzer.start() self.assertEqual(template2.num_mutations(), self.cb_call_count) def testCorrectCallbackIsCalledForEachEdge(self): template1 = Template(name='template1', fields=String('str1')) template2 = Template(name='template2', fields=String('str2')) template3 = Template(name='template3', fields=String('str3')) self.cb2_call_count = 0 self.cb3_call_count = 0 def t1_t2_cb(fuzzer, edge, response): self.assertEqual(fuzzer, self.fuzzer) self.assertEqual(edge.src, template1) self.assertEqual(edge.dst, template2) self.cb2_call_count += 1 def t1_t3_cb(fuzzer, edge, response): self.assertEqual(fuzzer, self.fuzzer) self.assertEqual(edge.src, template1) self.assertEqual(edge.dst, template3) self.cb3_call_count += 1 model = GraphModel() model.logger = self.logger model.connect(template1) model.connect(template1, template2, t1_t2_cb) model.connect(template1, template3, t1_t3_cb) self.model = model self.fuzzer.set_model(model) self.fuzzer.set_range() self.fuzzer.start() self.assertEqual(template2.num_mutations(), self.cb2_call_count) self.assertEqual(template3.num_mutations(), self.cb3_call_count) def testCorrectCallbackIsCalledForAllEdgesInPath(self): template1 = Template(name='template1', fields=String('str1')) template2 = Template(name='template2', fields=String('str2')) template3 = Template(name='template3', fields=String('str3')) self.cb2_call_count = 0 self.cb3_call_count = 0 def t1_t2_cb(fuzzer, edge, response): self.assertEqual(fuzzer, self.fuzzer) self.assertEqual(edge.src, template1) self.assertEqual(edge.dst, template2) self.cb2_call_count += 1 def t2_t3_cb(fuzzer, edge, response): self.assertEqual(fuzzer, self.fuzzer) self.assertEqual(edge.src, template2) self.assertEqual(edge.dst, template3) self.cb3_call_count += 1 model = GraphModel() model.logger = self.logger model.connect(template1) model.connect(template1, template2, t1_t2_cb) model.connect(template2, template3, t2_t3_cb) self.model = model self.fuzzer.set_model(model) self.fuzzer.set_range() self.fuzzer.start() self.assertEqual(template2.num_mutations() + template3.num_mutations(), self.cb2_call_count) self.assertEqual(template3.num_mutations(), self.cb3_call_count)
def run_http(self) -> None: """ This method provides the HTTP GET, POST, ... , templating for the HTTP header as fields, data provided by the config, explained in the User Documentation. kitty low level field model https://kitty.readthedocs.io/en/latest/kitty.model.low_level.field.html :returns: None :rtype: None """ http_template = None # HTTP GET TEMPLATE self.logger.info( f"[{time.strftime('%H:%M:%S')}] Initiate template for HTTP GET ..." ) if self.http_get: http_template = Template( name='HTTP_GET', fields=[ # GET / HTTP/1.1 String('GET', name='method', fuzzable=False), Delimiter(' ', name='delimiter-1', fuzzable=False), String(self.http_path, name='path'), Delimiter(' ', name='delimiter-2', fuzzable=self.http_fuzz_protocol), String('HTTP', name='protocol name', fuzzable=self.http_fuzz_protocol), Delimiter('/', name='fws-1', fuzzable=self.http_fuzz_protocol), Dword(1, name='major version', encoder=ENC_INT_DEC, fuzzable=self.http_fuzz_protocol), Delimiter('.', name='dot-1', fuzzable=self.http_fuzz_protocol), Dword(1, name='minor version', encoder=ENC_INT_DEC, fuzzable=self.http_fuzz_protocol), Static('\r\n', name='EOL-1'), # User agent String('User-Agent:', name='user_agent_field', fuzzable=self.http_fuzz_protocol), Delimiter(' ', name='delimiter-3', fuzzable=self.http_fuzz_protocol), String('Fuzzer', name='user-agent_name', fuzzable=self.http_fuzz_protocol), Static('\r\n', name='EOL-2'), # Token generated by framework to support following the session if necessary. String('Fuzzer-Token:', name='fuzzer_token', fuzzable=self.http_fuzz_protocol), Delimiter(' ', name='delimiter-4', fuzzable=self.http_fuzz_protocol), String(str(self.gen_uuid), name='fuzzer_token_type', fuzzable=False), # do not fuzz token Static('\r\n', name='EOL-3'), # Accept String('Accept:', name='accept', fuzzable=self.http_fuzz_protocol), Delimiter(' ', name='delimiter-5', fuzzable=self.http_fuzz_protocol), String('*/*', name='accept_type_', fuzzable=self.http_fuzz_protocol), Static('\r\n', name='EOL-4'), # Cache-control no-cache by default String('Cache-Control:', name='cache-control', fuzzable=self.http_fuzz_protocol), Delimiter(' ', name='delimiter-6', fuzzable=self.http_fuzz_protocol), String('no-cache', name='cache_control_type', fuzzable=self.http_fuzz_protocol), Static('\r\n', name='EOL-5'), # Host, the target host String('Host:', name='host_name', fuzzable=self.http_fuzz_protocol), Delimiter(' ', name='delimiter-7', fuzzable=self.http_fuzz_protocol), String(self.target_host, name='target_host', fuzzable=False), # do not fuzz target host address! Static('\r\n', name='EOL-6'), # Connection close, do not use keep-alive it results only one mutation, than the # fuzzer will hang. String('Connection:', name='accept_encoding', fuzzable=self.http_fuzz_protocol), Delimiter(' ', name='delimiter-8', fuzzable=self.http_fuzz_protocol), String('close', name='accept_encoding_types', fuzzable=False), # do not fuzz this field! Static('\r\n', name='EOM-7'), # Content-type from config. String('Content-Type:', name='Content-Type', fuzzable=self.http_fuzz_protocol), Delimiter(' ', name='delimiter-9', fuzzable=self.http_fuzz_protocol), String(self.http_content_type, name='content_type_', fuzzable=self.http_fuzz_protocol), Static('\r\n\r\n', name='EOM-8') ]) if self.http_post_put: self.logger.info( f"[{time.strftime('%H:%M:%S')}] Initiate template for HTTP POST ..." ) http_template = Template( name='HTTP_POST', fields=[ # POST / HTTP/1.1 String('POST', name='method', fuzzable=False), Delimiter(' ', name='delimiter-1', fuzzable=False), String(self.http_path, name='path'), Delimiter(' ', name='delimiter-2', fuzzable=self.http_fuzz_protocol), String('HTTP', name='protocol name', fuzzable=self.http_fuzz_protocol), Delimiter('/', name='fws-1', fuzzable=self.http_fuzz_protocol), Dword(1, name='major version', encoder=ENC_INT_DEC, fuzzable=self.http_fuzz_protocol), Delimiter('.', name='dot-1', fuzzable=self.http_fuzz_protocol), Dword(1, name='minor version', encoder=ENC_INT_DEC, fuzzable=self.http_fuzz_protocol), Static('\r\n', name='EOL-1'), # User agent String('User-Agent:', name='user_agent_field', fuzzable=self.http_fuzz_protocol), Delimiter(' ', name='delimiter-3', fuzzable=self.http_fuzz_protocol), String('Fuzzer', name='user-agent_name', fuzzable=self.http_fuzz_protocol), Static('\r\n', name='EOL-2'), # Token generated by framework to support following the session if necessary. String('Fuzzer-Token:', name='fuzzer_token', fuzzable=self.http_fuzz_protocol), Delimiter(' ', name='delimiter-4', fuzzable=self.http_fuzz_protocol), String(str(self.gen_uuid), name='fuzzer_token_type', fuzzable=self.http_fuzz_protocol), Static('\r\n', name='EOL-3'), # Accept String('Accept:', name='accept', fuzzable=self.http_fuzz_protocol), Delimiter(' ', name='delimiter-5', fuzzable=self.http_fuzz_protocol), String('*/*', name='accept_type_', fuzzable=self.http_fuzz_protocol), Static('\r\n', name='EOL-4'), # Cache-control no-cache by default String('Cache-Control:', name='cache-control', fuzzable=self.http_fuzz_protocol), Delimiter(' ', name='delimiter-6', fuzzable=self.http_fuzz_protocol), String('no-cache', name='cache_control_type', fuzzable=self.http_fuzz_protocol), Static('\r\n', name='EOL-5'), # Host, the target host String('Host:', name='host_name', fuzzable=self.http_fuzz_protocol), Delimiter(' ', name='delimiter-7', fuzzable=self.http_fuzz_protocol), String(self.target_host, name='target_host', fuzzable=False), # do not fuzz target host address! Static('\r\n', name='EOL-6'), # Content length: obvious payload lenght. String('Content-Length:', name='content_length', fuzzable=self.http_fuzz_protocol), Delimiter(' ', name='delimiter-9', fuzzable=self.http_fuzz_protocol), String(str(len(self.http_payload)), name='content_length_len', fuzzable=False), Static('\r\n', name='EOM-8'), # Connection close, do not use keep-alive it results only one mutation, than the # fuzzer will hang. String('Connection:', name='accept_encoding', fuzzable=self.http_fuzz_protocol), Delimiter(' ', name='delimiter-8', fuzzable=self.http_fuzz_protocol), String('close', name='accept_encoding_types', fuzzable=False), # do not fuzz this field! Static('\r\n', name='EOM-7'), # Content type String('Content-Type:', name='Content-Type', fuzzable=self.http_fuzz_protocol), Delimiter(' ', name='delimiter-10', fuzzable=self.http_fuzz_protocol), String(self.http_content_type, name='content_type_', fuzzable=self.http_fuzz_protocol), Static('\n\r\n', name='EOM-9'), # Payload String(self.http_payload, name='payload'), Static('\r\n\r\n', name='EOM-10') ]) self.logger.info( f"[{time.strftime('%H:%M:%S')}] Prepare HttpTarget ...") target = HttpTarget(name='HttpTarget', host=self.target_host, port=self.target_port, max_retries=10, timeout=None) target.set_expect_response('true') self.logger.info( f"[{time.strftime('%H:%M:%S')}] Prepare HttpController ...") controller = HttpGetController('HttpGetController', host=self.target_host, port=self.target_port) target.set_controller(controller) self.logger.info( f"[{time.strftime('%H:%M:%S')}] Defining GraphModel...") model = GraphModel() model.connect(http_template) fuzzer = ServerFuzzer() fuzzer.set_interface(WebInterface(port=26001)) fuzzer.set_model(model) fuzzer.set_target(target) fuzzer.set_delay_between_tests(1) self.logger.info(f"[{time.strftime('%H:%M:%S')}] Start Fuzzer...") self.logger.info( f"[Further info are in the related Kitty log output!]") fuzzer.start() self.logger.info(f"[{time.strftime('%H:%M:%S')}] End Fuzzer Session") fuzzer.stop()
class TestServerFuzzer(unittest.TestCase): def setUp(self): self.logger = get_test_logger() self.logger.debug('TESTING METHOD: %s', self._testMethodName) self.t_str = Template(name='simple_str_template', fields=[String(name='str1', value='kitty')]) self.t_int = Template(name='simple_int_template', fields=[UInt32(name='int1', value=0x1234)]) self.fuzzer = None self.prepare() def tearDown(self): if self.fuzzer: self.fuzzer.stop() def prepare(self): self.start_index = 10 self.end_index = 20 self.delay_duration = 0 self.fuzzer = ServerFuzzer(name="TestServerFuzzer", logger=self.logger) self.interface = EmptyInterface() self.fuzzer.set_interface(self.interface) self.model = GraphModel() self.model.connect(self.t_str) self.fuzzer.set_model(self.model) self.target = TargetMock({}) self.fuzzer.set_target(self.target) self.fuzzer.set_range(self.start_index, self.end_index) self.fuzzer.set_delay_between_tests(self.delay_duration) def test_start_without_session(self): self.fuzzer.set_model(None) self.assertRaises(AssertionError, self.fuzzer.start) self.fuzzer = None def test_start_without_target(self): self.fuzzer.set_target(None) self.assertRaises(AssertionError, self.fuzzer.start) self.fuzzer = None def test_start_without_interface(self): self.fuzzer.set_interface(None) self.assertRaises(AssertionError, self.fuzzer.start) self.fuzzer = None def test_vanilla(self): self.fuzzer.start() info = self.fuzzer._get_session_info() # reports = self.fuzzer._get_reports_manager() # self.assertEqual(len(reports), 0) self.assertEqual(info.failure_count, 0) self.assertEqual(info.current_index, self.end_index) # self.assertEqual(info.original_start_index, 10) self.assertEqual(info.start_index, self.start_index) self.assertEqual(info.end_index, self.end_index) mutations_tested = info.current_index - info.start_index self.assertEqual(mutations_tested, self.end_index - self.start_index) def test_start_index(self): start_index = self.model.num_mutations() - 2 self.fuzzer.set_range(start_index) self.fuzzer.start() info = self.fuzzer._get_session_info() self.assertEqual(info.current_index, self.model.last_index()) self.assertEqual(info.end_index, self.model.last_index()) def test_end_index(self): start_index = 0 end_index = 3 self.fuzzer.set_range(start_index, end_index) self.fuzzer.start() info = self.fuzzer._get_session_info() self.assertEqual(info.start_index, 0) self.assertEqual(info.end_index, 3) self.assertEqual(info.current_index, 3) def test_full_range(self): self.fuzzer.set_range() self.fuzzer.start() info = self.fuzzer._get_session_info() self.assertEqual(info.start_index, 0) self.assertEqual(info.end_index, self.model.last_index()) self.assertEqual(info.current_index, self.model.last_index()) def _MOVE_TO_TARGET_TESTS_test_send_failure(self): config = { '12': { 'send': ["raise exception"] } } send_error_target = TargetMock(config) self.fuzzer.set_target(send_error_target) self.fuzzer.start() info = self.fuzzer._get_session_info() reports = self.fuzzer._get_reports_manager() self.assertEqual(len(reports), 1) self.assertTrue(12 in reports) self.assertEqual(info.failure_count, 1) def ___test_target_failed_is_true(self): config = { '12': { 'report': { 'failed': False } }, '13': { 'report': { 'failed': True } } } target = TargetMock(config) self.fuzzer.set_target(target) self.fuzzer.start() info = self.fuzzer._get_session_info() reports = self.fuzzer._get_reports_manager() self.assertEqual(len(reports), 1) self.assertTrue(13 in reports) self.assertEqual(info.failure_count, 1) def test_set_range(self): start_index = self.model.num_mutations() - 5 self.model.connect(self.t_str, self.t_int) expected_end_index = self.model.last_index() expected_num_mutations = expected_end_index - start_index self.fuzzer.set_range(start_index) self.fuzzer.start() info = self.fuzzer._get_session_info() self.assertEqual(info.failure_count, 0) self.assertEqual(info.current_index, expected_end_index) mutations_tested = info.current_index - info.start_index self.assertEqual(mutations_tested, expected_num_mutations) self.assertEqual(info.start_index, start_index) self.assertEqual(info.end_index, expected_end_index)
def main(cls): """ Main fuzzing routine. :return: """ args = cls.parse_args() conf = cls.parse_config() logger = cls.logger(levels[args.log_level], "tPortmapd.fuzz", "./session.log") victim = args.target_addr port = args.target_port version = args.target_version vmrun = conf.get("VMWARE", "vmrun") vmx = conf.get("VMWARE", "vm_path") snapshot_name = conf.get("VMWARE", "snapshot") web_port = conf.getint("KITTY", "web_port") to_log = "Started VxWorks {}.x fuzzing session\n".format(version) to_log += "Target:\n\tip address: {}\n\tport: {}\n".format( victim, port) to_log += "VM: {}\nsnapshot: {}\n".format(vmx, snapshot_name) logger.info(to_log) # Define target target = TcpTarget("tPortmapd", logger=logger, host=victim, port=port, timeout=2) # Define the controller controller = VmWareController(name="VMWare Controller", logger=logger, vmrun_path=vmrun, vmx_path=vmx, snap_name=snapshot_name, target_addr=victim, target_port=port) target.set_controller(controller) # Define the monitor monitor = VxWorksProcessMonitor(name="VxWorks Process Monitor", logger=logger, target_addr=victim, target_version=version) target.add_monitor(monitor) # Define the model model = GraphModel() model.connect(portmap_proc_null) # Define the fuzzing session fuzzer = ServerFuzzer(name="PortmapFuzzer", logger=logger) fuzzer.set_interface(WebInterface(port=web_port)) fuzzer.set_model(model) fuzzer.set_target(target) fuzzer.set_delay_between_tests(0) # Start! try: fuzzer.start() except KeyboardInterrupt: logger.info("Session interrupted by user...") fuzzer.stop() return 1 except Exception as exc: logger.error(exc) fuzzer.stop() return -1
def run_dns(self): """ kitty low level field model https://kitty.readthedocs.io/en/latest/kitty.model.low_level.field.html """ fields = [] counter = 0 dns_label_length = len(self.default_labels.split('.')) dns_label_list = self.default_labels.split('.') self.logger.info( f"[{time.strftime('%H:%M:%S')}] Initiate template for DNS ...") while counter < dns_label_length: fields.append( String(dns_label_list[counter], name='sub_domain_' + str(counter), max_size=10)) fields.append(Delimiter('.', name='delimiter_' + str(counter))) counter += 1 fields.append(String(self.tld, name='tld', fuzzable=False)) dns_query = Template(name='DNS_QUERY', fields=fields) """ dns_query = Template(name='DNS_QUERY', fields=[ String('r', name='sub_domain', max_size=10), Delimiter('.', name='space1"), String('rf', name='sub_domain2', max_size=10), Delimiter('.', name='space2"), String(self.tld, name='tld', fuzzable=False), ]) """ # define target, in this case this is SslTarget because of HTTPS self.logger.info( f"[{time.strftime('%H:%M:%S')}] Prepare DnsTarget ...") target = DnsTarget(name='DnsTarget', host=self.target_host, port=self.target_port, timeout=self.timeout) target.set_expect_response('true') self.logger.info( f"[{time.strftime('%H:%M:%S')}] Prepare DnsController ...") controller = DnsController('DnsController', host=self.target_host, port=self.target_port) target.set_controller(controller) # Define model self.logger.info( f"[{time.strftime('%H:%M:%S')}] Defining GraphModel...") model = GraphModel() model.connect(dns_query) self.logger.info( f"[{time.strftime('%H:%M:%S')}] Prepare Server Fuzzer ...") fuzzer = ServerFuzzer() fuzzer.set_interface(WebInterface(port=26001)) fuzzer.set_model(model) fuzzer.set_target(target) fuzzer.set_delay_between_tests(1) self.logger.info(f"[{time.strftime('%H:%M:%S')}] Start Fuzzer...") self.logger.info( f"[Further info are in the related Kitty log output!]") fuzzer.start() self.logger.info(f"[{time.strftime('%H:%M:%S')}] End Fuzzer Session") fuzzer.stop()