def test_fuzz_headers_no_headers(self): cf_singleton.save('fuzzable_headers', ['Referer']) # This one changed cf_singleton.save('fuzz_cookies', False) cf_singleton.save('fuzz_url_filenames', False) cf_singleton.save('fuzzed_files_extension', 'gif') cf_singleton.save('fuzz_form_files', False) cf_singleton.save('fuzz_url_parts', False) url = URL('http://moth/?id=1') # No headers in the original request #headers = Headers([('Referer', 'http://moth/foo/bar/')]) freq = HTTPQSRequest(url) generated_mutants = create_mutants(freq, self.payloads) expected_urls = ['http://moth/?id=abc', 'http://moth/?id=def', 'http://moth/?id=1', 'http://moth/?id=1', ] generated_urls = [m.get_uri().url_string for m in generated_mutants] self.assertEqual(generated_urls, expected_urls) expected_headers = [Headers(), Headers(), Headers([('Referer', 'abc')]), Headers([('Referer', 'def')]), ] generated_headers = [m.get_headers() for m in generated_mutants] self.assertEqual(expected_headers, generated_headers) self.assertTrue(all(isinstance(m, QSMutant) or isinstance(m, HeadersMutant) for m in generated_mutants))
def audit(self, freq, orig_response): ''' Tests an URL for ReDoS vulnerabilities using time delays. :param freq: A FuzzableRequest ''' if self.ignore_this_request(freq): return fake_mutants = create_mutants(freq, [ '', ]) for mutant in fake_mutants: for delay_obj in self.get_delays(): adc = AproxDelayController(mutant, delay_obj, self._uri_opener, delay_setting=EXPONENTIALLY) success, responses = adc.delay_is_controlled() if success: # Now I can be sure that I found a vuln, we control the # response time with the delay desc = 'ReDoS was found at: %s' % mutant.found_at() response_ids = [r.id for r in responses] v = Vuln.from_mutant('ReDoS vulnerability', desc, severity.MEDIUM, response_ids, self.get_name(), mutant) self.kb_append_uniq(self, 'redos', v) break
def test_qs_and_cookie(self): cf_singleton.save('fuzzable_headers', []) cf_singleton.save('fuzz_cookies', True) # This one changed cf_singleton.save('fuzz_url_filenames', False) cf_singleton.save('fuzzed_files_extension', 'gif') cf_singleton.save('fuzz_form_files', False) cf_singleton.save('fuzz_url_parts', False) url = URL('http://moth/?id=1') # And now there is a cookie cookie = Cookie('foo=bar') freq = HTTPQSRequest(url, cookie=cookie) generated_mutants = create_mutants(freq, self.payloads) expected_urls = [u'http://moth/?id=abc', u'http://moth/?id=def', u'http://moth/?id=1', u'http://moth/?id=1'] generated_urls = [m.get_uri().url_string for m in generated_mutants] self.assertEqual(generated_urls, expected_urls) expected_cookies = ['foo=bar;', 'foo=bar;', 'foo=abc;', 'foo=def;'] generated_cookies = [str(m.get_cookie()) for m in generated_mutants] self.assertEqual(expected_cookies, generated_cookies) self.assertTrue(all(isinstance(m, QSMutant) or isinstance(m, CookieMutant) for m in generated_mutants))
def audit(self, freq, orig_response): ''' Searches for file upload vulns. :param freq: A FuzzableRequest ''' if freq.get_method().upper() == 'POST' and len( freq.get_file_vars()) != 0: for file_parameter in freq.get_file_vars(): fileh_filen_list = self._create_files() # Only file handlers are passed to the create_mutants functions file_handlers = [i[0] for i in fileh_filen_list] mutants = create_mutants(freq, file_handlers, fuzzable_param_list=[ file_parameter, ]) for mutant in mutants: _, filename = os.path.split(mutant.get_mod_value().name) mutant.uploaded_file_name = filename self._send_mutants_in_threads(self._uri_opener.send_mutant, mutants, self._analyze_result) self._remove_files(fileh_filen_list)
def _with_time_delay(self, freq): ''' Tests an URL for OS Commanding vulnerabilities using time delays. :param freq: A FuzzableRequest ''' fake_mutants = create_mutants(freq, ['', ]) for mutant in fake_mutants: if self._has_bug(mutant): continue for delay_obj in self._get_wait_commands(): ed = ExactDelayController(mutant, delay_obj, self._uri_opener) success, responses = ed.delay_is_controlled() if success: desc = 'OS Commanding was found at: %s' % mutant.found_at() v = Vuln.from_mutant('OS commanding vulnerability', desc, severity.HIGH, [r.id for r in responses], self.get_name(), mutant) v['os'] = delay_obj.get_OS() v['separator'] = delay_obj.get_separator() self.kb_append_uniq(self, 'os_commanding', v) break
def _is_token_checked(self, freq, token, orig_response): ''' Please note that this method generates lots of false positives and negatives. Read the github issue for more information. :see: https://github.com/andresriancho/w3af/issues/120 :return: True if the CSRF token is NOT verified by the web application ''' token_pname_lst = token.keys() token_value = token[token_pname_lst[0]] # This will generate mutants for the original fuzzable request using # the reversed token value as a CSRF-token (this is a feature: we want # to make sure it has the same length as the original token and that # it has the same type: digits, hash, etc. in order to pass the first # trivial validations) # # Only create mutants that modify the token parameter name mutants = create_mutants(freq, [ token_value[::-1], ], False, token_pname_lst) for mutant in mutants: mutant_response = self._uri_opener.send_mutant(mutant) if not self._is_resp_equal(orig_response, mutant_response): return True return False
def _is_token_checked(self, freq, token, orig_response): ''' Please note that this method generates lots of false positives and negatives. Read the github issue for more information. :see: https://github.com/andresriancho/w3af/issues/120 :return: True if the CSRF token is NOT verified by the web application ''' token_pname_lst = token.keys() token_value = token[token_pname_lst[0]] # This will generate mutants for the original fuzzable request using # the reversed token value as a CSRF-token (this is a feature: we want # to make sure it has the same length as the original token and that # it has the same type: digits, hash, etc. in order to pass the first # trivial validations) # # Only create mutants that modify the token parameter name mutants = create_mutants(freq, [token_value[::-1],], False, token_pname_lst) for mutant in mutants: mutant_response = self._uri_opener.send_mutant(mutant) if not self._is_resp_equal(orig_response, mutant_response): return True return False
def test_urlparts_filename_path_qs(self): cf_singleton.save('fuzzable_headers', []) cf_singleton.save('fuzz_cookies', False) cf_singleton.save('fuzz_url_filenames', True) # This one changed cf_singleton.save('fuzzed_files_extension', 'gif') cf_singleton.save('fuzz_form_files', False) cf_singleton.save('fuzz_url_parts', True) # This one changed url = URL('http://moth/foo/bar.htm?id=1') freq = HTTPQSRequest(url) generated_mutants = create_mutants(freq, self.payloads) generated_uris = [m.get_uri().url_string for m in generated_mutants] expected_uris = [ 'http://moth/foo/bar.htm?id=abc', 'http://moth/foo/bar.htm?id=def', 'http://moth/foo/abc.htm', 'http://moth/foo/def.htm', 'http://moth/foo/bar.abc', 'http://moth/foo/bar.def', 'http://moth/abc/bar.htm', 'http://moth/def/bar.htm', 'http://moth/foo/abc', 'http://moth/foo/def', ] self.assertEqual(generated_uris, expected_uris)
def _with_time_delay(self, freq): ''' Tests an URL for OS Commanding vulnerabilities using time delays. :param freq: A FuzzableRequest ''' fake_mutants = create_mutants(freq, [ '', ]) for mutant in fake_mutants: if self._has_bug(mutant): continue for delay_obj in self._get_wait_commands(): ed = ExactDelayController(mutant, delay_obj, self._uri_opener) success, responses = ed.delay_is_controlled() if success: desc = 'OS Commanding was found at: %s' % mutant.found_at() v = Vuln.from_mutant('OS commanding vulnerability', desc, severity.HIGH, [r.id for r in responses], self.get_name(), mutant) v['os'] = delay_obj.get_OS() v['separator'] = delay_obj.get_separator() self.kb_append_uniq(self, 'os_commanding', v) break
def _fuzz_with_time_delay(self, freq): ''' Tests an URL for eval() usage vulnerabilities using time delays. :param freq: A FuzzableRequest ''' fake_mutants = create_mutants(freq, ['', ]) self.worker_pool.map(self._test_delay, fake_mutants)
def audit(self, freq, orig_response): ''' Tests an URL for ReDoS vulnerabilities using time delays. :param freq: A FuzzableRequest ''' if self.ignore_this_request(freq): return fake_mutants = create_mutants(freq, ['', ]) for mutant in fake_mutants: for delay_obj in self.get_delays(): adc = AproxDelayController(mutant, delay_obj, self._uri_opener, delay_setting=EXPONENTIALLY) success, responses = adc.delay_is_controlled() if success: # Now I can be sure that I found a vuln, we control the # response time with the delay desc = 'ReDoS was found at: %s' % mutant.found_at() response_ids = [r.id for r in responses] v = Vuln.from_mutant('ReDoS vulnerability', desc, severity.MEDIUM, response_ids, self.get_name(), mutant) self.kb_append_uniq(self, 'redos', v) break
def test_filename_fname_qs(self): cf_singleton.save('fuzzable_headers', []) cf_singleton.save('fuzz_cookies', False) cf_singleton.save('fuzz_url_filenames', True) # This one changed cf_singleton.save('fuzzed_files_extension', 'gif') cf_singleton.save('fuzz_form_files', False) cf_singleton.save('fuzz_url_parts', False) url = URL('http://moth/foo.htm?id=1') freq = HTTPQSRequest(url) generated_mutants = create_mutants(freq, self.payloads) expected_urls = [u'http://moth/foo.htm?id=abc', u'http://moth/foo.htm?id=def', u'http://moth/abc.htm', u'http://moth/def.htm', u'http://moth/foo.abc', u'http://moth/foo.def', ] generated_urls = [m.get_uri().url_string for m in generated_mutants] self.assertEqual(generated_urls, expected_urls) self.assertTrue(all(isinstance(m, QSMutant) or isinstance(m, FileNameMutant) for m in generated_mutants))
def _fuzz_with_time_delay(self, freq): ''' Tests an URL for eval() usage vulnerabilities using time delays. :param freq: A FuzzableRequest ''' fake_mutants = create_mutants(freq, [ '', ]) self.worker_pool.map(self._test_delay, fake_mutants)
def audit(self, freq, orig_response): ''' Tests an URL for buffer overflow vulnerabilities. :param freq: A FuzzableRequest ''' mutants = create_mutants(freq, self.BUFFER_TESTS, orig_resp=orig_response) self.worker_pool.map(self._send_request, mutants)
def audit(self, freq, orig_response): ''' Tests an URL for response splitting vulnerabilities. :param freq: A fuzzable_request ''' mutants = create_mutants(freq, self.HEADER_INJECTION_TESTS) self._send_mutants_in_threads(self._uri_opener.send_mutant, mutants, self._analyze_result)
def audit(self, freq, orig_response): ''' Tests an URL for global redirect vulnerabilities. :param freq: A FuzzableRequest object ''' mutants = create_mutants(freq, self.TEST_URLS) self._send_mutants_in_threads(self._uri_opener.send_mutant, mutants, self._analyze_result)
def audit(self, freq, orig_response): ''' Find those phishing vectors! :param freq: A FuzzableRequest ''' mutants = create_mutants(freq, self._test_urls) self._send_mutants_in_threads(self._uri_opener.send_mutant, mutants, self._analyze_result)
def audit(self, freq, orig_response): ''' Tests an URL for xpath injection vulnerabilities. :param freq: A FuzzableRequest ''' xpath_strings = self._get_xpath_strings() mutants = create_mutants(freq, xpath_strings, orig_resp=orig_response) self._send_mutants_in_threads(self._uri_opener.send_mutant, mutants, self._analyze_result)
def _fuzz_with_echo(self, freq, orig_response): ''' Tests an URL for eval() usage vulnerabilities using echo strings. :param freq: A FuzzableRequest ''' print_strings = [pstr % (self._rnd, ) for pstr in self.PRINT_STRINGS] mutants = create_mutants(freq, print_strings, orig_resp=orig_response) self._send_mutants_in_threads(self._uri_opener.send_mutant, mutants, self._analyze_echo)
def audit(self, freq, orig_response): ''' Tests an URL for SQL injection vulnerabilities. :param freq: A FuzzableRequest ''' mutants = create_mutants(freq, self.SQLI_STRINGS, orig_resp=orig_response) self._send_mutants_in_threads(self._uri_opener.send_mutant, mutants, self._analyze_result)
def audit(self, freq, orig_response): ''' Tests an URL for XSS vulnerabilities. :param freq: A FuzzableRequest ''' fake_mutants = create_mutants(freq, ['',]) # Run this in the worker pool in order to get different # parameters tested at the same time. self.worker_pool.map(self._check_xss_in_parameter, fake_mutants)
def _fuzz_with_echo(self, freq, orig_response): ''' Tests an URL for eval() usage vulnerabilities using echo strings. :param freq: A FuzzableRequest ''' print_strings = [pstr % (self._rnd,) for pstr in self.PRINT_STRINGS] mutants = create_mutants(freq, print_strings, orig_resp=orig_response) self._send_mutants_in_threads(self._uri_opener.send_mutant, mutants, self._analyze_echo)
def audit(self, freq, orig_response): ''' Tests an URL for LDAP injection vulnerabilities. :param freq: A FuzzableRequest ''' mutants = create_mutants(freq, self.LDAPI_STRINGS, orig_resp=orig_response) self._send_mutants_in_threads(self._uri_opener.send_mutant, mutants, self._analyze_result)
def audit(self, freq, orig_response): ''' Tests an URL for unsafe usage of PHP's preg_replace. :param freq: A FuzzableRequest ''' # First I check If I get the error message from php mutants = create_mutants(freq, self.PREG_PAYLOAD, orig_resp=orig_response) self._send_mutants_in_threads(self._uri_opener.send_mutant, mutants, self._analyze_result)
def test_urlparts_no_path(self): cf_singleton.save('fuzzable_headers', []) cf_singleton.save('fuzz_cookies', False) cf_singleton.save('fuzz_url_filenames', False) cf_singleton.save('fuzzed_files_extension', 'gif') cf_singleton.save('fuzz_form_files', False) cf_singleton.save('fuzz_url_parts', True) # This one changed url = URL('http://moth/') freq = HTTPQSRequest(url) generated_mutants = create_mutants(freq, self.payloads) self.assertEqual(generated_mutants, [])
def _test_inclusion(self, freq, rfi_data, orig_response): ''' Checks a FuzzableRequest for remote file inclusion bugs. :param freq: The fuzzable request that we want to inject into :param rfi_data: A RFIData object with all the information about the RFI :return: None, vulnerabilities are stored in the KB in _analyze_result ''' rfi_url_list = self._mutate_rfi_urls(rfi_data.rfi_url) mutants = create_mutants(freq, rfi_url_list, orig_resp=orig_response) analyze_result_par = partial(self._analyze_result, rfi_data) self._send_mutants_in_threads(self._uri_opener.send_mutant, mutants, analyze_result_par)
def _with_echo(self, freq, orig_response): ''' Tests an URL for OS Commanding vulnerabilities using cat/type to write the content of a known file (i.e. /etc/passwd) to the HTML. :param freq: A FuzzableRequest ''' # Prepare the strings to create the mutants command_list = self._get_echo_commands() only_command_strings = [v.get_command() for v in command_list] mutants = create_mutants(freq, only_command_strings, orig_resp=orig_response) self._send_mutants_in_threads(self._uri_opener.send_mutant, mutants, self._analyze_echo)
def _search_xss(self, mutant): ''' Analyze the mutant for reflected XSS. @parameter mutant: A mutant that was used to test if the parameter was echoed back or not ''' xss_strings = [replace_randomize(i) for i in self.PAYLOADS] mutant_list = create_mutants( mutant.get_fuzzable_req(), xss_strings, fuzzable_param_list=[mutant.get_var()] ) self._send_mutants_in_threads(self._uri_opener.send_mutant, mutant_list, self._analyze_echo_result)
def audit(self, freq, orig_response): ''' Tests an URL for local file inclusion vulnerabilities. :param freq: A FuzzableRequest ''' # Which payloads do I want to send to the remote end? local_files = [] local_files.append(freq.get_url().get_file_name()) if not self._open_basedir: local_files.extend(self._get_local_file_list(freq.get_url())) mutants = create_mutants(freq, local_files, orig_resp=orig_response) self._send_mutants_in_threads(self._uri_opener.send_mutant, mutants, self._analyze_result, grep=False)
def test_qs_and_no_cookie(self): cf_singleton.save('fuzzable_headers', []) cf_singleton.save('fuzz_cookies', True) # This one changed cf_singleton.save('fuzz_url_filenames', False) cf_singleton.save('fuzzed_files_extension', 'gif') cf_singleton.save('fuzz_form_files', False) cf_singleton.save('fuzz_url_parts', False) url = URL('http://moth/?id=1') # But there is no cookie freq = HTTPQSRequest(url) generated_mutants = create_mutants(freq, self.payloads) expected_urls = ['http://moth/?id=abc', 'http://moth/?id=def'] generated_urls = [m.get_uri().url_string for m in generated_mutants] self.assertEqual(generated_urls, expected_urls)
def test_simple(self): cf_singleton.save('fuzzable_headers', []) cf_singleton.save('fuzz_cookies', False) cf_singleton.save('fuzz_url_filenames', False) cf_singleton.save('fuzzed_files_extension', 'gif') cf_singleton.save('fuzz_form_files', False) cf_singleton.save('fuzz_url_parts', False) url = URL('http://moth/?id=1') freq = HTTPQSRequest(url) generated_mutants = create_mutants(freq, self.payloads) expected_urls = ['http://moth/?id=abc', 'http://moth/?id=def'] generated_urls = [m.get_uri().url_string for m in generated_mutants] self.assertEqual(generated_urls, expected_urls) self.assertTrue( all(isinstance(m, QSMutant) for m in generated_mutants))
def audit(self, freq, orig_response): ''' Find all kind of bugs without using a fixed database of errors. :param freq: A FuzzableRequest ''' # First, get the original response and create the mutants mutants = create_mutants(freq, [ '', ], orig_resp=orig_response) for m in mutants: # First I check that the current modified parameter in the mutant # doesn't have an already reported vulnerability. I don't want to # report vulnerabilities more than once. if (m.get_url(), m.get_var()) in self._potential_vulns: continue # Now, we request the limit (something that doesn't exist) # If http://localhost/a.php?b=1 ; then I should request b=12938795 # (random number) # If http://localhost/a.php?b=abc ; then I should request b=hnv98yks # (random alnum) limit_response = self._get_limit_response(m) # Now I request something that could generate an error # If http://localhost/a.php?b=1 ; then I should request b=d'kcz'gj'"**5*(((*) # If http://localhost/a.php?b=abc ; then I should request b=d'kcz'gj'"**5*(((*) # # I also try to trigger errors by sending empty strings # If http://localhost/a.php?b=1 ; then I should request b= # If http://localhost/a.php?b=abc ; then I should request b= for error_string in self.ERROR_STRINGS: m.set_mod_value(error_string) error_response = self._uri_opener.send_mutant(m) # Now I compare responses self._analyze_responses(orig_response, limit_response, error_response, m)
def audit(self, freq, orig_response): ''' Tests an URL for server side inclusion vulnerabilities. :param freq: A FuzzableRequest ''' # Create the mutants to send right now, ssi_strings = self._get_ssi_strings() mutants = create_mutants(freq, ssi_strings, orig_resp=orig_response) # Used in end() to detect "persistent SSI" for mut in mutants: expected_result = self._extract_result_from_payload( mut.get_mod_value()) self._expected_res_mutant[expected_result] = mut self._freq_list.append(freq) # End of persistent SSI setup self._send_mutants_in_threads(self._uri_opener.send_mutant, mutants, self._analyze_result)
def audit(self, freq, orig_response): """ Searches for file upload vulns. :param freq: A FuzzableRequest """ if freq.get_method().upper() == "POST" and len(freq.get_file_vars()) != 0: for file_parameter in freq.get_file_vars(): fileh_filen_list = self._create_files() # Only file handlers are passed to the create_mutants functions file_handlers = [i[0] for i in fileh_filen_list] mutants = create_mutants(freq, file_handlers, fuzzable_param_list=[file_parameter]) for mutant in mutants: _, filename = os.path.split(mutant.get_mod_value().name) mutant.uploaded_file_name = filename self._send_mutants_in_threads(self._uri_opener.send_mutant, mutants, self._analyze_result) self._remove_files(fileh_filen_list)
def audit(self, freq, orig_response): ''' Tests an URL for blind SQL injection vulnerabilities. :param freq: A FuzzableRequest ''' # # Setup blind SQL injection detector objects # bsqli_resp_diff = blind_sqli_response_diff(self._uri_opener) bsqli_resp_diff.set_eq_limit(self._eq_limit) bsqli_time_delay = blind_sqli_time_delay(self._uri_opener) method_list = [bsqli_resp_diff, bsqli_time_delay] # # Use the objects to identify the vulnerabilities # fake_mutants = create_mutants(freq, [ '', ]) for mutant in fake_mutants: if self._has_sql_injection(mutant): # # If sqli.py was enabled and already detected a vulnerability # in this parameter, then it makes no sense to test it again # and report a duplicate to the user # continue for method in method_list: found_vuln = method.is_injectable(mutant) if found_vuln is not None: self.kb_append_uniq(self, 'blind_sqli', found_vuln) break
def audit(self, freq, orig_response): ''' Find all kind of bugs without using a fixed database of errors. :param freq: A FuzzableRequest ''' # First, get the original response and create the mutants mutants = create_mutants(freq, ['', ], orig_resp=orig_response) for m in mutants: # First I check that the current modified parameter in the mutant # doesn't have an already reported vulnerability. I don't want to # report vulnerabilities more than once. if (m.get_url(), m.get_var()) in self._potential_vulns: continue # Now, we request the limit (something that doesn't exist) # If http://localhost/a.php?b=1 ; then I should request b=12938795 # (random number) # If http://localhost/a.php?b=abc ; then I should request b=hnv98yks # (random alnum) limit_response = self._get_limit_response(m) # Now I request something that could generate an error # If http://localhost/a.php?b=1 ; then I should request b=d'kcz'gj'"**5*(((*) # If http://localhost/a.php?b=abc ; then I should request b=d'kcz'gj'"**5*(((*) # # I also try to trigger errors by sending empty strings # If http://localhost/a.php?b=1 ; then I should request b= # If http://localhost/a.php?b=abc ; then I should request b= for error_string in self.ERROR_STRINGS: m.set_mod_value(error_string) error_response = self._uri_opener.send_mutant(m) # Now I compare responses self._analyze_responses(orig_response, limit_response, error_response, m)
def audit(self, freq, orig_response): ''' Tests an URL for blind SQL injection vulnerabilities. :param freq: A FuzzableRequest ''' # # Setup blind SQL injection detector objects # bsqli_resp_diff = blind_sqli_response_diff(self._uri_opener) bsqli_resp_diff.set_eq_limit(self._eq_limit) bsqli_time_delay = blind_sqli_time_delay(self._uri_opener) method_list = [bsqli_resp_diff, bsqli_time_delay] # # Use the objects to identify the vulnerabilities # fake_mutants = create_mutants(freq, ['', ]) for mutant in fake_mutants: if self._has_sql_injection(mutant): # # If sqli.py was enabled and already detected a vulnerability # in this parameter, then it makes no sense to test it again # and report a duplicate to the user # continue for method in method_list: found_vuln = method.is_injectable(mutant) if found_vuln is not None: self.kb_append_uniq(self, 'blind_sqli', found_vuln) break
def test_form_file_post_no_files(self): cf_singleton.save('fuzzable_headers', []) cf_singleton.save('fuzz_cookies', False) cf_singleton.save('fuzz_url_filenames', False) cf_singleton.save('fuzzed_files_extension', 'gif') cf_singleton.save('fuzz_form_files', True) # This one changed cf_singleton.save('fuzz_url_parts', False) form = Form() form.add_input([("name", "username"), ("value", "")]) form.add_input([("name", "address"), ("value", "")]) freq = HTTPPostDataRequest(URL('http://www.w3af.com/?id=3'), dc=form, method='PUT') generated_mutants = create_mutants(freq, self.payloads) self.assertTrue(all('http://www.w3af.com/?id=3' == m.get_uri().url_string for m in generated_mutants)) self.assertTrue(all(isinstance(m, PostDataMutant) for m in generated_mutants), generated_mutants) self.assertTrue( all(m.get_method() == 'PUT' for m in generated_mutants)) expected_dc_lst = [Form( [('username', ['abc']), ('address', ['Bonsai Street 123'])]), Form([('username', [ 'def']), ('address', ['Bonsai Street 123'])]), Form([('username', [ 'John8212']), ('address', ['abc'])]), Form([('username', ['John8212']), ('address', ['def'])])] created_dc_lst = [i.get_dc() for i in generated_mutants] self.assertEqual(created_dc_lst, expected_dc_lst)