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 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 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 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(self): target = FuzzerTarget(name='target', base_url=self.base_url, report_dir=self.report_dir) interface = WebInterface() model = GraphModel() for template in self.templates: model.connect(template.compile_template()) fuzzer = OpenApiServerFuzzer() fuzzer.set_model(model) fuzzer.set_target(target) fuzzer.set_interface(interface) fuzzer.start()
def main(): test_name = 'GET fuzzed' get_template = Template( name=test_name, fields=[ XmlElement( name='html', element_name='html', content=[ XmlElement( name='head', element_name='head', content='<meta http-equiv="refresh" content="5; url=/">' ), XmlElement(name='body', element_name='body', content='123', fuzz_content=True), ]) ]) fuzzer = ClientFuzzer(name='Example 3 - Browser Fuzzer') fuzzer.set_interface(WebInterface(host='0.0.0.0', port=26000)) target = ClientTarget(name='BrowserTarget') # # Note: to avoid opening the process on our X server, we use another display for it # display ':2' that is specified below was started this way: # >> sudo apt-get install xvfb # >> Xvfb :2 -screen 2 1280x1024x8 # env = os.environ.copy() env['DISPLAY'] = ':2' controller = ClientProcessController('BrowserController', '/usr/bin/opera', ['http://localhost:8082/fuzzed'], process_env=env) target.set_controller(controller) target.set_mutation_server_timeout(20) model = GraphModel() model.connect(get_template) fuzzer.set_model(model) fuzzer.set_target(target) fuzzer.set_delay_between_tests(0.1) server = MyHttpServer(('localhost', 8082), MyHttpHandler, fuzzer) fuzzer.start() while True: server.handle_request()
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 main(): test_name = 'GET /fuzzed' get_template = Template(name=test_name, fields=[ XmlElement(name='html', element_name='html', content=[ XmlElement(name='head', element_name='head', content='<meta http-equiv="refresh" content="5; url=/">'), XmlElement(name='body', element_name='body', content='123', fuzz_content=True), ]) ]) fuzzer = ClientFuzzer(name='BrowserFuzzer') fuzzer.set_interface(WebInterface(host='0.0.0.0', port=26000)) target = ClientTarget(name='BrowserTarget') # # Note: to avoid opening the process on our X server, we use another display for it # display ':2' that is specified below was started this way: # >> sudo apt-get install xvfb # >> Xvfb :2 -screen 2 1280x1024x8 # env = os.environ.copy() env['DISPLAY'] = ':2' controller = ClientProcessController( 'BrowserController', '/usr/bin/opera', ['http://localhost:8082/fuzzed'], process_env=env ) target.set_controller(controller) target.set_mutation_server_timeout(20) model = GraphModel() model.connect(get_template) fuzzer.set_model(model) fuzzer.set_target(target) fuzzer.set_delay_between_tests(0.1) server = MyHttpServer(('localhost', 8082), MyHttpHandler, fuzzer) fuzzer.start() while True: server.handle_request()
def main(): test_name = 'GET fuzzed' get_template = Template(name=test_name, fields=[ XmlElement(name='html', element_name='html', content=[ XmlElement(name='head', element_name='head', content='<meta http-equiv="refresh" content="5; url=/">'), XmlElement(name='body', element_name='body', content='123', fuzz_content=True), ]) ]) fuzzer = ClientFuzzer(name='Example 2 - Browser Fuzzer (Remote)') fuzzer.set_interface(WebInterface(host='0.0.0.0', port=26000)) target = ClientTarget(name='BrowserTarget') # # Note: to avoid opening the process on our X server, we use another display for it # display ':2' that is specified below was started this way: # >> sudo apt-get install xvfb # >> Xvfb :2 -screen 2 1280x1024x8 # env = os.environ.copy() env['DISPLAY'] = ':2' controller = ClientProcessController( 'BrowserController', '/usr/bin/opera', ['http://localhost:8082/fuzzed'], process_env=env ) target.set_controller(controller) target.set_mutation_server_timeout(20) model = GraphModel() model.connect(get_template) fuzzer.set_model(model) fuzzer.set_target(target) # # only fuzz the half of the mutations, just as an example fuzzer.set_range(end_index=model.num_mutations() / 2) fuzzer.set_delay_between_tests(0.1) remote = RpcServer(host='localhost', port=26007, impl=fuzzer) remote.start()
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 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 run_proto(self) -> None: """ kitty low level field model https://kitty.readthedocs.io/en/latest/kitty.model.low_level.field.html """ js = ext_json.dict_to_JsonObject(dict(self.pb2_api[0]['Messages']), 'api') template_a = Template(name='Api', fields=js) self.logger.info(f"[{time.strftime('%H:%M:%S')}] Prepare ProtobufTarget ") target = ProtobufTarget('ProtobufTarget', host=self.target_host, port=self.target_port, max_retries=10, timeout=None, pb2_module=self.pb2_api[1]) self.logger.info(f"[{time.strftime('%H:%M:%S')}] Prepare ProtobufController ") controller = ProtobufController('ProtobufController', host=self.target_host, port=self.target_port) target.set_controller(controller) #target.set_expect_response('true') self.logger.info(f"[{time.strftime('%H:%M:%S')}] Defining GraphModel") model = GraphModel() model.connect(template_a) 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.start() self.logger.info(f"[{time.strftime('%H:%M:%S')}] Start Fuzzer") self.logger.info(f"[Further info are in the related Kitty log output!]") six.moves.input('press enter to exit') self.logger.info(f"[{time.strftime('%H:%M:%S')}] End Fuzzer Session") 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 WebInterfaceTest(BaseTestCase): def setUp(self): super(WebInterfaceTest, self).setUp(None) 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.host = '127.0.0.1' self.port = 11223 self.url = 'http://%(host)s:%(port)s' % {'host': self.host, 'port': self.port} self.prepare() def tearDown(self): if self.fuzzer: self.logger.info('still have fuzzer, stop it') self.fuzzer.stop() def prepare(self): self.start_index = 0 self.end_index = 20 self.delay_duration = 0 self.fuzzer = ServerFuzzer(name="TestServerFuzzer", logger=self.logger) self.model = GraphModel() self.model.logger = self.logger self.model.connect(self.t_str) self.fuzzer.set_model(self.model) def _webValidRequest(self, request): resp = requests.get(request) self.assertIsNotNone(resp) self.assertEqual(resp.status_code, 200) as_json = resp.json() self.assertIsNotNone(as_json) return as_json def _webGetStats(self): return self._webValidRequest('%s/api/stats.json' % self.url) def _webGetReport(self, report_id): return self._webValidRequest('%s/api/report?report_id=%s' % (self.url, report_id)) def _webGetReportList(self): resp = self._webGetStats() self.assertIn('reports', resp) reports = resp['reports'] return reports def _runFuzzerWithReportList(self, uut, report_list): config = {} for report_id in report_list: config[str(report_id)] = {'report': {'failed': True}} self.fuzzer.set_interface(uut) target = TargetMock(config, logger=self.logger) self.fuzzer.set_target(target) self.fuzzer.start() def _testStatsApiReportList(self, report_list): uut = WebInterface(host=self.host, port=self.port) report_list.sort() self._runFuzzerWithReportList(uut, report_list) actual_report_list = self._webGetReportList() self.assertListEqual(actual_report_list, report_list) def testStatsApiReportListEmpty(self): self._testStatsApiReportList([]) def testStatsApiReportListSingle(self): self._testStatsApiReportList([5]) def testStatsApiReportListMultiple(self): self._testStatsApiReportList([1, 2, 3, 4, 5]) def testStatsApiReportListAll(self): self._testStatsApiReportList([x for x in range(self.end_index)]) def _testStatsApi(self): ''' .. todo:: other stats API tests ''' pass def _testReportApiReportExists(self, report_list): for report_id in report_list: response = self._webGetReport(report_id) self.assertIn('report', response) self.assertIn('encoding', response) def _testReportApiValid(self, report_list): self._testStatsApiReportList(report_list) self._testReportApiReportExists(report_list) def testReportApiSingle(self): self._testReportApiValid([1]) def testReportApiMultiple(self): self._testReportApiValid([1, 2, 3]) def testReportApiAll(self): self._testReportApiValid([x for x in range(self.end_index)]) def _testReportApiError(self, request): self._testReportApiValid([x for x in range(self.end_index)]) resp = self._webValidRequest(request) self.assertIn('error', resp) self.assertNotIn('report', resp) def testReportApiErrorWhenNoReportId(self): self._testReportApiError('%s/api/report' % (self.url)) def testReportApiErrorWhenReportIdNotInt(self): self._testReportApiError('%s/api/report?report_id=%s' % (self.url, '%%')) def testReportApiErrorWhenNoSuchReport(self): self._testReportApiError('%s/api/report?report_id=%s' % (self.url, self.end_index + 1)) def _testPauseApi(self): ''' .. todo:: pause/resume api tests ''' pass
http_get_vX = Template(name='HTTP_GET_VX', fields = [ Method('GET', name='method', fuzzable=False), Delimiter(' ', name='space1', fuzzable=False), url.Path('api/pet/7', name='path'),# moze sqlinjection? Delimiter(' ', name='space2',fuzzable=False), String('HTTP', name='protocol name',fuzzable=False), Delimiter('/', name='fws1',fuzzable=False), Dword(1, name='major version', encoder=ENC_INT_DEC,fuzzable=False), Delimiter('.', name='dot1',fuzzable=False), Dword(1, name='minor version', encoder=ENC_INT_DEC,fuzzable=False), Static('\r\n'), Static('Host: 127.0.0.1:5000'), Static('\r\n'), Static('Connection: close'), Static('\r\n\r\n', name='eom') ]) model.connect(http_get_vX) fuzzer = ServerFuzzer() fuzzer.set_model(model) fuzzer.set_target(target) fuzzer.set_store_all_reports(True) os.remove("fuzz_session.sqlite") fuzzer.set_session_file("fuzz_session.sqlite") fuzzer.set_interface(WebInterface()) fuzzer.start() print 'finished!'
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()
Static('\r\n\r\n'), Container(name='chunk', fields=[ dict_to_JsonObject({ 'breed': 'Husky', 'id': 1, 'name': 'Bingo', 'tag': 'friendly' }) ]), Static( '\r\n\r\n', name='eom' ) # 4. The double "new lines" ("\r\n\r\n") at the end of the request ]) model.connect(http_post_json) fuzzer = ServerFuzzer() fuzzer.set_model(model) fuzzer.set_target(target) fuzzer.set_store_all_reports(True) os.remove("fuzz_session.sqlite") fuzzer.set_session_file("fuzz_session.sqlite") fuzzer.set_interface(WebInterface()) fuzzer.start() print 'finished!' # manager = fuzzer.dataman.get_session_info_manager() # session_info = manager.get_session_info() # #
from kitty.controllers import EmptyController from katnip.targets.file import FileTarget from kitty.model import GraphModel from kitty.model import String from kitty.model import Template opts = docopt.docopt(__doc__) t1 = Template(name='T1', fields=[ String('The default string', name='S1_1'), String('Another string', name='S1_2'), ]) # Writes content to files target = FileTarget('FileTarget', 'tmp/', 'fuzzed') controller = EmptyController('EmptyController') target.set_controller(controller) model = GraphModel() model.connect(t1) fuzzer = ServerFuzzer(name="Example 1 - File Generator", option_line=opts['--kitty-options']) fuzzer.set_interface(WebInterface(port=26001)) fuzzer.set_model(model) fuzzer.set_target(target) fuzzer.start() print('-------------- done with fuzzing -----------------') raw_input('press enter to exit') fuzzer.stop()
Dword(1, name='minor version', encoder=ENC_INT_DEC), Static('\r\n'), Static('Host: 127.0.0.1:5000'), Static('\r\n'), Static('Content-Type: text/plain'), Static('\r\n'), Static('Content-Length: '), Size(name='size in bytes', sized_field='chunk', length=32, encoder=ENC_INT_DEC, calc_func=lambda x: len(x) / 8), Static('\r\n\r\n'), Container(name='chunk', fields=[Static('say=Hi&to=Mom', name='form')]), Static( '\r\n\r\n', name='eom' ) # 4. The double "new lines" ("\r\n\r\n") at the end of the request ]) model.connect(http_get_vPost) fuzzer = ServerFuzzer() fuzzer.set_model(model) fuzzer.set_target(target) fuzzer.set_store_all_reports(True) os.remove("fuzz_session.sqlite") fuzzer.set_session_file("fuzz_session.sqlite") fuzzer.set_interface(WebInterface()) fuzzer.start() print 'finished!'
http_get_v1 = Template(name='HTTP_GET_V1', fields=[ String('GET', name='method', fuzzable=False), Delimiter(' ', name='space1', fuzzable=False), Delimiter('/', name='backslash'), String('somewhere', name='path', max_size=5), Delimiter(' ', name='space2'), String('HTTP', name='protocol name'), Delimiter('/', name='fws1'), Dword(1, name='major version', encoder=ENC_INT_DEC), Delimiter('.', name='dot1'), Dword(1, name='minor version', encoder=ENC_INT_DEC), Static('\r\n'), Static('Host: 127.0.0.1:5000'), Static('\r\n'), Static('Connection: close'), Static('\r\n\r\n', name='eom') ]) model.connect(http_get_v1) fuzzer = ServerFuzzer() fuzzer.set_model(model) fuzzer.set_target(target) fuzzer.set_store_all_reports(True) os.remove("fuzz_session.sqlite") fuzzer.set_session_file("fuzz_session.sqlite") fuzzer.set_interface(WebInterface()) fuzzer.start() print 'finished!'
http_get_v2 = Template(name='HTTP_GET_V2', fields=[ String('GET', name='method', fuzzable=False), Delimiter(' ', name='space1', fuzzable=False), url.Path('somewhere/else', name='path'), Delimiter(' ', name='space2'), String('HTTP', name='protocol name'), Delimiter('/', name='fws1'), Dword(1, name='major version', encoder=ENC_INT_DEC), Delimiter('.', name='dot1'), Dword(1, name='minor version', encoder=ENC_INT_DEC), Static('\r\n'), Static('Host: 127.0.0.1:5000'), Static('\r\n'), Static('Connection: close'), Static('\r\n\r\n', name='eom') ]) model.connect(http_get_v2) fuzzer = ServerFuzzer() fuzzer.set_model(model) fuzzer.set_target(target) fuzzer.set_store_all_reports(True) os.remove("fuzz_session.sqlite") fuzzer.set_session_file("fuzz_session.sqlite") fuzzer.set_interface(WebInterface()) fuzzer.start() print 'finished!'
]) ################# Actual fuzzer code ################# target = ClientTarget(name='104Target') controller = ClientProcessController( "simple_client_single", "./simple_client_fast", ["10.84.134.10"] ) target.set_controller(controller) target.set_mutation_server_timeout(20) model = GraphModel() model.connect(get_startdt) #model.connect(get_startdt, get_stopdt) model.connect(get_startdt, get_ASDU) #model.connect(get_startdt, get_testfr) #model.connect(get_testfr, get_stopdt) fuzzer = ClientFuzzer(name='104 Fuzzer') fuzzer.set_model(model) fuzzer.set_target(target) fuzzer.set_interface(WebInterface(host='0.0.0.0', port=26000)) fuzzer.set_delay_between_tests(0.1) my_stack = My104Stack("10.84.69.44") my_stack.set_fuzzer(fuzzer) fuzzer.start()
class GraphModelTests(unittest.TestCase): def setUp(self): self.logger = get_test_logger() self.logger.debug('TESTING METHOD: %s', self._testMethodName) self.model = GraphModel() self.templates = self.get_templates() self.todo = [] def get_templates(self): res = [] res.append(Template(name='t1', fields=[String('data1')])) res.append(Template(name='t2', fields=[String('data2'), UInt32(300)])) res.append(Template(name='t3', fields=[UInt32(400)])) return res def _check_sequences(self, expected_sequences): self.logger.debug('check num_mutations()') t_num_mutations = sum(s[-1].num_mutations() for s in expected_sequences) m_num_mutations = self.model.num_mutations() self.assertEqual(t_num_mutations, m_num_mutations) sequences = {} while self.model.mutate(): sequence = tuple(self.model.get_sequence()) if sequence not in sequences: sequences[sequence] = 1 else: sequences[sequence] += 1 self.assertEqual(len(sequences), len(expected_sequences)) self.logger.debug( 'check that each sequence appears the appropriate number of times') for sequence, count in sequences.items(): seq_templates = [e.dst for e in sequence] self.assertIn(seq_templates, expected_sequences) last = seq_templates[-1] last_num_mutations = last.num_mutations() self.assertEqual(count, last_num_mutations) def testSequenceSingleTemplate(self): t = self.templates[0] self.model.connect(t) expected_sequences = [[t]] self._check_sequences(expected_sequences) def testSequenceDirectPath(self): ''' root -> t1 ''' self.model.connect(self.templates[0]) for i in range(len(self.templates) - 1): self.model.connect(self.templates[i], self.templates[i + 1]) expected_sequences = list(self.templates[:i + 1] for i in range(len(self.templates))) self._check_sequences(expected_sequences) def testSequenceComplexPath(self): ''' root -> t1 root -> t1 -> t2 root -> t1 -> t3 root -> t1 -> t2 -> t3 ''' self.model.connect(self.templates[0]) self.model.connect(self.templates[0], self.templates[1]) self.model.connect(self.templates[0], self.templates[2]) self.model.connect(self.templates[1], self.templates[2]) expected_sequences = [[self.templates[0]], [self.templates[0], self.templates[1]], [self.templates[0], self.templates[2]], [ self.templates[0], self.templates[1], self.templates[2] ]] self._check_sequences(expected_sequences) def testMultiHeadPath(self): ''' root -> t1 root -> t2 root -> t3 ''' expected_sequences = [] for t in self.templates: expected_sequences.append([t]) self.model.connect(t) self._check_sequences(expected_sequences) def _check_skip(self, to_skip, expected_skipped, expected_mutated): skipped = self.model.skip(to_skip) self.assertEqual(expected_skipped, skipped) mutated = 0 while self.model.mutate(): mutated += 1 self.assertEqual(expected_mutated, mutated) def testSkipZeroSingleTemplate(self): self.model.connect(self.templates[0]) m_num_mutations = self.model.num_mutations() to_skip = 0 expected_skipped = to_skip expected_mutated = m_num_mutations self._check_skip(to_skip, expected_skipped, expected_mutated) def testSkipHalfSingleTemplate(self): self.model.connect(self.templates[0]) m_num_mutations = self.model.num_mutations() to_skip = m_num_mutations // 2 expected_skipped = to_skip expected_mutated = m_num_mutations - expected_skipped self._check_skip(to_skip, expected_skipped, expected_mutated) def testSkipExactSingleTemplate(self): self.model.connect(self.templates[0]) m_num_mutations = self.model.num_mutations() to_skip = m_num_mutations expected_skipped = to_skip expected_mutated = m_num_mutations - expected_skipped self._check_skip(to_skip, expected_skipped, expected_mutated) def testSkipTooMuchSingleTemplate(self): self.model.connect(self.templates[0]) m_num_mutations = self.model.num_mutations() to_skip = m_num_mutations + 10 expected_skipped = m_num_mutations expected_mutated = m_num_mutations - expected_skipped self._check_skip(to_skip, expected_skipped, expected_mutated) def testSkipZeroMultiTemplate(self): self.model.connect(self.templates[0]) self.model.connect(self.templates[0], self.templates[1]) m_num_mutations = self.model.num_mutations() to_skip = 0 expected_skipped = to_skip expected_mutated = m_num_mutations self._check_skip(to_skip, expected_skipped, expected_mutated) def testSkipHalfMultiTemplate(self): self.model.connect(self.templates[0]) self.model.connect(self.templates[0], self.templates[1]) m_num_mutations = self.model.num_mutations() to_skip = m_num_mutations // 2 expected_skipped = to_skip expected_mutated = m_num_mutations - expected_skipped self._check_skip(to_skip, expected_skipped, expected_mutated) def testSkipExactMultiTemplate(self): self.model.connect(self.templates[0]) self.model.connect(self.templates[0], self.templates[1]) m_num_mutations = self.model.num_mutations() to_skip = m_num_mutations expected_skipped = to_skip expected_mutated = m_num_mutations - expected_skipped self._check_skip(to_skip, expected_skipped, expected_mutated) def testSkipTooMuchMultiTemplate(self): self.model.connect(self.templates[0]) self.model.connect(self.templates[0], self.templates[1]) m_num_mutations = self.model.num_mutations() to_skip = m_num_mutations + 10 expected_skipped = m_num_mutations expected_mutated = m_num_mutations - expected_skipped self._check_skip(to_skip, expected_skipped, expected_mutated) def testFailureToTo(self): self.assertEqual(len(self.todo), 0) def testExceptionIfLoop(self): self.model.connect(self.templates[0]) self.model.connect(self.templates[0], self.templates[1]) self.model.connect(self.templates[1], self.templates[0]) with self.assertRaises(KittyException): self.model.num_mutations()
class WebInterfaceTest(BaseTestCase): def setUp(self): super(WebInterfaceTest, self).setUp(None) 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.host = '127.0.0.1' self.port = 11223 self.url = 'http://%(host)s:%(port)s' % { 'host': self.host, 'port': self.port } self.prepare() def tearDown(self): if self.fuzzer: self.logger.info('still have fuzzer, stop it') self.fuzzer.stop() def prepare(self): self.start_index = 0 self.end_index = 20 self.delay_duration = 0 self.fuzzer = ServerFuzzer(name="TestServerFuzzer", logger=self.logger) self.model = GraphModel() self.model.logger = self.logger self.model.connect(self.t_str) self.fuzzer.set_model(self.model) def _webValidRequest(self, request): resp = requests.get(request) self.assertIsNotNone(resp) self.assertEqual(resp.status_code, 200) as_json = resp.json() self.assertIsNotNone(as_json) return as_json def _webGetStats(self): return self._webValidRequest('%s/api/stats.json' % self.url) def _webGetReport(self, report_id): return self._webValidRequest('%s/api/report?report_id=%s' % (self.url, report_id)) def _webGetReportList(self): resp = self._webGetStats() self.assertIn('reports', resp) reports = resp['reports'] return reports def _runFuzzerWithReportList(self, uut, report_list): config = {} for report_id in report_list: config[str(report_id)] = {'report': {'failed': True}} self.fuzzer.set_interface(uut) target = ServerTargetMock(config, logger=self.logger) self.fuzzer.set_target(target) self.fuzzer.start() def _testStatsApiReportList(self, report_list): uut = WebInterface(host=self.host, port=self.port) report_list.sort() self._runFuzzerWithReportList(uut, report_list) actual_report_list = self._webGetReportList() self.assertListEqual(actual_report_list, report_list) def testStatsApiReportListEmpty(self): self._testStatsApiReportList([]) def testStatsApiReportListSingle(self): self._testStatsApiReportList([5]) def testStatsApiReportListMultiple(self): self._testStatsApiReportList([1, 2, 3, 4, 5]) def testStatsApiReportListAll(self): self._testStatsApiReportList([x for x in range(self.end_index)]) def _testStatsApi(self): ''' .. todo:: other stats API tests ''' pass def _testReportApiReportExists(self, report_list): for report_id in report_list: response = self._webGetReport(report_id) self.assertIn('report', response) self.assertIn('encoding', response) def _testReportApiValid(self, report_list): self._testStatsApiReportList(report_list) self._testReportApiReportExists(report_list) def testReportApiSingle(self): self._testReportApiValid([1]) def testReportApiMultiple(self): self._testReportApiValid([1, 2, 3]) def testReportApiAll(self): self._testReportApiValid([x for x in range(self.end_index)]) def _testReportApiError(self, request): self._testReportApiValid([x for x in range(self.end_index)]) resp = self._webValidRequest(request) self.assertIn('error', resp) self.assertNotIn('report', resp) def testReportApiErrorWhenNoReportId(self): self._testReportApiError('%s/api/report' % (self.url)) def testReportApiErrorWhenReportIdNotInt(self): self._testReportApiError('%s/api/report?report_id=%s' % (self.url, '%%')) def testReportApiErrorWhenNoSuchReport(self): self._testReportApiError('%s/api/report?report_id=%s' % (self.url, self.end_index + 1)) def _testPauseApi(self): ''' .. todo:: pause/resume api tests ''' pass
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)
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()
class TestClientFuzzer(unittest.TestCase): def setUp(self): self.logger = get_test_logger() self.logger.debug('TESTING METHOD: %s', self._testMethodName) self._default_stage = 'simple_str_template' self.t_str = Template(name=self._default_stage, fields=[String(name='str1', value='kitty')]) self.t_int = Template(name='simple_int_template', fields=[UInt32(name='int1', value=0x1234)]) self.fuzzer = None self.mutations = {} self.prepare() def tearDown(self): if self.fuzzer: self.fuzzer.stop() def default_callback(self, test, stage, resp): if test not in self.mutations: self.mutations[test] = [] self.mutations[test].append((stage, resp)) def prepare(self): self.start_index = 10 self.end_index = 20 self.delay_duration = 0 self.fuzzer = ClientFuzzer(name="TestServerFuzzer", logger=self.logger) self.interface = EmptyInterface() self.fuzzer.set_interface(self.interface) self.model = GraphModel() self.model.logger = self.logger self.model.connect(self.t_str) self.fuzzer.set_model(self.model) self.default_config = { 'always': { 'trigger': { 'fuzzer': self.fuzzer, 'stages': [(self._default_stage, {})] } } } self.target = ClientTargetMock(self.default_config, self.default_callback, logger=self.logger) 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 testVanilla(self): self.fuzzer.start() self.fuzzer.wait_until_done() 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() self.fuzzer.wait_until_done() 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() self.fuzzer.wait_until_done() 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() self.fuzzer.wait_until_done() 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 testTestFailedWhenReportIsFailed(self): config = { '13': { 'report': { 'status': 'failed', 'reason': 'failure reason' } } } config.update(self.default_config) target = ClientTargetMock(config, self.default_callback, logger=self.logger) self.fuzzer.set_target(target) self.fuzzer.start() self.fuzzer.wait_until_done() info = self.fuzzer._get_session_info() reports = self.fuzzer.dataman.get_report_test_ids() self.assertEqual( sorted(reports), sorted([int(x) for x in config.keys() if x != 'always'])) self.assertEqual(info.failure_count, len(config) - 1) 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' } } } config.update(self.default_config) target = ClientTargetMock(config, self.default_callback, logger=self.logger) self.fuzzer.set_target(target) self.fuzzer.start() self.fuzzer.wait_until_done() info = self.fuzzer._get_session_info() reports = self.fuzzer.dataman.get_report_test_ids() self.assertEqual( sorted(reports), sorted([int(x) for x in config.keys() if x != 'always'])) self.assertEqual(info.failure_count, len(config) - 1) def testStoringAllReportsWhenStoreAllReportsIsSetToTrue(self): config = {} config.update(self.default_config) target = ClientTargetMock(config, self.default_callback, logger=self.logger) self.fuzzer.set_store_all_reports(True) self.fuzzer.set_target(target) self.fuzzer.start() self.fuzzer.wait_until_done() 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() self.fuzzer.set_range(start_index) self.fuzzer.start() self.fuzzer.wait_until_done() 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 testGetMutationForStage(self): self.fuzzer.set_range(0, 1) config = { 'always': { 'trigger': { 'fuzzer': self.fuzzer, 'stages': [(self._default_stage, {})] } } } target = ClientTargetMock(config, self.default_callback, logger=self.logger) self.fuzzer.set_target(target) self.fuzzer.start() self.fuzzer.wait_until_done() self.assertIn(0, self.mutations) self.assertEqual(self.mutations[0][0][0], self._default_stage) self.assertIsNotNone(self.mutations[0][0][1]) def testGetMutationForStageTwice(self): self.fuzzer.set_range(0, 1) config = { 'always': { 'trigger': { 'fuzzer': self.fuzzer, 'stages': [(self._default_stage, {}), (self._default_stage, {})] } } } target = ClientTargetMock(config, self.default_callback, logger=self.logger) self.fuzzer.set_target(target) self.fuzzer.start() self.fuzzer.wait_until_done() self.assertIn(0, self.mutations) self.assertEqual(self.mutations[0][0][0], self._default_stage) self.assertIsNotNone(self.mutations[0][0][1]) self.assertEqual(self.mutations[0][1][0], self._default_stage) self.assertIsNotNone(self.mutations[0][1][1]) def testGetMutationWrongStage(self): self.fuzzer.set_range(0, 1) wrong_stage = 'simple_str_template1' config = { 'always': { 'trigger': { 'fuzzer': self.fuzzer, 'stages': [ (wrong_stage, {}), ] } } } target = ClientTargetMock(config, self.default_callback, logger=self.logger) self.fuzzer.set_target(target) self.fuzzer.start() self.fuzzer.wait_until_done() self.assertIn(0, self.mutations) self.assertEqual(self.mutations[0][0][0], wrong_stage) self.assertIsNone(self.mutations[0][0][1]) def testGetMutationWrongAfterCorrectStage(self): self.fuzzer.set_range(0, 1) wrong_stage = 'simple_str_template1' config = { 'always': { 'trigger': { 'fuzzer': self.fuzzer, 'stages': [ (self._default_stage, {}), (wrong_stage, {}), ] } } } target = ClientTargetMock(config, self.default_callback, logger=self.logger) self.fuzzer.set_target(target) self.fuzzer.start() self.fuzzer.wait_until_done() self.assertIn(0, self.mutations) self.assertEqual(self.mutations[0][0][0], self._default_stage) self.assertIsNotNone(self.mutations[0][0][1]) self.assertEqual(self.mutations[0][1][0], wrong_stage) self.assertIsNone(self.mutations[0][1][1]) def testGetMutationCorrectAfterWrongStage(self): self.fuzzer.set_range(0, 1) wrong_stage = 'simple_str_template1' config = { 'always': { 'trigger': { 'fuzzer': self.fuzzer, 'stages': [ (wrong_stage, {}), (self._default_stage, {}), ] } } } target = ClientTargetMock(config, self.default_callback, logger=self.logger) self.fuzzer.set_target(target) self.fuzzer.start() self.fuzzer.wait_until_done() self.assertIn(0, self.mutations) self.assertEqual(self.mutations[0][0][0], wrong_stage) self.assertIsNone(self.mutations[0][0][1]) self.assertEqual(self.mutations[0][1][0], self._default_stage) self.assertIsNotNone(self.mutations[0][1][1]) def testGetMutationWithWildcard(self): self.fuzzer.set_range(0, 1) config = { 'always': { 'trigger': { 'fuzzer': self.fuzzer, 'stages': [(ClientFuzzer.STAGE_ANY, {})] } } } target = ClientTargetMock(config, self.default_callback, logger=self.logger) self.fuzzer.set_target(target) self.fuzzer.start() self.fuzzer.wait_until_done() self.assertIn(0, self.mutations) self.assertEqual(self.mutations[0][0][0], ClientFuzzer.STAGE_ANY) self.assertIsNotNone(self.mutations[0][0][1])
from kitty.fuzzers import ServerFuzzer from kitty.interfaces import WebInterface from katnip.targets.file import FileTarget from kitty.model import String from kitty.model import Template from kitty.model import GraphModel from chaostoolkitfuzzdiscover.experimentfilewriter.writegeneratedfuzz import InputFuzzExperimentGenerator, InputFileFuzzExperimentGenerator, InternalFileFuzzExperimentGenerator import os, shutil, pickle sample_template = Template(name='T1', fields=[ String('The default string', name='S1_1'), String('Another string', name='S1_2'), ]) sample_model = GraphModel() sample_model.connect(sample_template) #TODO : Add input validations & fix input def __generate_fuzz_file_for_fuzzinternalfilereads(input_files = None, sample_input = None): controller = EmptyController() __my_modal = sample_model __tmp_folder = tmp_folder if os.path.exists(__tmp_folder): shutil.rmtree(__tmp_folder) if not os.path.exists(__tmp_folder): os.makedirs(__tmp_folder) if sample_input is not None: __parent_tokens = [] for __si in sample_input: __tokens = str(__si).split() if len(__tokens)>2: __parent_tokens.extend(__tokens[2:])
class GraphModelTests(unittest.TestCase): def setUp(self): self.logger = get_test_logger() self.logger.debug('TESTING METHOD: %s', self._testMethodName) self.model = GraphModel() self.templates = self.get_templates() self.todo = [] def get_templates(self): res = [] res.append(Template(name='t1', fields=[String('data1')])) res.append(Template(name='t2', fields=[String('data2'), UInt32(300)])) res.append(Template(name='t3', fields=[UInt32(400)])) return res def _check_sequences(self, expected_sequences): self.logger.debug('check num_mutations()') t_num_mutations = sum(s[-1].num_mutations() for s in expected_sequences) m_num_mutations = self.model.num_mutations() self.assertEqual(t_num_mutations, m_num_mutations) sequences = {} while self.model.mutate(): sequence = tuple(self.model.get_sequence()) if sequence not in sequences: sequences[sequence] = 1 else: sequences[sequence] += 1 self.assertEqual(len(sequences), len(expected_sequences)) self.logger.debug('check that each sequence appears the appropriate number of times') for sequence, count in sequences.items(): seq_templates = [e.dst for e in sequence] self.assertIn(seq_templates, expected_sequences) last = seq_templates[-1] last_num_mutations = last.num_mutations() self.assertEqual(count, last_num_mutations) def test_sequence_single_template(self): t = self.templates[0] self.model.connect(t) expected_sequences = [[t]] self._check_sequences(expected_sequences) def test_sequence_direct_path(self): ''' root -> t1 ''' self.model.connect(self.templates[0]) for i in range(len(self.templates) - 1): self.model.connect(self.templates[i], self.templates[i + 1]) expected_sequences = list(self.templates[:i + 1] for i in range(len(self.templates))) self._check_sequences(expected_sequences) def test_sequence_complex_path(self): ''' root -> t1 root -> t1 -> t2 root -> t1 -> t3 root -> t1 -> t2 -> t3 ''' self.model.connect(self.templates[0]) self.model.connect(self.templates[0], self.templates[1]) self.model.connect(self.templates[0], self.templates[2]) self.model.connect(self.templates[1], self.templates[2]) expected_sequences = [ [self.templates[0]], [self.templates[0], self.templates[1]], [self.templates[0], self.templates[2]], [self.templates[0], self.templates[1], self.templates[2]] ] self._check_sequences(expected_sequences) def test_multi_head_path(self): ''' root -> t1 root -> t2 root -> t3 ''' expected_sequences = [] for t in self.templates: expected_sequences.append([t]) self.model.connect(t) self._check_sequences(expected_sequences) def _check_skip(self, to_skip, expected_skipped, expected_mutated): skipped = self.model.skip(to_skip) self.assertEqual(expected_skipped, skipped) mutated = 0 while self.model.mutate(): mutated += 1 self.assertEqual(expected_mutated, mutated) def test_skip_zero_single_template(self): self.model.connect(self.templates[0]) m_num_mutations = self.model.num_mutations() to_skip = 0 expected_skipped = to_skip expected_mutated = m_num_mutations self._check_skip(to_skip, expected_skipped, expected_mutated) def test_skip_half_single_template(self): self.model.connect(self.templates[0]) m_num_mutations = self.model.num_mutations() to_skip = m_num_mutations / 2 expected_skipped = to_skip expected_mutated = m_num_mutations - expected_skipped self._check_skip(to_skip, expected_skipped, expected_mutated) def test_skip_exact_single_template(self): self.model.connect(self.templates[0]) m_num_mutations = self.model.num_mutations() to_skip = m_num_mutations expected_skipped = to_skip expected_mutated = m_num_mutations - expected_skipped self._check_skip(to_skip, expected_skipped, expected_mutated) def test_skip_too_much_single_template(self): self.model.connect(self.templates[0]) m_num_mutations = self.model.num_mutations() to_skip = m_num_mutations + 10 expected_skipped = m_num_mutations expected_mutated = m_num_mutations - expected_skipped self._check_skip(to_skip, expected_skipped, expected_mutated) def test_skip_zero_multi_template(self): self.model.connect(self.templates[0]) self.model.connect(self.templates[0], self.templates[1]) m_num_mutations = self.model.num_mutations() to_skip = 0 expected_skipped = to_skip expected_mutated = m_num_mutations self._check_skip(to_skip, expected_skipped, expected_mutated) def test_skip_half_multi_template(self): self.model.connect(self.templates[0]) self.model.connect(self.templates[0], self.templates[1]) m_num_mutations = self.model.num_mutations() to_skip = m_num_mutations / 2 expected_skipped = to_skip expected_mutated = m_num_mutations - expected_skipped self._check_skip(to_skip, expected_skipped, expected_mutated) def test_skip_exact_multi_template(self): self.model.connect(self.templates[0]) self.model.connect(self.templates[0], self.templates[1]) m_num_mutations = self.model.num_mutations() to_skip = m_num_mutations expected_skipped = to_skip expected_mutated = m_num_mutations - expected_skipped self._check_skip(to_skip, expected_skipped, expected_mutated) def test_skip_too_much_multi_template(self): self.model.connect(self.templates[0]) self.model.connect(self.templates[0], self.templates[1]) m_num_mutations = self.model.num_mutations() to_skip = m_num_mutations + 10 expected_skipped = m_num_mutations expected_mutated = m_num_mutations - expected_skipped self._check_skip(to_skip, expected_skipped, expected_mutated) def test_failure_to_to(self): self.assertEqual(len(self.todo), 0) def test_exception_if_loop(self): self.model.connect(self.templates[0]) self.model.connect(self.templates[0], self.templates[1]) self.model.connect(self.templates[1], self.templates[0]) with self.assertRaises(KittyException): self.model.num_mutations()
class WebInterfaceTest(BaseTestCase): def setUp(self): super(WebInterfaceTest, self).setUp(None) 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.host = '127.0.0.1' self.port = 11223 self.url = 'http://%(host)s:%(port)s' % {'host': self.host, 'port': self.port} self.prepare() def tearDown(self): if self.fuzzer: self.logger.info('still have fuzzer, stop it') self.fuzzer.stop() def prepare(self): self.start_index = 0 self.end_index = 20 self.delay_duration = 0 self.fuzzer = ServerFuzzer(name="TestServerFuzzer", logger=self.logger) self.model = GraphModel() self.model.logger = self.logger self.model.connect(self.t_str) self.fuzzer.set_model(self.model) def _webValidRequest(self, request): resp = requests.get(request) self.assertIsNotNone(resp) self.assertEqual(resp.status_code, 200) as_json = resp.json() self.assertIsNotNone(as_json) return as_json def _webGetStats(self): return self._webValidRequest('%s/api/stats.json' % self.url) def _webGetReport(self, report_id): return self._webValidRequest('%s/api/report?report_id=%s' % (self.url, report_id)) def _webGetReportList(self): resp = self._webGetStats() self.assertIn('reports_extended', resp) reports = resp['reports_extended'] return reports def _runFuzzerWithReportList(self, uut, report_list): config = {} for report_id in report_list: config[str(report_id)] = {'report': {'status': 'failed', 'reason': 'failure reason'}} self.fuzzer.set_interface(uut) target = ServerTargetMock(config, logger=self.logger) self.fuzzer.set_target(target) self.fuzzer.start() def _testStatsApiReportList(self, report_list): uut = WebInterface(host=self.host, port=self.port) report_list.sort() self._runFuzzerWithReportList(uut, report_list) actual_report_list = [x[0] for x in self._webGetReportList()] self.assertListEqual(actual_report_list, report_list) def testStatsApiReportListEmpty(self): self._testStatsApiReportList([]) def testStatsApiReportListSingle(self): self._testStatsApiReportList([5]) def testStatsApiReportListMultiple(self): self._testStatsApiReportList([1, 2, 3, 4, 5]) def testStatsApiReportListAll(self): self._testStatsApiReportList([x for x in range(self.end_index)]) def _testStatsApi(self): ''' .. todo:: other stats API tests ''' pass def _testReportApiReportExists(self, report_list): for report_id in report_list: response = self._webGetReport(report_id) self.assertIn('report', response) self.assertIn('encoding', response) def _testReportApiValid(self, report_list): self._testStatsApiReportList(report_list) self._testReportApiReportExists(report_list) def testReportApiSingle(self): self._testReportApiValid([1]) def testReportApiMultiple(self): self._testReportApiValid([1, 2, 3]) def testReportApiAll(self): self._testReportApiValid([x for x in range(self.end_index)]) def _testReportApiError(self, request): self._testReportApiValid([x for x in range(self.end_index)]) resp = self._webValidRequest(request) self.assertIn('error', resp) self.assertNotIn('report', resp) def testReportApiErrorWhenNoReportId(self): self._testReportApiError('%s/api/report' % (self.url)) def testReportApiErrorWhenReportIdNotInt(self): self._testReportApiError('%s/api/report?report_id=%s' % (self.url, '%%')) def testReportApiErrorWhenNoSuchReport(self): self._testReportApiError('%s/api/report?report_id=%s' % (self.url, self.end_index + 1)) def _testPauseApi(self): ''' .. todo:: pause/resume api tests ''' pass def get_static_content(self, filename): (dir_path, _) = os.path.split(__file__) index_path = os.path.join(dir_path, '..', 'kitty', 'interfaces', 'web', 'static', filename) data = None with open(index_path, 'rb') as f: data = f.read() return data def testGetIndexHtml(self): url = self.url + '/index.html' uut = WebInterface(host=self.host, port=self.port) self._runFuzzerWithReportList(uut, []) resp = requests.get(url) self.assertEqual(resp.status_code, 200) self.assertEqual(resp.headers['content-type'], 'text/html') index_content = self.get_static_content('index.html') self.assertEqual(resp.text, index_content) def testReturnIndexForRoot(self): root_url = self.url + '/' index_url = self.url + '/index.html' uut = WebInterface(host=self.host, port=self.port) self._runFuzzerWithReportList(uut, []) root_resp = requests.get(root_url) index_url = requests.get(index_url) self.assertEqual(root_resp.status_code, 200) self.assertEqual(root_resp.headers['content-type'], 'text/html') self.assertEqual(root_resp.text, index_url.text) def testGetOtherFilesReturns401(self): url = self.url + '/../../../../../../../etc/passwd' uut = WebInterface(host=self.host, port=self.port) self._runFuzzerWithReportList(uut, []) resp = requests.get(url) self.assertEqual(resp.status_code, 401) def testPost(self): url = self.url + '/index.html' uut = WebInterface(host=self.host, port=self.port) self._runFuzzerWithReportList(uut, []) resp = requests.post(url) self.assertEqual(resp.status_code, 200) self.assertEqual(resp.headers['content-type'], 'text/html') index_content = self.get_static_content('index.html') self.assertEqual(resp.text, index_content)
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
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)
http_get_v3 = Template( name='HTTP_GET_V3', fields=[ Method('GET', name='method', fuzzable=True), # 7 + iteration Delimiter(' ', name='space1', fuzzable=False), url.Path('somewhere/else', name='path'), Delimiter(' ', name='space2'), String('HTTP', name='protocol name'), Delimiter('/', name='fws1'), Dword(1, name='major version', encoder=ENC_INT_DEC), Delimiter('.', name='dot1'), Dword(1, name='minor version', encoder=ENC_INT_DEC), Static('\r\n'), Static('Host: 127.0.0.1:5000'), Static('\r\n'), Static('Connection: close'), Static('\r\n\r\n', name='eom') ]) model.connect(http_get_v3) fuzzer = ServerFuzzer() fuzzer.set_model(model) fuzzer.set_target(target) fuzzer.set_store_all_reports(True) os.remove("fuzz_session.sqlite") fuzzer.set_session_file("fuzz_session.sqlite") fuzzer.set_interface(WebInterface()) fuzzer.start() print 'finished!'
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()
target.set_expect_response(True) # Define controller controller = MyLocalProcessController(name='simple_server', process_path='./simple_server_orig', process_args=[], delay_after_start=0.2, start_each_test=False, logger=None) target.set_controller(controller) # Define model model = GraphModel() model.connect(get_startdt) model.connect(get_startdt, get_stopdt) model.connect(get_startdt, get_testfr) model.connect(get_testfr, get_stopdt) model.connect(get_startdt, get_GI) model.connect(get_GI, get_stopdt) # Define fuzzer fuzzer = ServerFuzzer(option_line="") fuzzer.set_interface(WebInterface(port=web_port)) fuzzer.set_model(model) fuzzer.set_target(target) fuzzer.set_delay_between_tests(0.2) fuzzer.start()
from kitty.fuzzers import ServerFuzzer from kitty.interfaces import WebInterface from kitty.controllers import EmptyController from katnip.targets.file import FileTarget from kitty.model import GraphModel from kitty.model import String from kitty.model import Template opts = docopt.docopt(__doc__) t1 = Template(name='T1', fields=[ String('The default string', name='S1_1'), String('Another string', name='S1_2'), ]) # Writes content to files target = FileTarget('FileTarget', 'tmp/', 'fuzzed') controller = EmptyController('EmptyController') target.set_controller(controller) model = GraphModel() model.connect(t1) fuzzer = ServerFuzzer(name="Example 1 - File Generator", option_line=opts['--kitty-options']) fuzzer.set_interface(WebInterface(port=26001)) fuzzer.set_model(model) fuzzer.set_target(target) fuzzer.start() print('-------------- done with fuzzing -----------------') raw_input('press any key to exit') fuzzer.stop()
class WebInterfaceTest(BaseTestCase): def setUp(self): super(WebInterfaceTest, self).setUp(None) 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.host = '127.0.0.1' self.port = 11223 self.url = 'http://%(host)s:%(port)s' % {'host': self.host, 'port': self.port} self.prepare() def tearDown(self): if self.fuzzer: self.logger.info('still have fuzzer, stop it') self.fuzzer.stop() def prepare(self): self.start_index = 0 self.end_index = 20 self.delay_duration = 0 self.fuzzer = ServerFuzzer(name="TestServerFuzzer", logger=self.logger) self.model = GraphModel() self.model.logger = self.logger self.model.connect(self.t_str) self.fuzzer.set_model(self.model) def _webValidRequest(self, request): resp = requests.get(request) self.assertIsNotNone(resp) self.assertEqual(resp.status_code, 200) as_json = resp.json() self.assertIsNotNone(as_json) return as_json def _webGetStats(self): return self._webValidRequest('%s/api/stats.json' % self.url) def _webGetReport(self, report_id): return self._webValidRequest('%s/api/report?report_id=%s' % (self.url, report_id)) def _webGetTemplateInfo(self): return self._webValidRequest('%s/api/template_info.json' % self.url) def _webGetStages(self): return self._webValidRequest('%s/api/stages.json' % self.url) def _webGetFavicon(self): resp = requests.get('%s/favicon.ico' % self.url) self.assertIsNotNone(resp) self.assertEqual(resp.status_code, 200) return resp def _webGetReportList(self): resp = self._webGetStats() self.assertIn('reports_extended', resp) reports = resp['reports_extended'] return reports def _runFuzzerWithReportList(self, uut, report_list): config = {} for report_id in report_list: config[str(report_id)] = {'report': {'status': 'failed', 'reason': 'failure reason'}} self.fuzzer.set_interface(uut) target = ServerTargetMock(config, logger=self.logger) self.fuzzer.set_target(target) self.fuzzer.start() def _testStatsApiReportList(self, report_list): uut = WebInterface(host=self.host, port=self.port) report_list.sort() self._runFuzzerWithReportList(uut, report_list) actual_report_list = [x[0] for x in self._webGetReportList()] self.assertListEqual(actual_report_list, report_list) def testStatsApiReportListEmpty(self): self._testStatsApiReportList([]) def testStatsApiReportListSingle(self): self._testStatsApiReportList([5]) def testStatsApiReportListMultiple(self): self._testStatsApiReportList([1, 2, 3, 4, 5]) def testStatsApiReportListAll(self): self._testStatsApiReportList([x for x in range(self.end_index)]) def testTemplateInfoApi(self): # # This is based on the usage in index.html # uut = WebInterface(host=self.host, port=self.port) self._runFuzzerWithReportList(uut, []) template_info = self._webGetTemplateInfo() self.assertIn('name', template_info) self.assertIn('field_type', template_info) self.assertIn('fields', template_info) self.assertIn('mutation', template_info) self.assertIn('total_number', template_info['mutation']) self.assertIn('current_index', template_info['mutation']) def testGetStagesApi(self): uut = WebInterface(host=self.host, port=self.port) self._runFuzzerWithReportList(uut, []) resp = self._webGetStages() self.assertIn('current', resp) self.assertIn('stages', resp) def _testReportApiReportExists(self, report_list): for report_id in report_list: response = self._webGetReport(report_id) self.assertIn('report', response) self.assertIn('encoding', response) def _testReportApiValid(self, report_list): self._testStatsApiReportList(report_list) self._testReportApiReportExists(report_list) def testReportApiSingle(self): self._testReportApiValid([1]) def testReportApiMultiple(self): self._testReportApiValid([1, 2, 3]) def testReportApiAll(self): self._testReportApiValid([x for x in range(self.end_index)]) def _testReportApiError(self, request): self._testReportApiValid([x for x in range(self.end_index)]) resp = self._webValidRequest(request) self.assertIn('error', resp) self.assertNotIn('report', resp) def testReportApiErrorWhenNoReportId(self): self._testReportApiError('%s/api/report' % (self.url)) def testReportApiErrorWhenReportIdNotInt(self): self._testReportApiError('%s/api/report?report_id=%s' % (self.url, '%%')) def testReportApiErrorWhenNoSuchReport(self): self._testReportApiError('%s/api/report?report_id=%s' % (self.url, self.end_index + 1)) def _testPauseApi(self): ''' .. todo:: pause/resume api tests ''' pass def get_static_content(self, filename): (dir_path, _) = os.path.split(__file__) index_path = os.path.join(dir_path, '..', 'kitty', 'interfaces', 'web', 'static', filename) data = None with open(index_path, 'rb') as f: data = f.read() return data def testGetIndexHtml(self): url = self.url + '/index.html' uut = WebInterface(host=self.host, port=self.port) self._runFuzzerWithReportList(uut, []) resp = requests.get(url) self.assertEqual(resp.status_code, 200) self.assertEqual(resp.headers['content-type'], 'text/html') index_content = self.get_static_content('index.html') self.assertEqual(resp.text, index_content) def testReturnIndexForRoot(self): root_url = self.url + '/' index_url = self.url + '/index.html' uut = WebInterface(host=self.host, port=self.port) self._runFuzzerWithReportList(uut, []) root_resp = requests.get(root_url) index_url = requests.get(index_url) self.assertEqual(root_resp.status_code, 200) self.assertEqual(root_resp.headers['content-type'], 'text/html') self.assertEqual(root_resp.text, index_url.text) def testGetOtherFilesReturns401(self): url = self.url + '/../../../../../../../etc/passwd' uut = WebInterface(host=self.host, port=self.port) self._runFuzzerWithReportList(uut, []) resp = requests.get(url) self.assertEqual(resp.status_code, 401) def testPost(self): url = self.url + '/index.html' uut = WebInterface(host=self.host, port=self.port) self._runFuzzerWithReportList(uut, []) resp = requests.post(url) self.assertEqual(resp.status_code, 200) self.assertEqual(resp.headers['content-type'], 'text/html') index_content = self.get_static_content('index.html') self.assertEqual(resp.text, index_content)
class TestClientFuzzer(unittest.TestCase): def setUp(self): self.logger = get_test_logger() self.logger.debug('TESTING METHOD: %s', self._testMethodName) self._default_stage = 'simple_str_template' self.t_str = Template(name=self._default_stage, fields=[String(name='str1', value='kitty')]) self.t_int = Template(name='simple_int_template', fields=[UInt32(name='int1', value=0x1234)]) self.fuzzer = None self.mutations = {} self.prepare() def tearDown(self): if self.fuzzer: self.fuzzer.stop() def default_callback(self, test, stage, resp): if test not in self.mutations: self.mutations[test] = [] self.mutations[test].append((stage, resp)) def prepare(self): self.start_index = 10 self.end_index = 20 self.delay_duration = 0 self.fuzzer = ClientFuzzer(name="TestServerFuzzer", logger=self.logger) self.interface = EmptyInterface() self.fuzzer.set_interface(self.interface) self.model = GraphModel() self.model.logger = self.logger self.model.connect(self.t_str) self.fuzzer.set_model(self.model) self.default_config = { 'always': { 'trigger': { 'fuzzer': self.fuzzer, 'stages': [ (self._default_stage, {}) ] } } } self.target = ClientTargetMock(self.default_config, self.default_callback, logger=self.logger) 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 testVanilla(self): self.fuzzer.start() self.fuzzer.wait_until_done() 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() self.fuzzer.wait_until_done() 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() self.fuzzer.wait_until_done() 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() self.fuzzer.wait_until_done() 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 testTestFailedWhenReportIsFailed(self): config = { '13': { 'report': { 'status': 'failed', 'reason': 'failure reason' } } } config.update(self.default_config) target = ClientTargetMock(config, self.default_callback, logger=self.logger) self.fuzzer.set_target(target) self.fuzzer.start() self.fuzzer.wait_until_done() info = self.fuzzer._get_session_info() reports = self.fuzzer.dataman.get_report_test_ids() self.assertEqual(sorted(reports), sorted([int(x) for x in config.keys() if x != 'always'])) self.assertEqual(info.failure_count, len(config) - 1) 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'}} } config.update(self.default_config) target = ClientTargetMock(config, self.default_callback, logger=self.logger) self.fuzzer.set_target(target) self.fuzzer.start() self.fuzzer.wait_until_done() info = self.fuzzer._get_session_info() reports = self.fuzzer.dataman.get_report_test_ids() self.assertEqual(sorted(reports), sorted([int(x) for x in config.keys() if x != 'always'])) self.assertEqual(info.failure_count, len(config) - 1) def testStoringAllReportsWhenStoreAllReportsIsSetToTrue(self): config = {} config.update(self.default_config) target = ClientTargetMock(config, self.default_callback, logger=self.logger) self.fuzzer.set_store_all_reports(True) self.fuzzer.set_target(target) self.fuzzer.start() self.fuzzer.wait_until_done() 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() self.fuzzer.set_range(start_index) self.fuzzer.start() self.fuzzer.wait_until_done() 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 testGetMutationForStage(self): self.fuzzer.set_range(0, 1) config = { 'always': { 'trigger': { 'fuzzer': self.fuzzer, 'stages': [ (self._default_stage, {}) ] } } } target = ClientTargetMock(config, self.default_callback, logger=self.logger) self.fuzzer.set_target(target) self.fuzzer.start() self.fuzzer.wait_until_done() self.assertIn(0, self.mutations) self.assertEquals(self.mutations[0][0][0], self._default_stage) self.assertIsNotNone(self.mutations[0][0][1]) def testGetMutationForStageTwice(self): self.fuzzer.set_range(0, 1) config = { 'always': { 'trigger': { 'fuzzer': self.fuzzer, 'stages': [ (self._default_stage, {}), (self._default_stage, {}) ] } } } target = ClientTargetMock(config, self.default_callback, logger=self.logger) self.fuzzer.set_target(target) self.fuzzer.start() self.fuzzer.wait_until_done() self.assertIn(0, self.mutations) self.assertEquals(self.mutations[0][0][0], self._default_stage) self.assertIsNotNone(self.mutations[0][0][1]) self.assertEquals(self.mutations[0][1][0], self._default_stage) self.assertIsNotNone(self.mutations[0][1][1]) def testGetMutationWrongStage(self): self.fuzzer.set_range(0, 1) wrong_stage = 'simple_str_template1' config = { 'always': { 'trigger': { 'fuzzer': self.fuzzer, 'stages': [ (wrong_stage, {}), ] } } } target = ClientTargetMock(config, self.default_callback, logger=self.logger) self.fuzzer.set_target(target) self.fuzzer.start() self.fuzzer.wait_until_done() self.assertIn(0, self.mutations) self.assertEquals(self.mutations[0][0][0], wrong_stage) self.assertIsNone(self.mutations[0][0][1]) def testGetMutationWrongAfterCorrectStage(self): self.fuzzer.set_range(0, 1) wrong_stage = 'simple_str_template1' config = { 'always': { 'trigger': { 'fuzzer': self.fuzzer, 'stages': [ (self._default_stage, {}), (wrong_stage, {}), ] } } } target = ClientTargetMock(config, self.default_callback, logger=self.logger) self.fuzzer.set_target(target) self.fuzzer.start() self.fuzzer.wait_until_done() self.assertIn(0, self.mutations) self.assertEquals(self.mutations[0][0][0], self._default_stage) self.assertIsNotNone(self.mutations[0][0][1]) self.assertEquals(self.mutations[0][1][0], wrong_stage) self.assertIsNone(self.mutations[0][1][1]) def testGetMutationCorrectAfterWrongStage(self): self.fuzzer.set_range(0, 1) wrong_stage = 'simple_str_template1' config = { 'always': { 'trigger': { 'fuzzer': self.fuzzer, 'stages': [ (wrong_stage, {}), (self._default_stage, {}), ] } } } target = ClientTargetMock(config, self.default_callback, logger=self.logger) self.fuzzer.set_target(target) self.fuzzer.start() self.fuzzer.wait_until_done() self.assertIn(0, self.mutations) self.assertEquals(self.mutations[0][0][0], wrong_stage) self.assertIsNone(self.mutations[0][0][1]) self.assertEquals(self.mutations[0][1][0], self._default_stage) self.assertIsNotNone(self.mutations[0][1][1]) def testGetMutationWithWildcard(self): self.fuzzer.set_range(0, 1) config = { 'always': { 'trigger': { 'fuzzer': self.fuzzer, 'stages': [ (ClientFuzzer.STAGE_ANY, {}) ] } } } target = ClientTargetMock(config, self.default_callback, logger=self.logger) self.fuzzer.set_target(target) self.fuzzer.start() self.fuzzer.wait_until_done() self.assertIn(0, self.mutations) self.assertEquals(self.mutations[0][0][0], ClientFuzzer.STAGE_ANY) self.assertIsNotNone(self.mutations[0][0][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()