def test_variants_true_similar_params(self): # change the url by adding a querystring. shouldn't affect anything. url = self.url.url_join('?a=z') fr = FuzzableRequest(url, method='GET', dc={'a': ['1'], 'b': ['bb']}) fr_other = FuzzableRequest( self.url, method='GET', dc={'a': ['2'], 'b': ['cc']}) self.assertTrue(fr.is_variant_of(fr_other))
def __init__(self, uri, method="POST", headers=Headers(), cookie=None, dc=None): if dc is not None and not isinstance(dc, Form): msg = "The dc parameter for forms needs to be a Form instance," "got %s instead." % type(dc) TypeError(msg) FuzzableRequest.__init__(self, uri, method, headers, cookie, dc)
def test_mutant_creation_repeated_params(self): qs = QueryString([('a', ['1', '2']), ('b', ['3'])]) freq = FuzzableRequest(self.url) freq.set_querystring(qs) created_mutants = FakeMutant.create_mutants(freq, self.payloads, [], False, self.fuzzer_config) expected_dcs = ['a=abc&a=2&b=3', 'a=1&a=abc&b=3', 'a=1&a=2&b=abc', 'a=def&a=2&b=3', 'a=1&a=def&b=3', 'a=1&a=2&b=def'] created_dcs = [str(i.get_dc()) for i in created_mutants] self.assertEquals(expected_dcs, created_dcs) token_0 = created_mutants[0].get_token() self.assertIsInstance(token_0, DataToken) self.assertEqual(token_0.get_name(), 'a') self.assertEqual(token_0.get_original_value(), '1') self.assertEqual(token_0.get_value(), 'abc') token_1 = created_mutants[1].get_token() self.assertIsInstance(token_1, DataToken) self.assertEqual(token_1.get_name(), 'a') self.assertEqual(token_1.get_original_value(), '2') self.assertEqual(token_1.get_value(), 'abc')
def test_mutant_creation(self): qs = QueryString(self.SIMPLE_KV) freq = FuzzableRequest(self.url) freq.set_querystring(qs) created_mutants = FakeMutant.create_mutants(freq, self.payloads, [], False, self.fuzzer_config) expected_dcs = ['a=abc&b=2', 'a=1&b=abc', 'a=def&b=2', 'a=1&b=def'] created_dcs = [str(i.get_dc()) for i in created_mutants] self.assertEquals(expected_dcs, created_dcs) token_0 = created_mutants[0].get_token() self.assertIsInstance(token_0, DataToken) self.assertEqual(token_0.get_name(), 'a') self.assertEqual(token_0.get_original_value(), '1') self.assertEqual(token_0.get_value(), 'abc') token_2 = created_mutants[1].get_token() self.assertIsInstance(token_0, DataToken) self.assertEqual(token_2.get_name(), 'b') self.assertEqual(token_2.get_original_value(), '2') self.assertEqual(token_2.get_value(), 'abc') self.assertTrue(all(isinstance(m, Mutant) for m in created_mutants)) self.assertTrue(all(m.get_mutant_class() == 'FakeMutant' for m in created_mutants))
def test_audit_plugin_timeout_threads(self): """ I want to make sure that when stopit kills the real audit function, the threads which are called from it won't do anything strange. The plan is to scan something large with httpretty, with delays in the HTTP responses to simulate a slow network and a low PLUGIN_TIMEOUT to make the test quicker. """ plugin_inst = self.w3afcore.plugins.get_plugin_inst('audit', 'sqli') url = URL(self.target_url) freq = FuzzableRequest(url) orig_response = plugin_inst.get_original_response(freq) mod = 'w3af.core.controllers.plugins.audit_plugin.%s' with patch(mod % 'om.out') as om_mock,\ patch(mod % 'AuditPlugin.PLUGIN_TIMEOUT', new_callable=PropertyMock) as timeout_mock: timeout_mock.return_value = 2 plugin_inst.audit_with_copy(freq, orig_response) msg = '[timeout] The "%s" plugin took more than %s seconds to'\ ' complete the analysis of "%s", killing it!' error = msg % (plugin_inst.get_name(), plugin_inst.PLUGIN_TIMEOUT, freq.get_url()) self.assertIn(call.debug(error), om_mock.mock_calls)
def test_add_QsRequest(self): ds = DiskSet() uri = URL('http://w3af.org/?id=2') hdr = Headers([('Referer', 'http://w3af.org/')]) qsr1 = FuzzableRequest(uri, method='GET', headers=hdr) uri = URL('http://w3af.org/?id=3') qsr2 = FuzzableRequest(uri, method='GET', headers=hdr) uri = URL('http://w3af.org/?id=7') qsr3 = FuzzableRequest(uri, method='FOO', headers=hdr) ds.add(qsr1) ds.add(qsr2) ds.add(qsr2) ds.add(qsr1) self.assertEqual(ds[0], qsr1) self.assertEqual(ds[1], qsr2) self.assertFalse(qsr3 in ds) self.assertTrue(qsr2 in ds) self.assertEqual(len(ds), 2) # This forces an internal change in the URL object qsr2.get_url().url_string self.assertIn(qsr2, ds)
def test_export_with_dc(self): fr = FuzzableRequest(URL("http://www.w3af.com/")) d = DataContainer() d['a'] = ['1',] fr.set_dc(d) self.assertEqual(fr.export(), 'GET,http://www.w3af.com/?a=1,')
def test_find_csrf_token_true_simple(self): url = URL('http://moth/w3af/audit/csrf/') query_string = parse_qs('secret=f842eb01b87a8ee18868d3bf80a558f3') freq = FuzzableRequest(url, method='GET') freq.set_querystring(query_string) token = self.csrf_plugin._find_csrf_token(freq) self.assertIn('secret', token)
def test_find_csrf_token_false(self): url = URL('http://moth/w3af/audit/csrf/') query_string = parse_qs('secret=not a token') freq = FuzzableRequest(url, method='GET') freq.set_querystring(query_string) token = self.csrf_plugin._find_csrf_token(freq) self.assertNotIn('secret', token)
def test_sent_post_data(self): form_params = FormParameters() form_params.add_field_by_attr_items([("name", "username"), ("value", """d'z"0""")]) form_params.add_field_by_attr_items([("name", "address"), ("value", "")]) form = dc_from_form_params(form_params) f = FuzzableRequest(URL('http://example.com/'), post_data=form) self.assertTrue(f.sent('d%5C%27z%5C%220'))
def test_export_import_with_post_data(self): dc = KeyValueContainer(init_val=[('a', ['1'])]) fr = FuzzableRequest(URL("http://www.w3af.com/"), post_data=dc) self.assertEqual(fr.to_csv(), '"GET","http://www.w3af.com/","a=1"') raise SkipTest('Failing because we do NOT export headers') imported_fr = fr.from_csv(fr.to_csv()) self.assertEqual(imported_fr, fr)
def test_mutant_creation_empty_dc(self): qs = QueryString() freq = FuzzableRequest(self.url) freq.set_querystring(qs) created_mutants = FakeMutant.create_mutants(freq, self.payloads, [], False, self.fuzzer_config) expected_dc_lst = [] created_dc_lst = [i.get_dc() for i in created_mutants] self.assertEqual(created_dc_lst, expected_dc_lst)
def test_basic(self): freq = FuzzableRequest(URL('http://www.w3af.com/')) fake_ref = 'http://w3af.org/' mutant = HeadersMutant(freq.copy()) mutant.set_var('Referer') original_referer = freq.get_referer() mutant.set_original_value(original_referer) mutant.set_mod_value(fake_ref) self.assertEqual(mutant.get_headers()['Referer'], fake_ref) self.assertEqual(mutant.get_original_value(), original_referer)
def test_dump_case01(self): expected = '\r\n'.join(['GET http://w3af.com/a/b/c.php HTTP/1.1', 'Hello: World', '', '']) headers = Headers([('Hello', 'World')]) #TODO: Note that I'm passing a dc to the FuzzableRequest and it's not # appearing in the dump. It might be a bug... fr = FuzzableRequest(self.url, method='GET', dc={'a': ['b']}, headers=headers) self.assertEqual(fr.dump(), expected)
def test_dump_case02(self): expected = u'\r\n'.join([u'GET http://w3af.com/a/b/c.php HTTP/1.1', u'Hola: Múndo', u'', u'']) headers = Headers([(u'Hola', u'Múndo')]) #TODO: Note that I'm passing a dc to the FuzzableRequest and it's not # appearing in the dump. It might be a bug... fr = FuzzableRequest(self.url, method='GET', dc={u'á': ['b']}, headers=headers) self.assertEqual(fr.dump(), expected)
def test_dump_case02(self): expected = u'\r\n'.join([u'GET http://w3af.com/a/b/c.php HTTP/1.1', u'Hola: Múndo', u'', u'a=b']) headers = Headers([(u'Hola', u'Múndo')]) post_data = KeyValueContainer(init_val=[('a', ['b'])]) fr = FuzzableRequest(self.url, method='GET', post_data=post_data, headers=headers) self.assertEqual(fr.dump(), expected.encode('utf-8'))
def test_mutant_creation_ignore_params(self): qs = QueryString(self.SIMPLE_KV) freq = FuzzableRequest(self.url) freq.set_querystring(qs) created_mutants = FakeMutant.create_mutants(freq, self.payloads, ['a'], False, self.fuzzer_config) expected_dcs = ['a=abc&b=2', 'a=def&b=2'] created_dcs = [str(i.get_dc()) for i in created_mutants] self.assertEqual(expected_dcs, created_dcs)
def test_mutant_copy(self): qs = QueryString(self.SIMPLE_KV) freq = FuzzableRequest(self.url) freq.set_querystring(qs) mutant = FakeMutant(freq) mutant.set_token(('a', 0)) mutant_copy = mutant.copy() self.assertEqual(mutant, mutant_copy) self.assertEqual(mutant.get_token(), mutant_copy.get_token()) self.assertIsNot(None, mutant_copy.get_token())
def test_basic(self): referer_1 = 'http://w3af.org/' referer_2 = 'http://spam.w3af.org/' freq = FuzzableRequest(URL('http://www.w3af.com/'), headers=Headers([('Referer', referer_1)])) self.assertEqual(freq.get_referer(), referer_1) m = HeadersMutant(freq) m.get_dc().set_token(('Referer',)) m.set_token_value(referer_2) self.assertEqual(m.get_token_value(), referer_2)
def test_dump_case03(self): header_value = ''.join(chr(i) for i in xrange(256)) expected = u'\r\n'.join([u'GET http://w3af.com/a/b/c.php HTTP/1.1', u'Hola: %s' % smart_unicode(header_value), u'', u'a=b']) headers = Headers([(u'Hola', header_value)]) post_data = KeyValueContainer(init_val=[('a', ['b'])]) fr = FuzzableRequest(self.url, method='GET', post_data=post_data, headers=headers) self.assertEqual(fr.dump(), expected)
def test_mutant_creation_append(self): qs = QueryString(self.SIMPLE_KV) freq = FuzzableRequest(self.url) freq.set_querystring(qs) created_mutants = FakeMutant.create_mutants(freq, self.payloads, [], True, self.fuzzer_config) expected_dcs = ['a=1abc&b=2', 'a=1&b=2abc', 'a=1def&b=2', 'a=1&b=2def',] created_dcs = [str(i.get_dc()) for i in created_mutants] self.assertEquals(expected_dcs, created_dcs)
def test_dump_case03(self): header_value = ''.join(chr(i) for i in xrange(256)) expected = u'\r\n'.join([u'GET http://w3af.com/a/b/c.php HTTP/1.1', u'Hola: %s' % smart_unicode(header_value), u'', u'']) headers = Headers([(u'Hola', header_value)]) #TODO: Note that I'm passing a dc to the FuzzableRequest and it's not # appearing in the dump. It might be a bug... fr = FuzzableRequest(self.url, method='GET', dc={u'a': ['b']}, headers=headers) self.assertEqual(fr.dump(), expected)
def crawl(self, fuzzable_request): """ Get the sitemap.xml file and parse it. :param fuzzable_request: A fuzzable_request instance that contains (among other things) the URL to test. """ base_url = fuzzable_request.get_url().base_url() sitemap_url = base_url.url_join('sitemap.xml') response = self._uri_opener.GET(sitemap_url, cache=True) if '</urlset>' in response and not is_404(response): # Send response to core fr = FuzzableRequest.from_http_response(response) self.output_queue.put(fr) om.out.debug('Parsing xml file with xml.dom.minidom.') try: dom = xml.dom.minidom.parseString(response.get_body()) except: raise BaseFrameworkException('Error while parsing sitemap.xml') else: raw_url_list = dom.getElementsByTagName("loc") parsed_url_list = [] for url in raw_url_list: try: url = url.childNodes[0].data url = URL(url) except ValueError, ve: msg = 'Sitemap file had an invalid URL: "%s"' om.out.debug(msg % ve) except: om.out.debug('Sitemap file had an invalid format')
def test_str_with_postdata(self): headers = Headers([('content-type', URLEncodedForm.ENCODING)]) fr = FuzzableRequest.from_parts("http://www.w3af.com/", post_data='a=1', headers=headers) expected = 'Method: GET | http://www.w3af.com/ | URL encoded ' \ 'form: (a)' self.assertEqual(str(fr), expected)
def _get_request_response_from_work_unit(self, work_unit): """ In some cases the work unit is a tuple with request / response instances. In other cases it is an ID, which needs to be queried from the History DB to get the request / response. :param work_unit: One of the options explained above :return: A request / response tuple """ if not isinstance(work_unit, int): request, response = work_unit else: # Before we sent requests and responses as work units, # but since we changed from Queue to CachedQueue for BaseConsumer # the database was growing really big (1GB) for storing that traffic # and I decided to migrate to using just the response.id and querying # the SQLite one extra time. history = HistoryItem() request, response = history.load_from_file(work_unit) # Create a fuzzable request based on the urllib2 request object headers_inst = Headers(request.header_items()) request = FuzzableRequest.from_parts(request.url_object, request.get_method(), request.get_data() or '', headers_inst) return request, response
def _check_if_exists(self, web_shell_url): """ Check if the file exists. :param web_shell_url: The URL to check """ try: response = self._uri_opener.GET(web_shell_url, cache=True) except BaseFrameworkException: om.out.debug('Failed to GET webshell:' + web_shell_url) else: if self._is_possible_backdoor(response): desc = 'A web backdoor was found at: "%s"; this could ' \ 'indicate that the server has been compromised.' desc = desc % response.get_url() v = Vuln('Potential web backdoor', desc, severity.HIGH, response.id, self.get_name()) v.set_url(response.get_url()) kb.kb.append(self, 'backdoors', v) om.out.vulnerability(v.get_desc(), severity=v.get_severity()) fr = FuzzableRequest.from_http_response(response) self.output_queue.put(fr)
def test_simplest(self): fr = FuzzableRequest.from_parts(self.url) self.assertEqual(fr.get_url(), self.url) self.assertEqual(fr.get_headers(), Headers()) self.assertEqual(fr.get_method(), "GET") self.assertIsInstance(fr.get_raw_data(), KeyValueContainer)
def _extract_html_forms(self, resp, fuzzable_req): """ Parses the HTTP response body and extract HTML forms, resulting forms are put() on the output queue. """ # Try to find forms in the document try: dp = parser_cache.dpc.get_document_parser_for(resp) except BaseFrameworkException: # Failed to find a suitable parser for the document return # Create one FuzzableRequest for each form variant mode = cf.cf.get('form_fuzzing_mode') for form_params in dp.get_forms(): # Form exclusion #15161 form_id_json = form_params.get_form_id().to_json() om.out.debug('A new form was found! Form-id is: "%s"' % form_id_json) if not self._should_analyze_url(form_params.get_action()): continue headers = fuzzable_req.get_headers() for form_params_variant in form_params.get_variants(mode): data_container = dc_from_form_params(form_params_variant) # Now data_container is one of Multipart of URLEncoded form # instances, which is a DataContainer. Much better than the # FormParameters instance we had before in form_params_variant r = FuzzableRequest.from_form(data_container, headers=headers) self.output_queue.put(r)
def _extract_html_forms(self, resp, fuzzable_req): """ Parses the HTTP response body and extract HTML forms, resulting forms are put() on the output queue. """ # Try to find forms in the document try: dp = parser_cache.dpc.get_document_parser_for(resp) except BaseFrameworkException: # Failed to find a suitable parser for the document return same_domain = lambda f: f.get_action().get_domain() == \ resp.get_url().get_domain() # Create one FuzzableRequest for each form variant mode = cf.cf.get('form_fuzzing_mode') for form_params in dp.get_forms(): if not same_domain(form_params): continue headers = fuzzable_req.get_headers() for form_params_variant in form_params.get_variants(mode): data_container = dc_from_form_params(form_params_variant) # Now data_container is one of Multipart of URLEncoded form # instances, which is a DataContainer. Much better than the # FormParameters instance we had before in form_params_variant r = FuzzableRequest.from_form(data_container, headers=headers) self.output_queue.put(r)
def _do_request(self, url, mutant): """ Perform a simple GET to see if the result is an error or not, and then run the actual fuzzing. """ response = self._uri_opener.GET( mutant, cache=True, headers=self._headers) if not (is_404(response) or response.get_code() in (403, 401) or self._return_without_eval(mutant)): # Create the fuzzable request and send it to the core fr = FuzzableRequest.from_http_response(response) self.output_queue.put(fr) # # Save it to the kb (if new)! # if response.get_url() not in self._seen and response.get_url().get_file_name(): desc = 'A potentially interesting file was found at: "%s".' desc = desc % response.get_url() i = Info('Potentially interesting file', desc, response.id, self.get_name()) i.set_url(response.get_url()) kb.kb.append(self, 'files', i) om.out.information(i.get_desc()) # Report only once self._seen.add(response.get_url())
def test_analyze_cookies_with_httponly_case_sensitive_expires(self): body = '' url = URL('https://www.w3af.com/') c = 'name2=value2; Expires=Wed, 09-Jun-2021 10:18:14 GMT;Secure;HttpOnly' headers = {'content-type': 'text/html', 'Set-Cookie': c} headers = Headers(headers.items()) response = HTTPResponse(200, body, headers, url, url, _id=1) request = FuzzableRequest(url, method='GET') self.plugin.grep(request, response) self.assertEqual(len(kb.kb.get('analyze_cookies', 'cookies')), 1) self.assertEqual(len(kb.kb.get('analyze_cookies', 'security')), 0)
def test_analyze_cookies_empty(self): body = '' url = URL('http://www.w3af.com/') headers = Headers({ 'content-type': 'text/html', 'Set-Cookie': '' }.items()) response = HTTPResponse(200, body, headers, url, url, _id=1) request = FuzzableRequest(url, method='GET') self.plugin.grep(request, response) self.assertEqual(len(kb.kb.get('analyze_cookies', 'cookies')), 1) self.assertEqual(len(kb.kb.get('analyze_cookies', 'invalid-cookies')), 0)
def _fingerprint_installer(self, domain_path, wp_unique_url, response): """ GET latest.zip and latest.tar.gz and compare with the hashes from the release.db that was previously generated from wordpress.org [0] and contains all release hashes. This gives the initial wordpress version, not the current one. [0] http://wordpress.org/download/release-archive/ """ zip_url = domain_path.url_join('latest.zip') tar_gz_url = domain_path.url_join('latest.tar.gz') install_urls = [zip_url, tar_gz_url] for install_url in install_urls: response = self._uri_opener.GET(install_url, cache=True, respect_size_limit=False) # md5sum the response body m = hashlib.md5() m.update(response.get_body()) remote_release_hash = m.hexdigest() release_db = self._release_db for line in file(release_db): try: line = line.strip() release_db_hash, release_db_name = line.split(',') except: continue if release_db_hash == remote_release_hash: desc = ('The sysadmin used WordPress version "%s" during the' ' installation, which was found by matching the contents' ' of "%s" with the hashes of known releases. If the' ' sysadmin did not update wordpress, the current version' ' will still be the same.') desc %= (release_db_name, install_url) i = Info('Fingerprinted Wordpress version', desc, response.id, self.get_name()) i.set_url(install_url) kb.kb.append(self, 'info', i) om.out.information(i.get_desc()) # Send link to core fr = FuzzableRequest(response.get_uri()) self.output_queue.put(fr)
def _brute_worker(self, url, combination): """ Try a user/password combination with HTTP basic authentication against a specific URL. :param url: A string representation of an URL :param combination: A tuple that contains (user,pass) """ # Remember that this worker is called from a thread which lives in a # threadpool. If the worker finds something, it has to let the rest know # and the way we do that is by setting self._found. # # If one thread sees that we already bruteforced the access, the rest # will simply no-op if not self._found or not self._stop_on_first: user, passwd = combination raw_values = "%s:%s" % (user, passwd) auth = 'Basic %s' % base64.b64encode(raw_values).strip() headers = Headers([('Authorization', auth)]) fr = FuzzableRequest(url, headers=headers, method='GET') try: response = self._uri_opener.send_mutant(fr, cache=False, grep=False) except BaseFrameworkException, w3: msg = 'Exception while brute-forcing basic authentication,' \ ' error message: "%s".' om.out.debug(msg % w3) else: # GET was OK if response.get_code() != 401: self._found = True desc = 'Found authentication credentials to: "%s".' \ ' A valid user and password combination is: %s/%s .' desc = desc % (url, user, passwd) v = Vuln('Guessable credentials', desc, severity.HIGH, response.id, self.get_name()) v.set_url(url) v['user'] = user v['pass'] = passwd v['response'] = response v['request'] = fr kb.kb.append(self, 'auth', v) om.out.vulnerability(v.get_desc(), severity=v.get_severity())
def test_analyze_cookies_no_httponly(self): body = '' url = URL('http://www.w3af.com/1') headers = Headers({ 'content-type': 'text/html', 'Set-Cookie': 'abc=def' }.items()) response = HTTPResponse(200, body, headers, url, url, _id=1) request = FuzzableRequest(url, method='GET') self.plugin.grep(request, response) url = URL('http://www.w3af.com/2') headers = Headers({ 'content-type': 'text/html', 'Set-Cookie': 'abc=def' }.items()) response = HTTPResponse(200, body, headers, url, url, _id=2) request = FuzzableRequest(url, method='GET') self.plugin.grep(request, response) http_only = kb.kb.get('analyze_cookies', 'http_only') self.assertEqual(len(http_only), 1) self.assertEqual(len(kb.kb.get('analyze_cookies', 'cookies')), 1) self.assertEqual(len(kb.kb.get('analyze_cookies', 'invalid-cookies')), 0) info_set = http_only[0] expected_desc = u'The application sent the "abc" cookie without the' \ u' HttpOnly flag in 2 different responses. The' \ u' HttpOnly flag prevents potential intruders from' \ u' accessing the cookie value through Cross-Site' \ u' Scripting attacks. The first ten URLs which sent' \ u' the insecure cookie are:\n' \ u' - http://www.w3af.com/2\n - http://www.w3af.com/1\n' self.assertEqual(info_set.get_desc(), expected_desc) self.assertEqual(info_set.get_id(), [1, 2]) self.assertEqual(len(info_set.infos), 2)
def test_is_suitable(self): # False because no cookie is set and no QS nor post-data url = URL('http://moth/') req = FuzzableRequest(url, method='GET') suitable = self.csrf_plugin._is_suitable(req) self.assertFalse(suitable) # False because no cookie is set url = URL('http://moth/?id=3') req = FuzzableRequest(url, method='GET') suitable = self.csrf_plugin._is_suitable(req) self.assertFalse(suitable) url_sends_cookie = URL( 'http://moth/w3af/core/cookie_handler/set-cookie.php') self.uri_opener.GET(url_sends_cookie) # Still false because it doesn't have any QS or POST data url = URL('http://moth/') req = FuzzableRequest(url, method='GET') suitable = self.csrf_plugin._is_suitable(req) self.assertFalse(suitable) self.csrf_plugin._strict_mode = True # Still false because of the strict mode url = URL('http://moth/?id=3') req = FuzzableRequest(url, method='GET') suitable = self.csrf_plugin._is_suitable(req) self.assertFalse(suitable) # False, no items in post-data url = URL('http://moth/') req = FuzzableRequest(url, method='POST', post_data=URLEncodedForm()) suitable = self.csrf_plugin._is_suitable(req) self.assertFalse(suitable) # True, items in DC, POST (passes strict mode) and cookies url = URL('http://moth/') form_params = FormParameters() form_params.add_input([('name', 'test'), ('type', 'text')]) form = URLEncodedForm(form_params) req = FuzzableRequest(url, method='POST', post_data=form) suitable = self.csrf_plugin._is_suitable(req) self.assertTrue(suitable) self.csrf_plugin._strict_mode = False # True now that we have strict mode off, cookies and QS url = URL('http://moth/?id=3') req = FuzzableRequest(url, method='GET') suitable = self.csrf_plugin._is_suitable(req) self.assertTrue(suitable)
def test_analyze_cookies_with_httponly_case_sensitive(self): body = '' url = URL('https://www.w3af.com/') headers = Headers({ 'content-type': 'text/html', 'Set-Cookie': 'abc=def;Secure;HttpOnly' }.items()) response = HTTPResponse(200, body, headers, url, url, _id=1) request = FuzzableRequest(url, method='GET') self.plugin.grep(request, response) self.assertEqual(len(kb.kb.get('analyze_cookies', 'cookies')), 1) self.assertEqual(len(kb.kb.get('analyze_cookies', 'http_only')), 0)
def test_sl_3(self, *args): """ Static link 3, text/javascript """ body = 'function { ws_url =' \ '"wss://www.example.com/socketserver:8080";' \ 'wslink = new WebSocket(url); return wslink} ' url = URL('https://www.w3af.com/') headers = Headers([('content-type', 'text/javascript')]) response = HTTPResponse(200, body, headers, url, url, _id=1) request = FuzzableRequest(url, method='GET') self.plugin.grep(request, response) self.assertEqual(len(kb.kb.get('websockets_links', 'websockets_links')), 1)
def test_clean_form_fuzzable_request_form(self): form_params = FormParameters() form_params.add_field_by_attr_items([("name", "username"), ("value", "abc")]) form_params.add_field_by_attr_items([("name", "address"), ("value", "")]) form_params.set_action(URL('http://example.com/?id=1')) form_params.set_method('post') form = dc_from_form_params(form_params) fr = FuzzableRequest.from_form(form) expected = u'(POST)-http://example.com/' \ u'?id=number!username=string&address=string' self.assertEqual(clean_fuzzable_request(fr), expected)
def test_https_with_ect(self): body = '' url = URL('https://www.w3af.com/') headers = Headers([ ('content-type', 'text/html'), ('expect-ct', 'max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"' ) ]) request = FuzzableRequest(url, method='GET') resp = HTTPResponse(200, body, headers, url, url, _id=1) self.plugin.grep(request, resp) self.assertEquals(len(kb.kb.get('expect_ct', 'expect_ct')), 0)
def test_not_php_serialized_objects(self): # Note that I'm sending the serialized object in reverse string order post_data = 'obj=%s' % base64.b64encode( SERIALIZED_PHP_OBJECTS[1][::-1]) headers = Headers([('Content-Type', 'application/x-www-form-urlencoded')]) form = URLEncodedForm.from_postdata(headers, post_data) request = FuzzableRequest(self.url, headers=headers, post_data=form) self.plugin.grep(request, self.response) self.assertEquals( len(kb.kb.get('serialized_object', 'serialized_object')), 0)
def test_handle_exception(self): url = URL('http://moth/') fr = FuzzableRequest(url) try: raise Exception() except Exception as e: self.bc.handle_exception('audit', 'sqli', fr, e) exception_data = self.bc.out_queue.get() self.assertTrue(exception_data.traceback is not None) self.assertEqual(exception_data.phase, 'audit') self.assertEqual(exception_data.plugin, 'sqli') self.assertEqual(exception_data.exception, e)
def test_json_post(self): post_data = '{"1":"2"}' hdr = Headers([('content-length', str(len(post_data))), ('content-type', 'application/json')]) fr = FuzzableRequest.from_parts(self.url, headers=hdr, post_data=post_data, method='POST') self.assertEqual(fr.get_url(), self.url) self.assertEqual(fr.get_headers(), hdr) self.assertEqual(fr.get_method(), 'POST') self.assertIsInstance(fr.get_raw_data(), JSONContainer)
def _grep(self, request, response): url_instance = request.url_object domain = url_instance.get_domain() if self._grep_queue_put is not None and \ domain in cf.cf.get('target_domains'): # Create a fuzzable request based on the urllib2 request object headers_inst = Headers(request.header_items()) fr = FuzzableRequest.from_parts(url_instance, request.get_method(), request.get_data() or '', headers_inst) self._grep_queue_put((fr, response))
def create_fuzzable_request(_id): url = 'http://example.com/product/1' form_params = FormParameters() form_params.add_field_by_attr_items([("name", "username%s" % _id), ("value", "abc")]) form_params.add_field_by_attr_items([("name", "address"), ("value", "")]) form_params.set_action(URL(url)) form_params.set_method('post') form = dc_from_form_params(form_params) return FuzzableRequest.from_form(form)
def test_url_session_in_url(self): body = 'abc' url = URL('http://www.w3af.com/?JSESSIONID=231badb19b93e44f47da1bd64a8147f2') headers = Headers([('content-type', 'text/html')]) request = FuzzableRequest(url, method='GET') resp = HTTPResponse(200, body, headers, url, url, _id=1) self.plugin.grep(request, resp) infos = kb.kb.get('url_session', 'url_session') self.assertEquals(len(infos), 1) info = infos[0] self.assertEqual(info.get_name(), 'Session ID in URL')
def audit(self, freq, orig_response): url = URL(freq.get_url() + self._PAYLOAD) print 'the url is %s', url freq_new = FuzzableRequest(url, method='GET') response = self._uri_opener.send_mutant(freq_new) print response.get_body() if 'root:/root' in response.get_body(): msg = 'Directory Traversal Vulnerbility found at ' + freq.get_url() v = Vuln.from_fr('Directory Traversal vulnerability', msg, severity.MEDIUM, orig_response.id, self.get_name(), freq) print 'hello there' self.kb_append_uniq(self, 'directory_traversal', v)
def test_mutant_creation(self): form_params = FormParameters() form_params.add_field_by_attr_items([("name", "username"), ("value", "")]) form_params.add_field_by_attr_items([("name", "address"), ("value", "")]) form = URLEncodedForm(form_params) freq = FuzzableRequest(URL('http://www.w3af.com/?id=3'), post_data=form, method='PUT') created_mutants = PostDataMutant.create_mutants( freq, self.payloads, [], False, self.fuzzer_config) expected_dcs = [ 'username=def&address=Bonsai%20Street%20123', 'username=abc&address=Bonsai%20Street%20123', 'username=John8212&address=def', 'username=John8212&address=abc' ] created_dcs = [str(i.get_dc()) for i in created_mutants] self.assertEqual(set(created_dcs), set(expected_dcs)) token = created_mutants[0].get_token() self.assertEqual(token.get_name(), 'username') self.assertEqual(token.get_original_value(), '') self.assertEqual(token.get_value(), 'abc') token = created_mutants[1].get_token() self.assertEqual(token.get_name(), 'address') self.assertEqual(token.get_original_value(), '') self.assertEqual(token.get_value(), 'abc') token = created_mutants[2].get_token() self.assertEqual(token.get_name(), 'username') self.assertEqual(token.get_original_value(), '') self.assertEqual(token.get_value(), 'def') token = created_mutants[3].get_token() self.assertEqual(token.get_name(), 'address') self.assertEqual(token.get_original_value(), '') self.assertEqual(token.get_value(), 'def') for m in created_mutants: self.assertIsInstance(m, PostDataMutant) for m in created_mutants: self.assertEqual(m.get_method(), 'PUT')
def test_image_with_image_content_type(self, *args): """ Verify that our plugins don't break when we send them an image. """ file_path = os.path.join(ROOT_PATH, 'plugins', 'tests', 'grep', 'data', 'w3af.png') body = file(file_path).read() hdrs = Headers({'Content-Type': 'image/png'}.items()) response = HTTPResponse(200, body, hdrs, self.url_inst, self.url_inst, _id=random.randint(1, 5000)) request = FuzzableRequest(self.url_inst) for pinst in self._plugins: pinst.grep(request, response)
def test_php_serialized_objects_query_string(self): for i, obj in enumerate(SERIALIZED_PHP_OBJECTS): url = self.url.copy() qs = QueryString([(str(i), [obj])]) url.set_querystring(qs) request = FuzzableRequest(url) self.plugin.grep(request, self.response) self.assertEquals(len(kb.kb.get('serialized_object', 'serialized_object')), 2)
def test_vs4(self, *args): body = 'header <form action="http://www.w3af.com/"><div>' \ '<input type="password" name="passwd" /></div></form>footer' url = URL('https://www.w3af.com/') headers = Headers([('content-type', 'text/html')]) response = HTTPResponse(200, body, headers, url, url, _id=1) request = FuzzableRequest(url, method='GET') self.plugin.grep(request, response) self.assertEqual( len(kb.kb.get('form_cleartext_password', 'form_cleartext_password')), 1) self.assertEqual( kb.kb.get('form_cleartext_password', 'form_cleartext_password') [0].get_name() == 'Insecure password submission over HTTP', 1)
def test_n1(self, *args): """ Not vulnerable """ body = 'header <form action="https://www.w3af.com/">' \ '<input type="text" /></form>footer' url = URL('http://www.w3af.com/') headers = Headers([('content-type', 'text/html')]) response = HTTPResponse(200, body, headers, url, url, _id=1) request = FuzzableRequest(url, method='GET') self.plugin.grep(request, response) self.assertEqual( len(kb.kb.get('form_cleartext_password', 'form_cleartext_password')), 0)
def setUp(self): super(TestCORSOrigin, self).setUp() self.co = cors_origin() self.url = URL('http://moth/') self.origin = 'http://moth/' self.response = HTTPResponse(200, '', Headers(), self.url, self.url, _id=3) self.request = FuzzableRequest(self.url)
def test_https_with_sts(self): body = '' url = URL('https://www.w3af.com/') headers = Headers([('content-type', 'text/html'), ('strict-transport-security', 'max-age=31536000; includeSubDomains; preload')]) request = FuzzableRequest(url, method='GET') resp = HTTPResponse(200, body, headers, url, url, _id=1) self.plugin.grep(request, resp) self.assertEquals( len( kb.kb.get('strict_transport_security', 'strict_transport_security')), 0)
def test_provides_cors_features_false(self): url = URL('http://moth/') fr = FuzzableRequest(url) http_response = HTTPResponse(200, '', Headers(), url, url) url_opener_mock = Mock() url_opener_mock.GET = MagicMock(return_value=http_response) cors = provides_cors_features(fr, url_opener_mock) call_header = Headers({'Origin': 'www.w3af.org'}.items()) url_opener_mock.GET.assert_called_with(url, headers=call_header) self.assertFalse(cors)
def add_req(): url = request.json["url"] method = request.json["method"] post_data = request.json["post_data"] headers = request.json["headers"] cookie_string = request.json['cookie'] headers = Headers(headers.items()) freq = FuzzableRequest(URL(url), method, headers, Cookie(cookie_string), dc_from_hdrs_post(headers, post_data)) urllist.req_queue.put_nowait(freq) print("req size %d" % urllist.req_queue.qsize()) return jsonify({"status": True})
def _create_fuzzable_request(self): """ Based on the attributes, return a fuzzable request object. Important variables used here: - self.headers : Stores the headers for the request - self.rfile : A file like object that stores the post_data - self.path : Stores the URL that was requested by the browser """ # See HTTPWrapperClass if hasattr(self.server, 'chainedHandler'): base_path = "https://" + self.server.chainedHandler.path path = base_path + self.path else: path = self.path fuzzable_request = FuzzableRequest(URL(path), self.command, Headers(self.headers.dict.items())) post_data = self._get_post_data() if post_data: fuzzable_request.set_data(post_data) return fuzzable_request
def test_store_fuzzable_request_two(self): ds = DiskSet() # Add a simple fr, without post-data fr = FuzzableRequest(URL('http://example.com/?id=1')) ds.add(fr) # Add a fr with post-data form_params = FormParameters() form_params.add_input([("name", "username"), ("value", "abc")]) form_params.add_input([("name", "address"), ("value", "")]) form_params.set_action(URL('http://example.com/?id=1')) form_params.set_method('post') form = dc_from_form_params(form_params) fr = FuzzableRequest.from_form(form) ds.add(fr) # Compare stored_fr = ds[1] self.assertEqual(stored_fr, fr) self.assertIsNot(stored_fr, fr)
def test_found_at(self): dc = JSONContainer(COMPLEX_OBJECT) freq = FuzzableRequest(self.url, post_data=dc, method='PUT') m = JSONMutant(freq) m.get_dc().set_token(('object-second_key-list-0-string', )) expected = '"http://www.w3af.com/", using HTTP method PUT.' \ ' The sent JSON-data was: "...object-second_key-list-' \ '0-string=abc..."' self.assertEqual(m.found_at(), expected) headers = m.get_headers() self.assertIn('Content-Type', headers) self.assertEqual(headers['Content-Type'], 'application/json')
def create_vuln(self): v = super(XPathTemplate, self).create_vuln() original_value = self.data[self.vulnerable_parameter][0] freq = FuzzableRequest(self.url, method=self.method, dc=self.data) mutant = Mutant(freq) mutant.set_var(self.vulnerable_parameter) mutant.set_dc(self.data) mutant.set_original_value(original_value) v.set_mutant(mutant) return v