Exemple #1
0
 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)
Exemple #2
0
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)
Exemple #3
0
    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")
Exemple #4
0
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