def config_hammertime(self): global_heuristics = [ DynamicTimeout(0.05, 2), RetryOnErrors(range(500, 503)), DeadHostDetection(threshold=200), ContentHashSampling(), ContentSampling(), ContentSimhashSampling() ] soft_404 = DetectSoft404() follow_redirects = FollowRedirects() reject_error_code = RejectStatusCode(range(400, 600)) heuristics = [ reject_error_code, RejectWebApplicationFirewall(), RejectCatchAllRedirect(), follow_redirects, soft_404, HashResponse(), SetExpectedMimeType(), RejectUnexpectedResponse() ] self.hammertime.heuristics.add_multiple(global_heuristics) self.hammertime.heuristics.add_multiple(heuristics) soft_404.child_heuristics.add_multiple(global_heuristics) follow_redirects.child_heuristics.add(reject_error_code) follow_redirects.child_heuristics.add_multiple(global_heuristics)
def setup_hammertime_heuristics(hammertime, *, user_agent=default_user_agent, vhost=None): # TODO Make sure rejecting 404 does not conflict with tomcat fake 404 detection. global heuristics_with_child dead_host_detection = DeadHostDetection(threshold=200) detect_soft_404 = DetectSoft404(distance_threshold=6) follow_redirects = FollowRedirects() heuristics_with_child = [ RejectCatchAllRedirect(), follow_redirects, RejectIgnoredQuery() ] hosts = (vhost, conf.target_host) if vhost is not None else conf.target_host global_heuristics = [ RejectStatusCode({404, 406, 502, 503}), DynamicTimeout(1.0, 5), RedirectLimiter(), FilterRequestFromURL(allowed_urls=hosts), IgnoreLargeBody(initial_limit=initial_limit) ] heuristics = [ StripTag('input'), StripTag('script'), detect_soft_404, RejectSoft404(), MatchString(), DetectBehaviorChange(buffer_size=100), LogBehaviorChange() ] # Dead host detection must be first to make sure there is no skipped after_headers hammertime.heuristics.add(dead_host_detection) hammertime.heuristics.add_multiple(global_heuristics) # Make sure follow redirect comes in before soft404 hammertime.heuristics.add_multiple(heuristics_with_child) hammertime.heuristics.add_multiple(heuristics) for heuristic in heuristics_with_child: heuristic.child_heuristics.add_multiple(global_heuristics) detect_soft_404.child_heuristics.add(StripTag('input')) detect_soft_404.child_heuristics.add(StripTag('script')) detect_soft_404.child_heuristics.add(dead_host_detection) detect_soft_404.child_heuristics.add(follow_redirects) add_http_header(hammertime, "User-Agent", user_agent) add_http_header(hammertime, "Host", vhost if vhost is not None else conf.target_host)
async def test_add_http_headers(self, loop): hammertime = HammerTime(loop=loop) config.heuristics_with_child = [ RejectCatchAllRedirect(), FollowRedirects() ] hammertime.heuristics.add_multiple(config.heuristics_with_child) hammertime.heuristics.add = MagicMock() for heuristic in config.heuristics_with_child: heuristic.child_heuristics.add = MagicMock() config.add_http_header(hammertime, "header", "value") set_header = hammertime.heuristics.add.call_args[0][0] self.assertEqual(set_header.name, "header") self.assertEqual(set_header.value, "value") for heuristic_with_child in config.heuristics_with_child: set_header = heuristic_with_child.child_heuristics.add.call_args[ 0][0] self.assertEqual(set_header.name, "header") self.assertEqual(set_header.value, "value")
def setup_hammertime_heuristics(hammertime, *, user_agent=default_user_agent, vhost=None, confirmation_factor=1, har_output_dir=None): global heuristics_with_child dead_host_detection = DeadHostDetection(threshold=200) detect_soft_404 = DetectSoft404(distance_threshold=6, confirmation_factor=confirmation_factor) follow_redirects = FollowRedirects() heuristics_with_child = [ RejectCatchAllRedirect(), follow_redirects, RejectIgnoredQuery() ] hosts = (vhost, conf.target_host) if vhost is not None else conf.target_host init_heuristics = [ SetHeader("User-Agent", user_agent), SetHeader("Host", vhost if vhost is not None else conf.target_host), ContentHashSampling(), ContentSampling(), ContentSimhashSampling(), dead_host_detection, RejectStatusCode({503, 508}, exception_class=StopRequest), StripTag('input'), StripTag('script') ] global_heuristics = [ RejectStatusCode({404, 406, 502}), RejectWebApplicationFirewall(), DynamicTimeout(1.0, 5), RedirectLimiter(), FilterRequestFromURL(allowed_urls=hosts), IgnoreLargeBody(initial_limit=initial_limit) ] # Dead host detection must be first to make sure there is no skipped after_headers hammertime.heuristics.add_multiple(init_heuristics) # General hammertime.heuristics.add_multiple(global_heuristics) hammertime.heuristics.add_multiple(heuristics_with_child) hammertime.heuristics.add_multiple([ detect_soft_404, MatchString(), ValidateEntry(), DetectBehaviorChange(buffer_size=100), LogBehaviorChange(), ValidateEntry(), ]) detect_soft_404.child_heuristics.add_multiple(init_heuristics) detect_soft_404.child_heuristics.add_multiple(heuristics_with_child) for heuristic in heuristics_with_child: heuristic.child_heuristics.add_multiple(init_heuristics) heuristic.child_heuristics.add_multiple(global_heuristics) if har_output_dir is not None: from tachyon.har import StoreHAR, FileWriter hammertime.heuristics.add(StoreHAR(writer=FileWriter(har_output_dir)))
def setUp(self): self.heuristic = RejectCatchAllRedirect() self.host = "http://example.com" self.fake_engine = MagicMock() self.heuristic.set_engine(self.fake_engine) self.heuristic.child_heuristics = MagicMock()
class TestRejectCatchAllRedirect(TestCase): def setUp(self): self.heuristic = RejectCatchAllRedirect() self.host = "http://example.com" self.fake_engine = MagicMock() self.heuristic.set_engine(self.fake_engine) self.heuristic.child_heuristics = MagicMock() @async_test() async def test_after_headers_request_random_filename_in_same_path_as_initial_request_if_response_is_redirect( self): self.fake_engine.perform_high_priority = make_mocked_coro( return_value=MagicMock()) entry0 = self.create_redirected_request( "/admin/restricted-resource.php", redirected_to="/admin/login.php") entry1 = self.create_redirected_request("/junkpath", redirected_to="/index.html") entry2 = self.create_redirected_request( "/path/to/admin/resource", redirected_to="/path/to/admin/login.php") with patch("hammertime.rules.redirects.uuid4", MagicMock(return_value="uuid")): await self.heuristic.after_headers(entry0) await self.heuristic.after_headers(entry1) await self.heuristic.after_headers(entry2) self.assertRequested(self.host + "/admin/uuid", self.fake_engine.perform_high_priority, order=0) self.assertRequested(self.host + "/uuid", self.fake_engine.perform_high_priority, order=1) self.assertRequested(self.host + "/path/to/admin/uuid", self.fake_engine.perform_high_priority, order=2) @async_test() async def test_after_headers_doesnt_request_random_filename_if_response_is_not_redirect( self): self.fake_engine.perform_high_priority = make_mocked_coro() entry = Entry.create("http://example.com/junkpath", response=StaticResponse(404, {}, b"Not found")) await self.heuristic.after_headers(entry) self.fake_engine.perform_high_priority.assert_not_called() @async_test() async def test_after_headers_reject_request_if_request_for_random_file_has_same_redirect_as_initial_request( self): initial_request = self.create_redirected_request( "/admin/resource.php", redirected_to="/admin/login.php") returned_entry = self.create_redirected_request( "/admin/uuid", redirected_to="/admin/login.php") self.fake_engine.perform_high_priority = make_mocked_coro( return_value=returned_entry) with self.assertRaises(RejectRequest): await self.heuristic.after_headers(initial_request) @async_test() async def test_after_headers_transform_relative_location_to_absolute_location( self): initial_request = self.create_redirected_request( "/admin/resource.php", redirected_to="/admin/login.php", relative=True) returned_entry = self.create_redirected_request( "/admin/uuid", redirected_to="/admin/login.php") self.fake_engine.perform_high_priority = make_mocked_coro( return_value=returned_entry) with self.assertRaises(RejectRequest): await self.heuristic.after_headers(initial_request) @async_test() async def test_ignore_when_path_is_present_in_redirect(self): initial_request = self.create_redirected_request( "/admin/a.php", redirected_to="/404.php?path=/admin/a.php") returned_entry = self.create_redirected_request( "/admin/uuid", redirected_to="/404.php?path=/admin/uuid") self.fake_engine.perform_high_priority = make_mocked_coro( return_value=returned_entry) with self.assertRaises(RejectRequest): with patch("hammertime.rules.redirects.uuid4", MagicMock(return_value="uuid")): await self.heuristic.after_headers(initial_request) @async_test() async def test_before_request_accept_request_if_random_file_not_redirected_to_same_path_as_initial_request( self): initial_request = self.create_redirected_request( "/admin/resource.php", redirected_to="/admin/login.php") returned_entry = self.create_redirected_request( "/admin/uuid", redirected_to="/catchAll.html") self.fake_engine.perform_high_priority = make_mocked_coro( return_value=returned_entry) await self.heuristic.after_headers(initial_request) @async_test() async def test_after_headers_add_request_response_to_knowledge_base(self): first_host = "http://example1.com" returned_entry = self.create_redirected_request( "/anything", relative=True, redirected_to="/login.php") self.fake_engine.perform_high_priority = make_mocked_coro( return_value=returned_entry) entry0 = self.create_redirected_request( "/admin/restricted-resource.php", host=first_host, redirected_to="/login.php") entry1 = self.create_redirected_request("/junkpath", host=first_host, redirected_to="/login.php") entry2 = self.create_redirected_request("/path/to/admin/resource", host=first_host, redirected_to="/login.php") second_host = "http://example2.com" entry3 = self.create_redirected_request( "/admin/restricted-resource.php", host=second_host, redirected_to="/login.php") entry4 = self.create_redirected_request("/junkpath/", host=second_host, redirected_to="/login.php") entry5 = self.create_redirected_request("/path/to/admin/resource", host=second_host, redirected_to="/login.php") kb = KnowledgeBase() self.heuristic.set_kb(kb) with patch("hammertime.rules.redirects.uuid4", MagicMock(return_value="uuid")): await self.ignore_reject_request(self.heuristic.after_headers, entry0) await self.ignore_reject_request(self.heuristic.after_headers, entry1) await self.ignore_reject_request(self.heuristic.after_headers, entry2) await self.ignore_reject_request(self.heuristic.after_headers, entry3) await self.ignore_reject_request(self.heuristic.after_headers, entry4) await self.ignore_reject_request(self.heuristic.after_headers, entry5) self.assertEqual(kb.redirects["%s/admin/" % first_host], "%s/login.php" % first_host) self.assertEqual(kb.redirects["%s/" % first_host], "%s/login.php" % first_host) self.assertEqual(kb.redirects["%s/path/to/admin/" % first_host], "%s/login.php" % first_host) self.assertEqual(kb.redirects["%s/admin/" % second_host], "%s/login.php" % second_host) self.assertEqual(kb.redirects["%s/junkpath/" % second_host], "%s/login.php" % second_host) self.assertEqual(kb.redirects["%s/path/to/admin/" % second_host], "%s/login.php" % second_host) @async_test() async def test_after_headers_doesnt_request_random_filename_if_redirect_is_already_in_knowledge_base( self): self.fake_engine.perform_high_priority = make_mocked_coro() kb = KnowledgeBase() self.heuristic.set_kb(kb) kb.redirects["%s/wp-admin/" % self.host] = "%s/somewhere" % self.host entry = self.create_redirected_request("/wp-admin/admin.php", redirected_to="/wp-login.php") await self.heuristic.after_headers(entry) self.fake_engine.perform_high_priority.assert_not_called() def create_redirected_request(self, path, *, host=None, redirected_to, relative=False): host = host or self.host if relative: headers = {"location": redirected_to} else: headers = {"location": host + redirected_to} response = StaticResponse(code=302, headers=headers, content=b"content") return Entry.create(host + path, response=response) def assertRequested(self, url, request_method, order=None): requested = False if order is not None: call_list = [request_method.call_args_list[order]] else: call_list = request_method.call_args_list for call in call_list: args, kwargs = call entry = args[0] if entry.request.url == url: requested = True break self.assertTrue(requested) async def ignore_reject_request(self, coro, arg): try: await coro(arg) except RejectRequest: pass