class TestFoundHttpsAFD(TestFoundAFD): target_url = 'https://httpretty/' MOCK_RESPONSES = [MockResponse(target_url, 'Home page'), MockResponse(BAD_SIG_URI, 'Blocked by WAF'), MockResponse(re.compile(target_url + '.*'), 'Another page')]
class TestJSONDoblequotesFiltered(PluginTest): target_url = 'http://json-filtered/?q=rfd' MOCK_RESPONSES = [ MockResponse(url='http://json-filtered/%3B/w3af.cmd%3B/w3af.cmd?q=rfd', body='message "w3afExecToken"', content_type='text/json', method='GET', status=200), MockResponse(url='http://json-filtered/%3B/w3af.cmd%3B/w3af.cmd?' 'q=w3afExecToken', body=' {"a":"w3afExecToken","b":"b"}', content_type='text/json', method='GET', status=200), MockResponse(url='http://json-filtered/%3B/w3af.cmd%3B/w3af.cmd?' 'q=w3afExecToken%22%26%7C%0A', body=' {"a":"w3afExecToken&|\n","b":"b"}', content_type='application/javascript', method='GET', status=200), ] def test_not_found_json(self): cfg = RUN_CONFIG['cfg'] self._scan(self.target_url, cfg['plugins']) vulns = self.kb.get('rfd', 'rfd') self.assertEquals(0, len(vulns))
class TestGlobalRedirectBasic(PluginTest): target_url = 'http://httpretty' MOCK_RESPONSES = [ MockResponse('http://httpretty/', '<a href="/redir?target=">redirect</a>'), MockResponse('http://httpretty/redir?target=', 'No redirect'), MockResponse('http://httpretty/redir?target=http://www.w3af.org/', status=302, headers={'Location': 'https://www.w3af.org/'}, body='') ] def test_original_response_has_no_redirect(self): cfg = SCAN_CONFIG['cfg'] cfg['target'] = self.target_url self._scan(cfg['target'], cfg['plugins']) vulns = self.kb.get('global_redirect', 'global_redirect') expected = [('redir', 'target')] self.assertAllVulnNamesEqual('Insecure redirection', vulns) self.assertExpectedVulnsFound(expected, vulns)
class TestJSONP(PluginTest): target_url = 'http://jsonp/?callback=rfd' MOCK_RESPONSES = [ MockResponse(url='http://jsonp/%3B/w3af.cmd%3B/w3af.cmd?callback' '=rfd', body=' rfd({ "Result": ' '{ "Timestamp": 1417601045 } }) ', content_type='application/javascript', method='GET', status=200), MockResponse(url='http://jsonp/%3B/w3af.cmd%3B/w3af.cmd?callback' '=w3afExecToken', body=' w3afExecToken({ "Result": ' '{ "Timestamp": 1417601045 } }) ', content_type='application/javascript', method='GET', status=200), ] def test_found_jsonp(self): cfg = RUN_CONFIG['cfg'] self._scan(self.target_url, cfg['plugins']) vulns = self.kb.get('rfd', 'rfd') self.assertEquals(1, len(vulns))
class TestGlobalRedirectBasicWithMetaRedir(PluginTest): target_url = 'http://httpretty' MOCK_RESPONSES = [ MockResponse('http://httpretty/', '<a href="/redir?target=">redirect</a>'), MockResponse('http://httpretty/redir?target=', '<meta http-equiv="refresh" content="0; url=">'), MockResponse( 'http://httpretty/redir?target=http://www.w3af.org/', body= '<meta http-equiv="refresh" content="0; url=http://www.w3af.org/">' ) ] def test_original_response_has_meta_redirect(self): cfg = SCAN_CONFIG['cfg'] cfg['target'] = self.target_url self._scan(cfg['target'], cfg['plugins']) vulns = self.kb.get('global_redirect', 'global_redirect') expected = [('redir', 'target')] self.assertAllVulnNamesEqual('Insecure redirection', vulns) self.assertExpectedVulnsFound(expected, vulns)
class TestSerializedObjectIntegration(PluginTest): target_url = 'http://mock/' html = ('<form action="/form" method="GET">' '<input type="hidden" name="viewstate" value="%s">' '</form>' % base64.b64encode(SERIALIZED_PHP_OBJECTS[0])) MOCK_RESPONSES = [MockResponse(url='http://mock/', body=html, method='GET', status=200), MockResponse(url='http://mock/form', body='Ok', method='GET', status=200)] def test_vuln_found(self): self._scan(self.target_url, RUN_CONFIGS['cfg']['plugins']) vulns = self.kb.get('serialized_object', 'serialized_object') expected_vulns = {('Serialized object', u'A total of 1 HTTP requests contained a serialized object' u' in the parameter with name "viewstate". The first ten' u' matching URLs are:\n - http://mock/form\n')} vulns_set = set() for vuln in vulns: desc = vuln.get_desc(with_id=False) vulns_set.add((vuln.get_name(), desc)) self.assertEqual(expected_vulns, vulns_set)
class TestDeadLock(PluginTest): """ This test reproduces a lock that I've found while debugging #5834, as far as I know, it has nothing to do with #5834 itself. I tried, but was unable to make this test fail when the dead-lock is found, instead when the dead-lock is found the test will "hang", CI will timeout, and in your workstation you'll have to manually kill it. """ target_url = 'http://mock/' _run_configs = { 'cfg': { 'target': target_url, 'plugins': { 'crawl': (PluginConfig('web_spider'), ) } } } TEST_ROOT = os.path.join(ROOT_PATH, 'plugins', 'tests', 'crawl', 'web_spider', '5834') INDEX_HTML = file(os.path.join(TEST_ROOT, 'index.html')).read() MOCK_RESPONSES = [ MockResponse('http://mock/', INDEX_HTML), MockResponse('http://mock/', 'Thanks.', method='POST') ] def test_no_lock(self): cfg = self._run_configs['cfg'] self._scan(cfg['target'], cfg['plugins'])
class TestAFDShortResponses(PluginTest): target_url = 'http://httpretty/' _run_configs = { 'cfg': { 'target': target_url, 'plugins': {'infrastructure': (PluginConfig('afd'),)} } } MOCK_RESPONSES = [MockResponse(target_url, 'hello world'), MockResponse(BAD_SIG_URI, MOD_SECURITY_ANSWER, status=403), MockResponse(re.compile(target_url + '\?.*'), 'hello world')] def test_afd_found(self): cfg = self._run_configs['cfg'] self._scan(self.target_url, cfg['plugins']) infos = self.kb.get('afd', 'afd') self.assertEqual(len(infos), 1, infos) info = infos[0] self.assertEqual(info.get_name(), 'Active filter detected') values = [u.url_string.split('=')[1] for u in info['filtered']] self.assertIn('../../../../etc/passwd', set(values), values)
class TestFoundHttpsAFD(TestFoundAFD): target_url = 'https://httpretty/' MOCK_RESPONSES = [ MockResponse(target_url, 'PASS'), MockResponse(BAD_SIG_URI, 'FAIL') ]
class TestFoundAFD(PluginTest): target_url = 'http://httpretty/' _run_configs = { 'cfg': { 'target': target_url, 'plugins': { 'infrastructure': (PluginConfig('afd'), ) } } } MOCK_RESPONSES = [ MockResponse('/', 'PASS'), MockResponse(BAD_SIG_URI, 'FAIL') ] def test_afd_found_http(self): cfg = self._run_configs['cfg'] self._scan(self.target_url, cfg['plugins']) infos = self.kb.get('afd', 'afd') self.assertEqual(len(infos), 1, infos) info = infos[0] self.assertEqual(info.get_name(), 'Active filter detected') values = [u.url_string.split('=')[1] for u in info['filtered']] self.assertIn('../../../../etc/passwd', set(values), values)
class TestPaymentWebHookFinderPOST(PluginTest): target_url = 'http://httpretty/' MOCK_RESPONSES = [ MockResponse('http://httpretty/', body='index home page', method='GET', status=200), MockResponse('http://httpretty/cgi-bin/paymentsuccessful.cgi', body='hidden', method='POST', status=200), MockResponse('http://httpretty/.*', body='Not found', method='POST', status=404), ] _run_config = { 'target': target_url, 'plugins': { 'crawl': (PluginConfig('payment_webhook_finder'), ) } } def test_find_using_POST(self): fill_kb_with_cgi_urls(self.target_url, self.kb.add_url) self._scan(self._run_config['target'], self._run_config['plugins']) urls = self.kb.get_all_known_urls() urls = [url.url_string for url in urls] self.assertIn(self.target_url + 'cgi-bin/paymentsuccessful.cgi', urls)
class TestUserDir(PluginTest): target_url = 'http://httpretty/' _run_configs = { 'cfg': { 'target': target_url, 'plugins': {'crawl': (PluginConfig('user_dir'),)} } } MOCK_RESPONSES = [MockResponse('/~www/', 'www user home directory.'), MockResponse('/~kmem/', 'kmem user home directory.'), MockResponse('/xfs/', 'home sweet home')] EXPECTED_RESULTS = {('Web user home directory', 'http://httpretty/~www/'), ('Web user home directory', 'http://httpretty/~kmem/'), ('Web user home directory', 'http://httpretty/xfs/'), ('Identified installed application', 'http://httpretty/xfs/'), ('Fingerprinted operating system', 'http://httpretty/~kmem/')} def test_fuzzer_user(self): # Don't enable dependencies self.w3afcore.plugins.resolve_dependencies = Mock() cfg = self._run_configs['cfg'] self._scan(cfg['target'], cfg['plugins']) users = self.kb.get('user_dir', 'users') scan_results = set([(i.get_name(), i.get_url().url_string) for i in users]) self.assertEqual(self.EXPECTED_RESULTS, scan_results)
class TestFormAuthFailedLoginMatchWithStaticLargeResponse(GenericFormAuthTest): target_url = u'http://w3af.org/' login_url = u'http://w3af.org/login' FORM = ('<form method="POST" action="/login">' ' <input name="username" type="text" />' ' <input name="password" type="password" />' ' <input name="submit" type="submit" />' '</form>') HEADER = 'abc <b>def</b> xyz'.join('\n' for _ in xrange(100)) FOOTER = 'abc <b>def</b> xyz'.join('\n' for _ in xrange(100)) def request_callback(self, request, uri, response_headers): response_headers['content-type'] = 'text/html' username = request.parsed_body.get('username', [''])[0] password = request.parsed_body.get('password', [''])[0] klass = TestFormAuthFailedLoginMatchWithStaticLargeResponse if username == 'admin' and password == 'admin': body = '%s\n%s\n%s' % (klass.HEADER, 'Success, redirecting to the home page... <a href="/home">home<a>', klass.FOOTER) else: body = klass.HEADER + '\nFail\n' + klass.FOOTER return 200, response_headers, body MOCK_RESPONSES = [ MockResponse(url=target_url, body=FORM, status=200, method='GET', content_type='text/html'), MockResponse(url=login_url, body=request_callback, method='POST', content_type='text/html', status=200), ] def test_found_credentials(self): self._scan(self.target_url, self.basic_config) # Assert the general results vulns = self.kb.get('form_auth', 'auth') self.assertEquals(len(vulns), 1) vuln = vulns[0] self.assertEquals(vuln.get_name(), 'Guessable credentials') self.assertEquals(vuln.get_url().url_string, self.login_url) self.assertEquals(vuln['user'], 'admin') self.assertEquals(vuln['pass'], 'admin')
class TestRetireJS(PluginTest): target_url = 'http://httpretty' # This is a vulnerable version of JQuery JQUERY_VULN = os.path.join(ROOT_PATH, 'plugins', 'tests', 'grep', 'retirejs', 'jquery.js') INDEX = '<html><script src="/js/jquery.js"></script></html>' MOCK_RESPONSES = [ MockResponse('http://httpretty/', body=INDEX, method='GET', status=200), MockResponse('http://httpretty/js/jquery.js', body=file(JQUERY_VULN).read(), method='GET', status=200), ] _run_configs = { 'cfg': { 'target': target_url, 'plugins': { 'grep': (PluginConfig('retirejs'), ), 'crawl': (PluginConfig('web_spider', ('only_forward', True, PluginConfig.BOOL)), ) } } } def test_is_vulnerable_detected(self): cfg = self._run_configs['cfg'] self._scan(cfg['target'], cfg['plugins']) vulns = self.kb.get('retirejs', 'js') self.assertEqual(len(vulns), 1, vulns) vuln = vulns[0] self.assertEqual(vuln.get_name(), 'Vulnerable JavaScript library in use') self.assertEqual(vuln.get_url().url_string, 'http://httpretty/js/jquery.js') expected_desc = ( u'A JavaScript library with known vulnerabilities was' u' identified at http://httpretty/js/jquery.js. The' u' library was identified as "jquery" version 1.11.0' u' and has these known vulnerabilities:\n' u'\n' u' - 3rd party CORS request may execute\n' u' - parseHTML() executes scripts in event handlers\n' u'\n' u'Consider updating to the latest stable release of the' u' affected library.') self.assertEqual(vuln.get_desc(with_id=False), expected_desc)
class TestDetailedRedirect(PluginTest): target_url = 'http://mock/auth/' auth_url = URL(target_url + 'login_form.py') check_url = URL(target_url + 'verify.py') check_string = 'Logged in' data_format = '%u=%U&%p=%P&Login=Login' MOCK_RESPONSES = [ MockResponse('http://mock/auth/login_form.py', '', status=302, headers={'Location': '/confirm/?token=123'}, method='POST'), MockResponse('http://mock/confirm/?token=123', 'Login success', status=302, headers={'Location': '/auth/home.py'}), MockResponse('http://mock/auth/home.py', 'Home page'), MockResponse('http://mock/auth/verify.py', 'Not logged in') ] _run_config = { 'target': target_url, 'plugins': { 'audit': (PluginConfig('xss'), ), 'auth': (PluginConfig( 'detailed', ('username', '*****@*****.**', PluginConfig.STR), ('password', 'passw0rd', PluginConfig.STR), ('username_field', 'username', PluginConfig.STR), ('password_field', 'password', PluginConfig.STR), ('data_format', data_format, PluginConfig.STR), ('auth_url', auth_url, PluginConfig.URL), ('method', 'POST', PluginConfig.STR), ('check_url', check_url, PluginConfig.URL), ('check_string', check_string, PluginConfig.STR), ('follow_redirects', True, PluginConfig.BOOL), ), ), } } def test_redirect_login(self): self._scan(self._run_config['target'], self._run_config['plugins']) all_paths = set() for request in httpretty.latest_requests: all_paths.add(request.path) # Followed two redirects self.assertIn('/confirm/?token=123', all_paths) self.assertIn('/auth/home.py', all_paths) # Send the POST to login self.assertIn('/auth/login_form.py', all_paths)
class TestPHPEggs(PluginTest): target_url = 'http://mock/' MOCK_RESPONSES = [ MockResponse('http://mock/?=PHPB8B5F2A0-3C92-11d3-A3A9-4C7B08C10000', '1'), MockResponse('http://mock/?=PHPE9568F34-D428-11d2-A769-00AA001ACF42', '2', content_type='image/png'), MockResponse('http://mock/?=PHPE9568F35-D428-11d2-A769-00AA001ACF42', '3', content_type='image/png'), MockResponse('http://mock/?=PHPE9568F36-D428-11d2-A769-00AA001ACF42', '4', content_type='image/png') ] _run_configs = { 'cfg': { 'target': None, 'plugins': { 'infrastructure': (PluginConfig('php_eggs'), ) } } } def test_php_eggs_fingerprinted(self): cfg = self._run_configs['cfg'] with patch('w3af.plugins.infrastructure.php_eggs.md5_hash') as md5mock: def side_effect(body): return { '1': 'a4c057b11fa0fba98c8e26cd7bb762a8', '2': 'c48b07899917dfb5d591032007041ae3', '3': 'fb3bbd9ccc4b3d9e0b3be89c5ff98a14', '4': '7675f1d01c927f9e6a4752cf182345a2' }.get(body) md5mock.side_effect = side_effect self._scan(self.target_url, cfg['plugins']) eggs = self.kb.get('php_eggs', 'eggs') self.assertEqual(len(eggs), 4, eggs) for egg in eggs: self.assertIn('PHP Egg', egg.get_name()) php_version = self.kb.get('php_eggs', 'version') self.assertEqual(len(php_version), 1, php_version) php_version = php_version[0] self.assertEqual(php_version['version'], [u'5.3.2', u'5.3.1'])
class TestRegexOutputFromUpload(TestParseOutputFromUpload): target_url = u'http://w3af.org/' FORM = """\ <form enctype="multipart/form-data" action="upload" method="POST"> <input type="hidden" name="MAX_FILE_SIZE" value="10000000" /> Choose a file to upload: <input name="uploadedfile" type="file" /><br /> <input type="submit" value="Upload File" /> </form> """ RESULT = "Thanks for uploading your file to <pre>../../hackable/uploads/mockname.png</pre>" FILE_CONTENT_RAND = 'w3af.core.data.fuzzer.utils.rand_alnum' IMAGE_CONTENT = 'PNG' + 'B' * 239 FILENAME_RAND_ALPHA = 'w3af.core.data.constants.file_templates.file_templates.rand_alpha' MOCK_RESPONSES = [ MockResponse(url=target_url, body=FORM, content_type='text/html', method='GET', status=200), MockResponse(url=target_url + 'upload', body=RESULT, content_type='text/html', method='POST', status=200), MockResponse(url=target_url + 'hackable/uploads/mockname.png', body=IMAGE_CONTENT, content_type='image/png', method='GET', status=200), ] def test_parse_response(self): with patch(self.FILENAME_RAND_ALPHA) as rand_alpha_mock: rand_alpha_mock.return_value = 'mockname' with patch(self.FILE_CONTENT_RAND) as rand_alnum_mock: rand_alnum_mock.return_value = 'B' * 239 cfg = self._run_configs['cfg'] self._scan(cfg['target'], cfg['plugins']) fu_vulns = self.kb.get('file_upload', 'file_upload') self.assertEquals(1, len(fu_vulns)) v = fu_vulns[0] self.assertEquals(v.get_name(), 'Insecure file upload') self.assertEquals(str(v.get_url().get_domain_path()), self.target_url)
class TestAutocomplete(PluginTest): target_url = 'http://w3af.org/' login_form_url = URL(target_url + 'login_form.py') login_post_handler_url = URL(target_url + 'login_post.py') check_url = URL(target_url + 'admin') check_string = 'Logged in' MOCK_RESPONSES = [ MockResponse('http://w3af.org/login_form.py', HTML_LOGIN_FORM, status=200, method='GET', headers={'Set-Cookie': '__csrf=09876xyzxyz'}), LoginMockResponse('http://w3af.org/login_post.py', '', method='POST'), SessionCheckMockResponse('http://w3af.org/admin', ''), MockResponse('http://w3af.org/unittest', 'Success', status=200, method='GET') ] _run_config = { 'target': target_url, 'plugins': { 'audit': (PluginConfig('xss'),), 'auth': (PluginConfig('autocomplete', ('username', USER, PluginConfig.STR), ('password', PASS, PluginConfig.STR), ('login_form_url', login_form_url, PluginConfig.URL), ('check_url', check_url, PluginConfig.URL), ('check_string', check_string, PluginConfig.STR)),), } } def test_find_form_submit_csrf_token(self): self._scan(self._run_config['target'], self._run_config['plugins']) all_paths = set() for request in httpretty.latest_requests: all_paths.add(request.path) self.assertIn('/login_form.py', all_paths) self.assertIn('/login_post.py', all_paths) self.assertIn('/admin', all_paths) self.assertTrue(SUCCESS)
class TestFormExclusions(PluginTest): """ This is an integration test for form exclusions :see: https://github.com/andresriancho/w3af/issues/15161 """ target_url = 'http://mock/' scan_config = { 'target': target_url, 'plugins': { 'crawl': (PluginConfig('web_spider'), ) } } MOCK_RESPONSES = [ MockResponse( 'http://mock/', '<html>' '' '<form action="/out/" method="POST">' '<input name="x" /></form>' '' '<form action="/in/" method="POST">' '<input name="x" /></form>' '' '</html>'), MockResponse('http://mock/out/', 'Thanks.', method='POST'), MockResponse('http://mock/in/', 'Thanks.', method='POST') ] def test_form_exclusions(self): user_value = '[{"action": "/out.*"}]' cf.cf.save('form_id_list', FormIDMatcherList(user_value)) cf.cf.save('form_id_action', EXCLUDE) self._scan(self.scan_config['target'], self.scan_config['plugins']) # Define the expected/desired output expected_files = ['', '/in/'] expected_urls = set( URL(self.target_url).url_join(end).url_string for end in expected_files) # pylint: disable=E1101 # Pylint fails to detect the object types that come out of the KB urls = self.kb.get_all_known_urls() found_urls = set(str(u).decode('utf-8') for u in urls) self.assertEquals(found_urls, expected_urls) # revert any changes to the default so we don't affect other tests cf.cf.save('form_id_list', FormIDMatcherList('[]')) cf.cf.save('form_id_action', EXCLUDE)
class TestFormAuthFailedLoginMatchTrivial(GenericFormAuthTest): target_url = u'http://w3af.org/' login_url = u'http://w3af.org/login' FORM = ('<form method="POST" action="/login">' ' <input name="username" type="text" />' ' <input name="password" type="password" />' ' <input name="submit" type="submit" />' '</form>') def request_callback(self, request, uri, response_headers): response_headers['content-type'] = 'text/html' username = request.parsed_body.get('username', [''])[0] password = request.parsed_body.get('password', [''])[0] if username == 'admin' and password == 'admin': body = 'Welcome Mr. Admin' else: body = 'Fail' return 200, response_headers, body MOCK_RESPONSES = [ MockResponse(url=target_url, body=FORM, status=200, method='GET', content_type='text/html'), MockResponse(url=login_url, body=request_callback, method='POST', content_type='text/html', status=200), ] def test_found_credentials(self): self._scan(self.target_url, self.basic_config) # Assert the general results vulns = self.kb.get('form_auth', 'auth') self.assertEquals(len(vulns), 1) vuln = vulns[0] self.assertEquals(vuln.get_name(), 'Guessable credentials') self.assertEquals(vuln.get_url().url_string, self.login_url) self.assertEquals(vuln['user'], 'admin') self.assertEquals(vuln['pass'], 'admin')
class TestFrontpage(PluginTest): target_url = 'http://httpretty' FRONTPAGE_BODY = ('FPVersion="1.2.3"\n' 'FPAdminScriptUrl="/admin"\n' 'FPAuthorScriptUrl="/author"\n') MOCK_RESPONSES = [ MockResponse('http://httpretty/_vti_inf.html', body=FRONTPAGE_BODY, method='GET', status=200), MockResponse('http://httpretty/author', body='', method='POST', status=200), MockResponse('http://httpretty/AAAAAA.html', body='AAAAAA.html'[::-1], method='GET', status=200), ] _run_configs = { 'cfg': { 'target': target_url, 'plugins': { 'infrastructure': (PluginConfig('frontpage_version'), ), 'audit': (PluginConfig('frontpage'), ) } } } def test_upload(self): cfg = self._run_configs['cfg'] with patch( 'w3af.plugins.audit.frontpage.rand_alpha') as rand_alpha_mock: rand_alpha_mock.side_effect = ['AAAAAA'] self._scan(cfg['target'], cfg['plugins']) vulns = self.kb.get('frontpage', 'frontpage') self.assertEqual(len(vulns), 1, vulns) vuln = vulns[0] self.assertEqual(vuln.get_url().url_string, 'http://httpretty/AAAAAA.html') self.assertEqual(vuln.get_name(), 'Insecure Frontpage extensions configuration')
class TestHTAccessFalsePositiveGeneric(PluginTest): target_url = 'http://httpretty-mock/' MOCK_RESPONSES = [ MockResponse('/', 'Bad credentials', method='GET', status=401), MockResponse('/', 'Bad credentials', method='POST', status=403) ] def test_false_positive(self): cfg = RUN_CONFIG['cfg'] self._scan(self.target_url, cfg['plugins']) vulns = self.kb.get('htaccess_methods', 'auth') self.assertEquals(0, len(vulns))
class TestRelativePathsIn404(PluginTest): """ This test reproduces the issue #5834 which generates an endless crawl loop :see: https://github.com/andresriancho/w3af/issues/5834 """ target_url = 'http://mock/' _run_configs = { 'cfg': { 'target': target_url, 'plugins': { 'crawl': (PluginConfig('web_spider'), ) } } } TEST_ROOT = os.path.join(ROOT_PATH, 'plugins', 'tests', 'crawl', 'web_spider', '5834') GALERIA_HTML = file(os.path.join(TEST_ROOT, 'galeria-root.html')).read() INDEX_HTML = file(os.path.join(TEST_ROOT, 'index.html')).read() MOCK_RESPONSES = [ MockResponse(re.compile('http://mock/galeria/.*'), GALERIA_HTML), MockResponse('/', 'Thanks.', method='POST'), MockResponse('/', INDEX_HTML) ] def test_crawl_404_relative(self): cfg = self._run_configs['cfg'] self._scan(cfg['target'], cfg['plugins']) # Define the expected/desired output expected_files = [ '', '/galeria/', '/galeria/assets/ico/', '/i18n/setlang/', '/galeria/assets/ico/tel:982560987', '/reserva/resumen/' ] expected_urls = set( URL(self.target_url).url_join(end).url_string for end in expected_files) # pylint: disable=E1101 # Pylint fails to detect the object types that come out of the KB urls = self.kb.get_all_known_urls() found_urls = set(str(u).decode('utf-8') for u in urls) self.assertEquals(found_urls, expected_urls)
class TestEventValidationGrouping(PluginTest): target_url = 'http://mock/' html = ('<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"' ' value="/wEPDwUKLTMyNjg0MDc1MWQYAQUeX19Db250cm9sc1JlcXVpcmVQb3' 'N0QmFja0tleV9fFgEFIGJwJF8kY3RsMDAkXyRicyRfJHdzJF8kU2VhcmNoQm94' 'bxUzDQVBRPB2cN8nnSmNhVZ6WX0=" />') MOCK_RESPONSES = [ MockResponse(url='http://mock/', body='<a href="/1">1</a>' '<a href="/2">2</a>', method='GET', status=200), MockResponse(url='http://mock/1', body=html, method='GET', status=200), MockResponse(url='http://mock/2', body=html, method='GET', status=200) ] def test_grouped_vulnerabilities(self): self._scan(self.target_url, RUN_CONFIGS['cfg']['plugins']) vulns = self.kb.get('dot_net_event_validation', 'dot_net_event_validation') expected_vulns = { ('.NET Event Validation is disabled', u'The application contains 2 unique URLs which have' u' .NET Event Validation disabled. This programming' u' / configuration error should be manually' u' verified. The first two vulnerable URLs are:\n' u' - http://mock/2\n - http://mock/1\n'), ('.NET ViewState encryption is disabled', u'The application contains 2 unique URLs with .NET' u' ViewState encryption disabled. This programming' u' / configuration error can be exploited to decode' u' and inspect the ViewState contents. The first two' u' vulnerable URLs are:\n - http://mock/2\n' u' - http://mock/1\n') } vulns_set = set() for vuln in vulns: desc = vuln.get_desc(with_id=False) vulns_set.add((vuln.get_name(), desc)) self.assertEqual(expected_vulns, vulns_set)
class TestPHPInfo516(PluginTest): target_url = 'http://httpretty/' PHPINFO = os.path.join(ROOT_PATH, 'plugins', 'tests', 'crawl', 'phpinfo', 'phpinfo-5.1.6.html') MOCK_RESPONSES = [ MockResponse('http://httpretty/', body='index home page', method='GET', status=200), MockResponse('http://httpretty/phpversion.php', body=file(PHPINFO).read(), method='GET', status=200), ] _run_config = { 'target': target_url, 'plugins': { 'crawl': (PluginConfig('phpinfo'), ) } } def test_phpinfo(self): self._scan(self._run_config['target'], self._run_config['plugins']) urls = self.kb.get_all_known_urls() urls = [url.url_string for url in urls] self.assertIn(self.target_url + 'phpversion.php', urls) infos = self.kb.get('phpinfo', 'phpinfo') self.assertTrue(len(infos) > 5, infos) info_urls = [i.get_url().url_string for i in infos] self.assertIn(self.target_url + 'phpversion.php', info_urls) found_infos = set([i.get_name() for i in infos]) expected_infos = { 'PHP register_globals: On', 'PHP expose_php: On', 'PHP session.hash_function:md5', 'phpinfo() file found' } for expected_info in expected_infos: self.assertIn(expected_info, found_infos)
class TestOpenAPIFindsSpecInOtherDirectory2(PluginTest): target_url = 'http://w3af.org/a/b/c/' _run_configs = { 'cfg': { 'target': target_url, 'plugins': { 'crawl': (PluginConfig('open_api'), ) } } } MOCK_RESPONSES = [ MockResponse('http://w3af.org/a/openapi.json', NestedModel().get_specification()) ] def test_auth_warning_raised(self): cfg = self._run_configs['cfg'] self._scan(cfg['target'], cfg['plugins']) # # Since we configured authentication we should only get one of the Info # infos = self.kb.get('open_api', 'open_api') self.assertEqual(len(infos), 2, infos) info_i = infos[0] self.assertEqual(info_i.get_name(), 'Open API specification found')
class TestUnSSL(PluginTest): target_url = 'http://httpretty/' # This mocked response will be returned for both http and https MOCK_RESPONSES = [MockResponse('/', 'foo bar spam', )] _run_configs = { 'cfg': { 'target': target_url, 'plugins': { 'audit': (PluginConfig('un_ssl'),), } } } def setUp(self): super(TestUnSSL, self).setUp() self._register_httpretty_uri('https', 'httpretty', 443) def test_found_unssl(self): cfg = self._run_configs['cfg'] self._scan(cfg['target'], cfg['plugins']) vulns = self.kb.get('un_ssl', 'un_ssl') self.assertEquals(1, len(vulns)) # Now some tests around specific details of the found vuln vuln = vulns[0] self.assertEquals(vuln.get_name(), 'Secure content over insecure channel') self.assertEquals(vuln.get_url().url_string, 'http://httpretty/')
class TestOpenAPIRaisesWarningIfNoAuth(PluginTest): target_url = 'http://w3af.org/' _run_configs = { 'cfg': { 'target': target_url, 'plugins': { 'crawl': (PluginConfig('open_api'), ) } } } MOCK_RESPONSES = [ MockResponse('http://w3af.org/openapi.json', NestedModel().get_specification(), content_type='application/json') ] def test_auth_warning_raised(self): cfg = self._run_configs['cfg'] self._scan(cfg['target'], cfg['plugins']) # # Since we configured authentication we should only get one of the Info # infos = self.kb.get('open_api', 'open_api') self.assertEqual(len(infos), 2, infos) info_i = infos[0] self.assertEqual(info_i.get_name(), 'Open API specification found') info_i = infos[1] self.assertEqual(info_i.get_name(), 'Open API missing credentials')
class TestServerStatus(PluginTest): target_url = 'http://httpretty-mock/' MOCK_RESPONSES = [ MockResponse('/server-status', '<dl><dt>Server Version: Apache/2.2.9 (Unix)</dt>'), ] _run_configs = { 'cfg': { 'target': target_url, 'plugins': { 'infrastructure': (PluginConfig('server_status'), ) } } } def test_find_server(self): cfg = self._run_configs['cfg'] self._scan(cfg['target'], cfg['plugins']) server = self.kb.get('server_status', 'server') self.assertEqual(len(server), 1, server) self.assertTrue( 'remote server version: "Apache/2.' in server[0].get_desc(), server[0].get_desc())
class TestUserDir(PluginTest): target_url = 'http://moth/' _run_configs = { 'cfg': { 'target': target_url, 'plugins': { 'crawl': (PluginConfig('user_dir'), ) } } } MOCK_RESPONSES = [MockResponse('/~www/', 'www user home directory.')] def test_fuzzer_user(self): # Don't enable dependencies self.w3afcore.plugins.resolve_dependencies = Mock() cfg = self._run_configs['cfg'] self._scan(cfg['target'], cfg['plugins']) users = self.kb.get('user_dir', 'users') self.assertEqual(len(users), 1, users) user = users[0] self.assertTrue(user.get_name().startswith('Web user home directory')) self.assertEquals(user.get_url().url_string, 'http://moth/~www/')